ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

aiohttp 跨源重定向时 per-request cookies 的丢弃机制与安全修复解析

aiohttp 跨源重定向时 per-request cookies 的丢弃机制与安全修复解析 aiohttp 跨源重定向时 per-request cookies 的丢弃机制与安全修复解析【免费下载链接】aiohttpAsynchronous HTTP client/server framework for asyncio and Python项目地址: https://gitcode.com/gh_mirrors/ai/aiohttp本篇文章聚焦 aiohttp 中一个具体且重要的安全修复跨源cross-origin重定向时每次请求单独传入的 per-request cookies 此前未被丢弃现已修复为随重定向被安全移除。该修复记录在变更片段 CHANGES/12540.bugfix.rst 中并已合入项目变更日志 CHANGES.rst。读完本文你将理解 aiohttp 中 session 级 cookies 与 per-request cookies 的底层差异、重定向循环的完整处理流程以及客户端在跨源跳转时如何通过源码级机制防止凭据泄漏并能据此编写更安全的 HTTP 客户端代码。一、变更记录解读修复了什么原始变更记录只有一句话Fixed per-requestcookiesnot being dropped on cross-origin redirects -- by :user:Dreamsorcerer.拆解这条记录的三个关键词per-request cookies指调用ClientSession.get()、ClientSession.post()等方法时通过cookies参数临时附加到单次请求上的 Cookie区别于创建ClientSession(cookies...)时注入的 session 级 Cookiecross-origin redirects服务器返回 3xx 重定向且Location指向的地址与当前请求地址源scheme host port不同的跳转not being dropped修复前这类 per-request cookies 会在跨源重定向后继续携带到目标站点属于凭据泄漏类安全隐患修复后它们与敏感头一起被丢弃。这条修复属于bugfix类别文件后缀.bugfix.rst表明它不是新增功能而是对既有行为的修正——即把跨源跳转后仍携带调用方显式传入的 Cookie这一不符合安全预期的行为纠正为丢弃。二、背景per-request cookies 与 session cookies 的两种来源要理解这个修复先要弄清 aiohttp 中 Cookie 的两条注入路径。二者都在 aiohttp/client.py 中实现。2.1 session 级 cookies绑定整个会话创建会话时传入import aiohttp async def main(): async with aiohttp.ClientSession(cookies{session_id: abc123}) as session: ...底层实现位于 aiohttp/client.pyif cookie_jar is None: cookie_jar CookieJar() self._cookie_jar cookie_jar if cookies: self._cookie_jar.update_cookies(cookies)这些 Cookie 进入会话的CookieJar之后由 jar 依据 domain、path、expires 等属性统一管理并会在收到Set-Cookie响应头时自动更新aiohttp/client.py。2.2 per-request cookies仅作用于单次请求单次调用时传入async with session.get( https://example.com/api, cookies{trace: req-001}, ) as resp: ...cookies是请求级可选参数定义在_RequestOptions中aiohttp/client.py并被ClientSession._request()接收aiohttp/client.py。单次请求发送时aiohttp/client.py 先过滤 jar 中的 Cookie再叠加 per-request cookiesall_cookies self._cookie_jar.filter_cookies(url) if cookies is not None: tmp_cookie_jar CookieJar( unsafeself._cookie_jar.unsafe, quote_cookieself._cookie_jar.quote_cookie, ) tmp_cookie_jar.update_cookies(cookies) req_cookies tmp_cookie_jar.filter_cookies(url) if req_cookies: all_cookies.load(req_cookies)这里有两个值得注意的实现细节filter_cookies()是CookieJar按请求 URL 过滤 Cookie 的核心方法定义于 aiohttp/cookiejar.py它决定哪些 Cookie 与当前 URL 匹配per-request cookies 通过临时CookieJar中转后再过滤因此它们同样会受域名等属性约束——但它只存在于本次_request()调用周期内不会写入会话 jar也不影响后续请求。这正是 bug 的温床per-request cookies 存放在_request()的局部变量cookies中由重定向循环接力使用修复前跨源跳转时该变量没有被清空导致 Cookie 被带到新源。三、核心机制重定向循环如何处理 3xx 响应aiohttp 默认自动跟随重定向allow_redirectsTrue且只信任标准重定向状态码。处理逻辑位于 aiohttp/client.py。3.1 识别重定向状态码if resp.status in (301, 302, 303, 307, 308) and allow_redirects:支持 301/302/303/307/308 五种状态码若allow_redirectsFalse则直接把 3xx 响应返回给调用方而不跳转。3.2 重定向次数上限redirects 1 history.append(resp) if max_redirects and redirects max_redirects: ... raise TooManyRedirects(history[0].request_info, tuple(history))默认max_redirects10aiohttp/client.py超限会抛出TooManyRedirects。历史响应保存在resp._history中可通过ClientResponse.history属性读取aiohttp/client_reqrep.py。3.3 请求方法转换规则不同状态码对请求方法的重写策略不同aiohttp/client.pyif (resp.status 303 and resp.method ! hdrs.METH_HEAD) or ( resp.status in (301, 302) and resp.method hdrs.METH_POST ): method hdrs.METH_GET data None ... else: # For 307/308, always preserve the request body ...303 See Other除 HEAD 外一律改为 GET 并丢弃 body301/302 POST按旧版浏览器惯例改为 GETRFC 9110 对此有争议aiohttp 沿用了 requests 的决策307/308保持原方法并保留请求体若 body 已被消费则抛出ClientPayloadError。3.4 提取并校验Location重定向目标取自Location头兼容URI头随后进行严格校验aiohttp/client.pyr_url resp.headers.get(hdrs.LOCATION) or resp.headers.get(hdrs.URI) ... parsed_redirect_url URL(r_url, encodednot self._requote_redirect_url) ... scheme parsed_redirect_url.scheme if scheme not in HTTP_AND_EMPTY_SCHEMA_SET: raise NonHttpUrlRedirectClientError(r_url) elif not scheme: parsed_redirect_url url.join(parsed_redirect_url) ... redirect_origin parsed_redirect_url.origin()非 HTTP(S) 协议直接拒绝NonHttpUrlRedirectClientError相对路径会基于当前 URL 解析解析失败抛InvalidUrlRedirectClientError。四、修复核心跨源跳转时的凭据清理修复的关键逻辑在 aiohttp/client.pyif url.origin() ! redirect_origin: cookies None headers.popall(hdrs.AUTHORIZATION, None) headers.popall(hdrs.COOKIE, None) headers.popall(hdrs.PROXY_AUTHORIZATION, None)逐行解读url.origin() ! redirect_origin用yarl.URL.origin()比较当前请求与重定向目标的源。只要 scheme、host、port 任一不同即判定为跨源cookies None这是本次修复的核心动作——清空 per-request cookies 变量。由于重定向循环后续迭代会检查if cookies is not None:aiohttp/client.py置None后 per-request cookies 不再被重新合并进请求headers.popall(...)同时移除显式携带的Authorization、COOKIE、Proxy-Authorization三个敏感头防止认证信息被转发到不可信源。修复前cookies变量在跨源分支中没有被清空per-request cookies 会在下一次循环迭代中随all_cookies.load(req_cookies)被发送到跨源目标修复后跨源跳转时这部分 Cookie 与敏感头一并丢弃。4.1 同源跳转Cookie 正常延续需要强调的是清理仅发生在跨源分支。同源same-origin重定向时cookies保持原值per-request cookies 继续生效这符合同源可信、跨源隔离的安全模型。4.2 与 session 级 Cookie 的区别jar 中的 session cookies 在每次迭代开始时都会基于新的 URL重新过滤filter_cookies(url)由 Cookie 的 domain/path 属性自行决定是否匹配新源而 per-request cookies 是被局部变量人工携带的因此需要显式置空。两种机制共同构成了跨源重定向下的完整 Cookie 防线。五、同类防护Authorization 头与 Digest 认证跨源隔离并不仅针对 Cookie。同段代码中Authorization、Proxy-Authorization头同样被移除相关行为有测试佐证tests/test_client_functional.py 断言session 级Authorization头在跨源重定向时被丢弃tests/test_client_middleware_digest_auth.py 验证跨源重定向目标不会收到 Digest 认证响应注释明确说明aiohttp 会在跨源重定向时剥离 Authorization 头该模块源码中亦有注释标注此行为aiohttp/client_middleware_digest_auth.py。这构成一条一致的策略凡是显式携带的凭据Cookie、Authorization、Proxy-Authorization跨源跳转时一律不转发。六、测试验证修复行为的可观测证据仓库测试直接覆盖了相关行为6.1 重定向链上的 Cookie 更新tests/test_client_functional.py 的test_cookies_redirect构造了两级 301 跳转链每级响应都通过set_cookie()设置同名 Cookie最终处理器断言只收到最后一级的值2——验证重定向链中 jar 内 Cookie 的覆盖语义。6.2 空 jar 下的 per-request cookiestests/test_client_functional.py 的test_cookies_on_empty_session_jar在ClientSession(cookiesNone)空 jar下通过session.get(url, cookies{custom-cookie: abc})发起请求断言服务端能收到该 per-request cookie——证明 per-request cookies 独立于会话 jar 独立生效这正是重定向循环中必须单独管理它的原因。七、实战建议基于以上机制编写涉及重定向的 aiohttp 客户端时应注意不要用 per-request cookies 传递敏感凭据。它只应携带本次请求的临时上下文如追踪 ID认证信息请使用 jar 内受控的 Cookie或显式Authorization头——跨源跳转时两者都会被隔离而同源跳转时可正常延续若需跨源转发凭据请自行控制重定向设置allow_redirectsFalse在收到 3xx 后由业务代码校验Location再显式决定是否携带凭据发起新请求此时可通过resp.history检查是否发生过跳转涉及上传时注意 body 可重放性307/308 会保留请求体若 body 已被消费将抛ClientPayloadErroraiohttp/client.py建议使用 bytes 或可 seek 的文件对象限制跳转次数保持默认max_redirects10或按需调小配合TooManyRedirects异常处理防止被恶意重定向链拖垮升级到包含该修复的版本此变更已合入项目主分支并记录于 CHANGES.rst生产环境请使用包含该修复的版本避免跨源重定向场景下的 Cookie 泄漏。八、小结本修复从源码层面补上了跨源重定向的一条凭据泄漏路径重定向循环在检测到源变化时aiohttp/client.py将 per-request cookies 置空并同步剥离Authorization、COOKIE、Proxy-Authorization头。理解这条逻辑既有助于你在升级后正确评估行为变化如某些依赖跨源携带 Cookie 的场景可能失效也能帮助你更稳妥地使用 aiohttp 的重定向与 Cookie 能力。【免费下载链接】aiohttpAsynchronous HTTP client/server framework for asyncio and Python项目地址: https://gitcode.com/gh_mirrors/ai/aiohttp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表