客户端与授权服务器经浏览器传递 S256 挑战与回调授权码。另一条令牌端点路径用码加校验串换取令牌。客户端既可以是公共客户端,也可以是机密客户端。

OAuth 授权码用码换令牌

授权码流程把访问令牌的交付移到令牌端点,而不是在面向浏览器的授权重定向里直接返回访问令牌。这能减少令牌暴露在地址中的机会,却不代表浏览器绝不可能持有令牌。究竟由谁兑换授权码,以及结果保存在哪里,取决于客户端架构。审阅时应区分协议步骤、客户端类型和实际存储位置,不能把一种部署方式当成整个流程的定义。 授权跳转传递授权码,令牌端点交付令牌。PKCE、客户端与回调绑定、过期和单次使用各守一条边界。存储方式取决于客户端架构;离线模型不能证明完整登录流程。 公共客户端也能使用授权码流程 机密 Web 后端可以向令牌端点进行客户端认证,并把令牌保存在服务端。浏览器随后使用应用自己的会话,但会话安全仍需单独处理。原生应用和浏览器应用等公共客户端无法可靠地保守分发出去的客户端秘密,然而它们仍然可以使用授权码加 PKCE。因此,授权码流程并不是只有后端才能采用的方案。 若浏览器应用自行兑换,JavaScript 就会收到令牌响应。流程避免了在授权跳转地址里交付访问令牌,却不会消除脚本被入侵或令牌存储泄露的问题。后端架构改变了边界,并没有让所有浏览器侧风险消失。OAuth 解决委托访问;需要确认用户身份的应用,应采用 OpenID Connect 等适合的身份协议,并验证其中独立的身份凭据,不能把任意访问令牌直接当作登录证明。 把兑换绑定到原始请求 PKCE 为每次事务生成高熵校验串。授权请求携带它的 S256 挑战,令牌请求再提供原校验串,授权服务器检查二者是否对应。校验串不是固定的客户端口令,必须与发起它的事务关联。这样,只有截获授权码、却没有校验串的一方,不应能够完成兑换。日志和错误报告也不应同时泄露这两部分。 RFC 9700 要求公共客户端使用 PKCE,并建议机密客户端也使用。应选择 S256,避免降级成 plain。机密客户端仍需执行已配置的客户端认证,PKCE 不能代替这一职责。客户端认证也不一定意味着发送共享秘密;部署可以采用非对称认证机制。判断安全性时,要核对服务端实际执行的验证,而不是只看请求里出现了几个参数。 每枚授权码都应绑定客户端与回调地址,设置短有效期并限制只能使用一次。登记的回调地址通常必须精确匹配;原生应用的回环地址端口存在标准规定的例外。授权请求携带 redirect_uri 时,令牌请求中的值必须一致。在授权阶段就要拒绝未登记的回调,而不是等到授权码已泄露之后才靠兑换失败补救。 坏掉的样子 一次性并不能阻止攻击者抢先兑换一枚没有充分保护的授权码。过期只是缩短机会,不会让截获变得无害。回调中的授权码仍可能出现在访问日志、浏览器历史、来源信息和崩溃报告里。避免记录这些值,使用合适的 HTTPS 回调;开发原生客户端时,则按照原生回环重定向的专门规则处理,不要把例外扩展到任意明文回调。 ...

十月 20, 2027 · 2 分钟 · XB Field Notes
两条箭头把相同 Authorization 值从客户端送到服务器,两次均显示 200。Base64 可逆,是否继续接受取决于服务端策略。

Basic Auth 携带的是可重复使用的凭据

