Base64UrlSafe accepts bytes 0x80 to 0xFF when ext-sodium is loaded

未关闭
#766 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 1 天内回复

还没有人认领这个 Issue。

评估

难度
3/5
预计耗时
1-2 天
新手友好度
76/100
Issue 类型
缺陷
描述清晰度
描述清楚
活跃度
活跃
技术栈
php
领域
backend, security

调研方向

Start with Base64UrlSafe::decodeNoPadding() and compare its sodium path with the pure PHP decoder; the issue says the class handles key material too. Check how the existing code raises RangeException for invalid input, then verify that bytes outside the base64url alphabet are rejected consistently while valid input still decodes. The issue gives a reproduction using QUJ_ and QUJ\xFF; use these to confirm the behavior.

由索引模型根据 Issue 内容生成。

描述

bug
Version(s) affected

3.4.9 to 3.4.12, 4.0.7 to 4.0.8, 4.1.0 to 4.1.10, 4.2.0 to 4.2.5, and the 4.3.x and 5.0.x branches, when ext-sodium is linked against libsodium 1.0.21 or older on a platform where char is signed (x86-64)

Description

When ext-sodium is loaded, Base64UrlSafe::decodeNoPadding() hands its input to sodium_base642bin() and relies on libsodium to reject characters outside the base64url alphabet. Up to 1.0.21, libsodium decodes every byte from 0x80 to 0xFF as _ where char is signed (seen with 1.0.18 on x86-64). This was fixed upstream in 1.0.22 (jedisct1/libsodium@a615d0ed74, "base64: reject signed chars"). The pure PHP decoder of the same class rejects these bytes, so the result depends on the environment.

Every token segment goes through this decoder. Each _ in the signature of a compact JWS, or in the encrypted key, IV, ciphertext or tag of a compact JWE, can be replaced by any byte from 0x80 to 0xFF, and the token still loads with the same header, payload and plaintext. The header and payload segments are used as received, so they cannot be changed this way.

Nothing is forged or disclosed. A token holder can however produce other strings for a token the application accepts, which matters to applications that recognise a token by its string or its hash (deny-list of revoked tokens, replay cache).

The sodium path was added in #636 (4.1.0) and backported in #644 (3.4.9, 4.0.7).

How to reproduce
use Jose\Component\Core\Util\Base64UrlSafe;

echo bin2hex(Base64UrlSafe::decodeNoPadding('QUJ_')), PHP_EOL;    // 41427f
echo bin2hex(Base64UrlSafe::decodeNoPadding("QUJ\xFF")), PHP_EOL; // 41427f with ext-sodium, RangeException without it
Possible Solution

Reject the bytes outside the alphabet on the sodium path too, with the same RangeException as the pure PHP path. The class also decodes key material (k, d…), so the check should not take a time that depends on the characters, which strspn() does before PHP 8.4. Decoding, re-encoding the result with sodium_bin2base64() and comparing with hash_equals() rejects all 128 high bytes, gives the same output on valid input, and keeps most of the speed-up of the sodium path (22 ms on 1.3 MiB, against 12.5 ms today and 409 ms for the pure PHP path).

Additional Context

Found by the Anthropic OSS Scanner (ANT-2026-DYQEQ988).

主要语言
PHP
星标
954
派生
119
平均合并
48 分钟
30 天内合并 PR
17

环境准备

这个项目没有提供开发容器、Dockerfile 或贡献指南,环境需要你自己搭建:先看它的 README,通用步骤见我们的新手贡献指南。

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

web-token/jwt-framework 的其他 Issue

查看 web-token/jwt-framework 的全部 Issue

相似的 Issue

更多 PHP Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。