主机感知型HTTP代理隧道类型的提案
本提案旨在通过引入一种新的HTTP代理隧道类型,解决传统HTTP-over-I2P使用中的“共享身份问题”。该隧道类型具备补充行为,旨在防止或限制潜在恶意隐藏服务运营者对目标用户代理(浏览器)及I2P客户端应用本身进行追踪的有效性。
什么是“共享身份”问题?
“共享身份”问题发生在加密寻址覆盖网络上的用户代理之间共享加密身份时。例如,当Firefox和GNU Wget均被配置为使用同一个HTTP代理时,就会出现这种情况。
在此场景下,服务器可以收集并存储用于响应活动的加密地址(Destination),并将其视为一个“指纹”——由于其源于加密机制,该指纹始终100%唯一。这意味着由共享身份问题导致的可关联性是完全的。
但这真的是一个问题吗? ^^^^^^^^^^^^^^^^^^^^
当使用相同协议的用户代理希望实现不可关联性时,共享身份问题就成为一个问题。该问题最早在Reddit的一个帖子中提及 ,删除的评论可通过pullpush.io 获取。当时我是最活跃的回应者之一,当时我认为这个问题影响较小。在过去8年中,情况以及我对该问题的看法已经改变,我现在认为,随着越来越多的网站具备“画像”特定用户的能力,恶意目标关联所带来的威胁显著增加。
这种攻击的门槛非常低,仅要求隐藏服务运营者运营多个服务即可。对于同时访问(访问多个站点)的攻击,这是唯一的要求。对于非同时期的关联,其中一个服务必须是属于单个用户且该用户被针对性追踪的“账户”类服务。
目前,任何托管用户账户的服务运营商都可以利用“共享身份”问题,将其与他们控制的所有站点上的活动相关联。只要运营多个服务并对创建用户画像有兴趣,Mastodon、Gitlab甚至简单的论坛都可能伪装成攻击者。这种监视可能是出于跟踪、经济利益或情报相关目的。目前已有数十个主要运营商能够实施此类攻击并从中获得有意义的数据。我们目前大多信任他们不会这样做,但完全不在乎我们观点的参与者很容易出现。
这与明网(clear web)上一种相当基础的画像构建方式直接相关,即组织可以将其网站上的交互与其控制网络中的交互进行关联。在I2P中,由于加密目标地址是唯一的,这种技术有时甚至更可靠,尽管缺乏地理位置定位的额外能力。
共享身份对仅使用I2P来混淆地理位置的用户无用。它也不能用于破坏I2P的路由机制。它只是上下文身份管理方面的问题。
- 无法利用共享身份问题对I2P用户进行地理定位。
- 如果I2P会话不是同时发生的,则无法利用共享身份问题进行会话关联。
然而,在某些可能非常常见的情况下,可以利用它来降低I2P用户的匿名性。它们之所以常见,部分原因是我们鼓励使用Firefox——一种支持“标签页”操作的网页浏览器。
- 对于任何支持请求第三方资源的网页浏览器,始终可以从共享身份问题中生成指纹。
- 禁用JavaScript对抵御共享身份问题毫无作用。
- 如果能通过“传统”浏览器指纹技术建立非同时期会话之间的关联,则共享身份可被传递应用,从而可能启用非同时期关联策略。
- 如果能在明网活动与I2P身份之间建立关联,例如目标用户在同一站点的I2P和明网版本中均登录,则共享身份可被传递应用,从而可能导致完全去匿名化。
你如何看待共享身份问题在I2P HTTP代理中的严重性,取决于你(或更准确地说,一个可能缺乏知情预期的“用户”)认为该应用的“上下文身份”位于何处。有几种可能性:
- HTTP既是应用也是上下文身份 —— 这是当前的工作方式。所有HTTP应用共享一个身份。
- 进程是应用也是上下文身份 —— 这是当应用使用SAMv3或I2CP等API时的工作方式,其中应用创建并控制其身份的生命周期。
- HTTP是应用,但主机是上下文身份 —— 这是本提案的目标,将每个主机视为潜在的“Web应用”,并据此对待威胁面。
是否可解决? ^^^^^^^^^^^^^^^
可能无法制造出一种代理,能够智能响应其操作可能削弱应用匿名性的每一种情况。然而,可以构建一种代理,能够智能响应以可预测方式运行的特定应用。例如,在现代网页浏览器中,用户通常会打开多个标签页,与多个网站交互,这些网站通过主机名区分。
这使我们能够通过让代理的行为匹配用户代理的行为来改进HTTP代理对此类HTTP用户代理的表现:即在使用HTTP代理时,为每个主机分配其自己的Destination。此更改使得无法利用共享身份问题推导出可用于关联客户端在两个主机上活动的指纹,因为这两个主机将不再共享返回身份。
描述: ^^^^^^^^^^^^
将创建一个新的HTTP代理并添加到隐藏服务管理器(I2PTunnel)中。新的HTTP代理将作为I2PSocketManagers的“多路复用器”运行。多路复用器本身没有Destination。构成多路复用的每个独立I2PSocketManager都有其自己的本地Destination及其自己的隧道池。I2PSocketManagers由多路复用器按需创建,“需求”即首次访问新主机。可以通过预先创建一个或多个I2PSocketManagers并将其存储在多路复用器外部,在插入多路复用器之前优化其创建过程。这可能会提高性能。
另外设置一个具有自己Destination的I2PSocketManager,作为“出站代理”(Outproxy),用于任何没有I2P Destination的站点,例如任何明网站点。这实际上使所有出站代理使用成为一个单一的上下文身份,但需注意:为隧道配置多个出站代理将导致正常的“粘性”出站代理轮换,其中每个出站代理仅接收单个站点的请求。这几乎等同于在明网上按Destination隔离HTTP-over-I2P代理的行为。
资源考虑: ’’’’’’’’’’’’’’’’’’’’’’''
与现有HTTP代理相比,新的HTTP代理需要额外资源。它将:
- 可能构建更多的隧道和I2PSocketManagers
- 更频繁地构建隧道
这些都需要:
- 本地计算资源
- 来自对等节点的网络资源
设置: ’’’’’’’''
为了最小化资源使用增加的影响,应将代理配置为尽可能少地使用资源。属于多路复用器(非父代理)的代理应配置为:
- 多路复用的I2PSocketManagers在其隧道池中构建1条入站隧道、1条出站隧道
- 多路复用的I2PSocketManagers默认采用3跳
- 在10分钟不活动后关闭套接字
- 由多路复用器启动的I2PSocketManagers与多路复用器具有相同的生命周期。多路复用的隧道直到父级多路复用器关闭才被“销毁”。
图示: ^^^^^^^^^
下图表示当前HTTP代理的运行方式,对应“是否是问题”部分下的“可能性1”。如图所示,HTTP代理仅使用一个Destination直接与I2P站点交互。在此场景中,HTTP既是应用也是上下文身份。
**当前情况:HTTP是应用,HTTP是上下文身份**
__-> Outproxy <-> i2pgit.org
/
Browser <-> HTTP Proxy(一个Destination)<->I2PSocketManager <---> idk.i2p
\__-> translate.idk.i2p
\__-> git.idk.i2p
下图表示主机感知型HTTP代理的运行方式,对应“是否是问题”部分下的“可能性3”。在此场景中,HTTP是应用,但主机定义了上下文身份,其中每个I2P站点与具有每主机唯一Destination的不同HTTP代理交互。这防止了多个站点的运营者能够区分同一人是否正在访问他们运营的多个站点。
**变更后:HTTP是应用,主机是上下文身份**
__-> I2PSocketManager(Destination A - 仅用于Outproxy) <--> i2pgit.org
/
Browser <-> HTTP Proxy Multiplexer(无Destination) <---> I2PSocketManager(Destination B) <--> idk.i2p
\__-> I2PSocketManager(Destination C) <--> translate.idk.i2p
\__-> I2PSocketManager(Destination C) <--> git.idk.i2p
状态: ^^^^^^^
符合本提案较早版本的主机感知代理的可用Java实现在idk的分支中提供:i2p.i2p.2.6.0-browser-proxy-post-keepalive(链接见引用)。该实现正在经历重大修订,以将更改分解为更小的部分。
已有使用SAMv3库编写的Go语言实现,功能各异,可用于嵌入其他Go应用或go-i2p,但不适用于Java I2P。此外,它们缺乏与加密leaseSet交互操作的良好支持。
附录:i2psocks
通过结合I2PTunnel已广泛可用且在隐私社区中经过测试的现有工具,可以在不实现新隧道类型或更改现有I2P代码的情况下,实现隔离其他类型客户端的简单应用导向方法。然而,这种方法做了一个困难的假设,而该假设对HTTP不成立,对许多其他潜在I2P客户端也不成立。
大致而言,以下脚本将生成一个应用感知的SOCKS5代理并对底层命令进行socksify:
#! /bin/sh
command_to_proxy="$@"
java -jar ~/i2p/lib/i2ptunnel.jar -wait -e 'sockstunnel 7695'
torsocks --port 7695 $command_to_proxy
附录:攻击的示例实现
HTTP用户代理上共享身份攻击的示例实现
已存在多年。另一个示例可在idk的prop166仓库
的simple-colluder子目录中找到。这些示例故意设计用于证明攻击有效,若要将其转变为真实攻击,需要修改(尽管是轻微的)。