HTTP Basic 认证把编码后的用户名与密码放进 Authorization 请求头。由于编码可逆,取得该请求头的值,往往就相当于取得凭据本身。Basic 确实定义了认证挑战与响应流程,但不会用密码学方式把响应绑定到服务器刚生成的一次性随机数。只要服务器继续接受这个账号和凭据,一份曾被接受的值就可能被再次使用。不能把“存在挑战流程”误解成“重放一定失败”。 看清真正发生的交换 服务器可以返回四零一,并通过 WWW-Authenticate: Basic 挑战声明 realm,客户端再带 Authorization 重试。客户端也可以在合适的保护空间内提前发送凭据。因此,带认证的请求会携带凭据,不意味着每个最初请求一开始就必须有它。realm 用于标识保护空间,并不会自动授予同名路径上的所有权限,更不能替代具体的资源访问控制。 编码输入由用户标识、一个冒号和密码拼接。第一个冒号是分隔符,所以用户名不能包含冒号,密码则可以包含后续冒号。用户名和密码都不能包含控制字符;允许密码里出现冒号,并不意味着它可以包含任意字符。Base64 既不是加密,也不是密码哈希。涉及非 ASCII 凭据时还要考虑字符编码:RFC 7617 在兼容 ASCII 的条件下没有统一默认编码,并允许服务器挑战指明 UTF-8。仅验证一个 ASCII 示例,不等于已经覆盖所有国际化账号的编码与比较路径。 Basic 本身不定义会话令牌或通用退出登录协议。应用仍可以设置账号到期、禁用账号、改变授权或拒绝旧密码。因此,轮换密码并不是阻止泄露请求头继续生效的唯一办法。凭据的有效期与撤销方式,应依据服务器真实的账号策略定义,不能期待网络传输格式自动提供完整账号生命周期管理。客户端停止发送,与服务器已经拒绝旧值,也是不同事实。 坏掉的样子 在没有保护的网络链路上,能够读取请求头的人可以解码并再次使用。正确校验证书的 HTTPS 保护的是到 TLS 终点的传输,不会让终点本身、合法终止 TLS 的代理或疏忽的应用日志看不到凭据。还需要保护后续链路,并避免在日常追踪、报告和共享诊断文件中记录 Authorization 的真实值。传输加密不能替代服务端对凭据的妥善处理。 权限过大的凭据会扩大泄露后果。账号应匹配具体任务及其生命周期,身份验证之后仍要执行授权。凭据有效,不等于每个操作都允许;realm 标签也不是租户隔离机制。客户端需要限制凭据发送位置,尤其关注重定向和脚本复用全局请求头的情况。把同一个认证头传给无关目的地址,超出了原来的保护空间,也破坏了凭据使用边界。 重复的四零一可能来自凭据缺失、错误、到期或服务端不支持相应方式。在下面例子中,四零三表示已经识别的演示账号缺少目标权限,但不能据此认为所有服务返回四零三都证明认证已成功。应结合服务契约及响应中的认证挑战检查,不能仅凭一个数字推断完整的身份状态。认证结果和业务许可,应分别提供能够判断的证据。 Basic 挑战不会使已接受的请求头变成一次性值。本实验中,识别凭据与授予 /admin 权限是两个判断。 快速验证 保存为 basic-auth.py,执行 python3 basic-auth.py。所有凭据都是刻意编造的演示值:用户名为 DEMO,密码为 NOT:A:SECRET。服务器只绑定回环地址,用明文 HTTP 展示协议机制;不要把这些值替换成真实凭据。实验没有测试 TLS、持久账号存储或生产认证服务,也不会读取当前环境的登录信息。 ...

二月 11, 2027 · 2 分钟 · XB Field Notes
不透明会话模型中,浏览器保存含不透明标识和存储期限的 Cookie。标识查找服务端保存用户状态和服务端期限的会话记录。退出时既清除 Cookie,也作废会话。

Cookie 与会话守住边界

