PHP框架JWT安全审计需按五步实操:一、用composer audit扫描依赖漏洞,强制升级firebase/php-jwt≥6.13.0;二、定位JWT::decode调用,检查硬编码密钥与算法配置;三、Burp实操none算法绕过、密钥爆破、kid路径遍历;四、伪造user_id/role验证越权;五、检查错误响应与日志是否泄露密钥。

PHP框架审计教程怎么写?JWT令牌安全审计实战——你需要一套可落地的、覆盖依赖扫描→代码模式识别→权限验证→文件上传→日志泄露的完整路径,同时对JWT签名绕过、算法降级、密钥爆破等高频漏洞进行Burp Suite实操验证,不依赖理论堆砌,只讲能立刻执行的步骤。
第一步:快速建立审计环境与依赖基线
在Ubuntu 24.04中进入项目根目录,先确认Composer已安装且版本≥2.5;若未安装,运行sudo apt install composer并验证composer --version输出。
执行composer outdated --direct列出所有直接依赖项及其最新安全版本,重点关注firebase/php-jwt、guzzlehttp/guzzle、laravel/framework等高风险组件。
运行composer audit --no-dev(需提前启用composer-plugin-security),该命令会直接报出CVE编号、影响版本范围及修复建议;若提示“Command 'audit' is not defined”,立即执行composer global require roave/security-advisories:dev-master再重试。
立即学习“PHP免费学习笔记(深入)”;
【必须检查composer.lock中firebase/php-jwt的实际安装版本是否≤6.12.2】——该版本存在已知的none算法签名绕过漏洞(CVE-2023-47852),低于此版本需强制升级至6.13.0+。
第二步:定位JWT签发与校验点
全局搜索关键词:JWT::encode、JWT::decode、Firebase\JWT\JWT::、new \Lcobucci\JWT\Token,优先查看app/Http/Controllers/AuthController.php或app/Services/JwtService.php等认证核心文件。
找到JWT::decode()调用处,检查第二个参数是否为硬编码字符串(如'my_secret_key')或从env('JWT_SECRET')读取;若为前者,说明使用HS256对称算法且密钥静态,【此时已具备离线爆破条件】。
观察JWT::decode()第三个参数是否显式传入算法数组,例如['HS256', 'RS256'];若仅传['HS256']且未做alg头校验,则存在算法混淆风险。
打开config/jwt.php(Laravel)或config/auth.php,确认'supported_algs'配置项是否包含'none';若存在,攻击者可直接删除签名段提交空令牌。
第三步:Burp Suite实战测试JWT三类高危漏洞
方法一:算法降级攻击(none)
拦截登录成功后返回的JWT,在Burp Repeater中将Header部分{"alg":"HS256","typ":"JWT"}改为{"alg":"none","typ":"JWT"},Base64Url编码后替换原Header段,清空Signature段(即最后只剩两个点),发送请求;若返回200且获得管理员权限,说明服务端未校验alg字段。
方法二:密钥爆破
将当前JWT粘贴至Burp Intruder,Payloads类型选Simple list,导入常用密钥字典(如SecLists/Discovery/Web-Content/wordlists/jwt-secrets.txt),攻击位置设为Signature段;启动攻击后,观察Length明显不同的响应——这表示密钥匹配成功。
方法三:Kid注入+服务器端文件读取(SSRF)
若JWT Header中存在"kid"字段(如{"kid":"/etc/passwd"}),在Burp中修改kid值为/var/www/html/.env,发送请求;若响应体中出现DB_PASSWORD=等敏感内容,说明服务端将kid拼接进文件路径且未过滤路径遍历字符。
第四步:验证越权与IDOR是否与JWT耦合
第一步:用正常用户A账号登录,获取其JWT,解码Payload确认"user_id": "1001"和"role": "user";
第二步:修改Payload中"user_id"为"1002"(另一普通用户ID),用原密钥重新签名生成新JWT;
第三步:携带该伪造JWT访问/api/v1/profile,若返回用户1002的手机号、邮箱等隐私字段,证明服务端仅校验JWT签名和过期时间,【未绑定用户上下文做二次鉴权】;
第四步:将"role"改为"admin"并重签,访问/api/v1/admin/users;若返回全量用户列表,说明权限控制完全依赖JWT载荷,无服务端策略引擎校验。
第五步:检查错误信息与日志是否泄露JWT密钥
触发一个强制报错场景:向/api/v1/auth/refresh发送一个格式错误的JWT(如篡改Signature使其验证失败),观察响应体是否包含"message":"Invalid signature"之外的堆栈信息;若出现vendor/firebase/php-jwt/src/JWT.php on line 192,说明display_errors=On未关闭。
在php.ini中确认display_errors = Off且log_errors = On;检查/var/log/apache2/error.log或storage/logs/laravel.log中是否存在JWT_SECRET、APP_KEY等密钥明文打印——某些开发者会在调试时用Log::info("JWT key: $key")误写进日志。
运行grep -r "JWT_SECRET\|APP_KEY" storage/logs/ --include="*.log" 2>/dev/null,若返回任何结果,立即删除对应日志文件并修复代码。



