Cookie 是浏览器的存储与传输机制。服务端会话则是一种应用设计,用 Cookie 的值查找服务器保存的状态。分开这两种责任,登录、过期和退出才容易验证。本文沿着不透明会话标识的生命周期展开,不再重复主机、路径或 SameSite 匹配规则;这些发送条件不能回答旧会话是否已经真正失效。 Cookie 期限控制浏览器存储,会话有效性和操作授权由服务端负责。替换并作废旧标识后,还要测试重放。两种计时示例使用原文实验单位,并未证明浏览器或分布式系统行为。 持有标识仍可能获得权限 在这种设计中,服务器生成不可预测的标识,并把它关联到一条会话记录。客户端不需要理解该值,但持有它仍可能获得访问资格:窃取有效 bearer 标识的人,可以尝试直接重放。称它“只是指针”不能让读者以为它无害。不要把它放入 URL、日志、诊断截图或支持材料中,随机字符串也可能是实际凭据。 不能直接信任客户端控制的明文角色字段。不过,Cookie 中含有有意义的数据,也不等于必然可以伪造:经过正确认证和签名的会话格式属于另一种设计。签名保护完整性,不提供保密性;无状态方案仍须明确过期、密钥管理与撤销策略。应选择维护良好的会话框架,不要仅为了省一次服务端查询就自行发明格式。 不透明方案需要使用密码学安全随机生成器和足够熵值。未知标识应被拒绝,而不能用客户端任意指定的值建立特权状态。服务器解析会话后,还须根据当前权限授权每项操作。某个会话已经认证,不代表它可以读取每一个对象、操作每一个账号,或自动获得所有管理功能;会话存在与具体动作获准是两项检查。 两种寿命需要两处控制 Cookie 的 Max-Age 与 Expires 管理客户端存储,不会自动使服务端会话记录过期。非浏览器客户端可以保留这个值,并在浏览器本该删除后继续重放。浏览器会话 Cookie 也可能通过会话恢复机制保留。因此必须在服务端强制执行绝对期限与空闲期限,使用明确的边界比较和一致的时间来源,而不能靠标签页里的倒计时作为安全判断。 退出时既要作废服务端状态,也要按匹配的作用域清除浏览器 Cookie,否则复制出来的令牌仍可能有效。认证后与权限变化后轮换标识,有助于防止会话固定,但前提是旧标识确实失去权限。并发请求和分布式存储需要额外协调,避免一个较晚完成的旧请求悄悄恢复已经撤销的会话;只覆盖单进程正常路径远远不够。 ...

一月 12, 2027 · 2 分钟 · XB Field Notes
身份提供方认证用户,将 ID Token 交给应用边界。应用验证令牌并绑定登录请求,继续管理自己的会话与访问决定。ID Token 过期不会自动结束应用会话。

单点登录委托认证,应用仍需验证

单点登录让应用依赖身份提供方的认证结果,用户不必把提供方的密码交给每一个应用。应用仍要负责自己的授权决定、本地会话和接入配置;机密客户端还可能持有客户端凭据。委托认证不会消除应用的安全边界,也不保证以后每次登录都无需用户交互。是否需要重新认证,仍然受提供方策略、当前会话和应用要求影响。 SSO 将认证结果传入应用自身的安全边界。应用仍要验证令牌、绑定请求并管理会话和访问。解码不能建立可信身份;ID Token 的过期不会自动结束一个独立的应用会话。 先分清协议与令牌 SAML 与 OpenID Connect 都可支持联合身份,但它们是不同协议,不能简化成可以互换的 XML 与 JSON 编码。本文聚焦 OIDC:它在 OAuth 2.0 之上定义身份层,通过 ID Token 表达认证信息。访问令牌用于访问资源,格式也可能不同。一个看起来像 JWT 的字符串并不自动成为 ID Token;资源服务器更不能因为认识签发者,就把任意一种令牌当作访问许可。 ...

十一月 17, 2026 · 2 分钟 · XB Field Notes
URL 内高亮查询令牌分支传播到日志、复制链接与地址记录;下方独立 URL 和请求头色条表示字段分离。

让可复用访问令牌远离请求地址

可复用的持有者访问令牌把约定访问能力交给持有它的人;发送者约束令牌还要求提供与发送者绑定的证明。把令牌放进地址,会让这项能力进入平时就会复制地址的系统,例如访问日志、监控记录、浏览器导航历史、书签和分享链接。具体暴露范围取决于请求及策略,但查询参数的名称看起来像秘密,并不会自动让它受到保护。传输位置本身就是凭据披露边界的一部分,应当结合它会经过哪些记录和分享环节判断。 对于 OAuth 持有者令牌请求,应采用 Authorization 请求头;RFC 9700 现已禁止客户端按照 RFC 6750 的查询方式,在 URI 查询参数中传递访问令牌。HTTPS 仍然不可缺少,因为把值从查询移动到请求头并不会加密它;TLS 保护的是到终止位置为止的传输数据。传输结束后,应用和中间组件仍然能够检查字段。请求头的直接收益,是让令牌离开请求地址,而不是保证它永远不会被日志或诊断工具捕获。不能把两种不同的保护作用混为一谈。 分清减少暴露与绝对保密 某些访问日志只记录方法、请求目标和状态。在这种格式里,请求头令牌不会出现在目标字段中,而查询令牌会出现。但其他系统可能采集请求头,因此授权字段仍需在日志、链路和错误报告配置中被明确处理。应验证真实管线究竟保存了什么,不能假定请求头可以绕过全部日志层。检查时使用无效的合成值即可,不应为了演示而把真正凭据再次写入更多记录。 引用来源策略能够减少源地址向后续目标披露,却不会删除已经发给目的地的查询内容,也不会抹去浏览器记录或改写已复制的链接。同样,禁止缓存响应并不是要求清除全部访问日志和导航历史的通用指令。把令牌移到片段,会改变 HTTP 请求发送哪些内容,但值仍可被页面脚本和浏览器地址状态访问,也不是适合所有场景的凭据存储方案。 坏掉的样子 团队可能把接口认证从查询参数改成请求头,却让旧的带令牌地址继续留在报告和共享书签中。传输方式改变并不会撤销此前暴露,需要调查受影响的副本,并在系统支持时使已经暴露的可复用凭据失效。另一种误判是把有效期很短的链接称为泄露也无所谓;在有效期间,任何取得这项能力的人仍然可能使用它。缩短时间窗口只能减少风险,不能取消披露的后果。 当实际客户端流程需要时,签名地址可以是有意设计的、范围很窄的资源访问方式,但允许访问的资源、操作、期限和处理规则都应明确审阅。到期失效与只能使用一次并不相同,要阻止到期之前重复使用,需要真正强制执行消费状态的机制。简单的回调接口或浏览器导航,也不会自动构成把无限制可复用接口凭据放进查询的理由,应根据真实客户端选择合适的认证流程。 请求头位置减少 URL 暴露,但请求头仍须保护。签名资源 URL 是独立限定的设计,不是 OAuth 例外。运行与技术检查仍受阻。 快速验证 保存代码为 token-placement.py,执行 python3 token-placement.py --serve,打开打印出的页面。原文报告使用 Python 3.14.4 和 Chrome 152,本轮源码复核没有复现。显式 Python 检查在优化模式下仍会执行;全部演示值都是明确无效的固定标记,不能替换为真实凭据。回环服务只是观察实验,不是认证服务;它记录请求目标以及示例授权头是否存在,不记录该请求头的值。Python 已禁用代理发现并拒绝重定向,浏览器代理策略仍须单独控制。服务模式默认保留 120 秒,--serve-seconds 接受 1–300;控制键加 C 也会请求清理。只有单独的清理结果确认服务线程停止且监听关闭;有时限的 join 未能确认结束就会报错。临时事件只保存在进程内存,前面的检查通过不证明清理已经完成。 ...

十一月 1, 2026 · 3 分钟 · XB Field Notes