921 字
2 分钟
源码审计实操:硬编码 JWT、注册直传角色提权与安慰剂防注入的代码级复盘
最近在审计一套外部交付的代码时,挖到了一堆堪称反面教材的安全实现。这些代码表面上写了不少所谓的“防护逻辑”,但只要读进源码底层,就会发现它从认证、授权到防注入几乎处处都是致命破绽。
今天不扯虚的框架概念,直接把这些有问题的代码片段拉出来,逐行分析其漏洞机理并给出具体的加固实现。
1. 认证与权限漏洞细节
1.1 JWT 签名密钥硬编码 + Claims 无角色声明
在后端的配置文件 appsettings.json 中,JWT 签名密钥被明文写死:
"Jwt": { "SecretKey": "abcdwlsnhledkhasihvskjpszisoivchjvhxhfav"}查看其生成 Token 的源码逻辑:
var claims = new[]{ new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), new Claim(JwtRegisteredClaimNames.Name, user.username), new Claim("userid", user.id.ToString())};var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:SecretKey"]));var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);致命破绽:
- 密钥硬编码且已随源码泄露,任何人都能在本地用同一把 Key 构造出签名完全合法的 JWT。
- Claims 中没有任何权限角色(Role)声明,服务端收到请求后完全靠
userid回查。攻击者伪造一个userid=1的 Token,就能直接伪装成超级管理员。
1.2 注册接口客户端直传 role 字段
再看用户注册接口 userinfoController.cs:
[HttpPost("AddUser")][AllowAnonymous]public async Task<IActionResult> AddUser([FromBody] UserInfo user){ // 直接将客户端反序列化的对象写入数据库 _context.UserInfo.Add(user); await _context.SaveChangesAsync();
// 把包含密码的完整对象回显给前端 return Ok(new { code = 200, data = user });}- 漏洞机理:接口打着
[AllowAnonymous]允许匿名调用,却没有设计专用的入参 DTO。前端传来什么字段就往数据库写什么字段。 - 只要构造请求体:
{"username": "hacker", "password": "123", "role": "超级管理员"},直接在数据库生成超级管理员账号。 - 响应体直接返回
data: user,包含了刚写入的明文密码,前端甚至还写了一句console.log(response.data),把明文密码输出在浏览器控制台。
1.3 权限校验退化为字符串 Hardcode
在公共权限检查类 comMethod.cs 中:
public static bool IsAdmin(string role, string name){ // 纯字符串暴力匹配 return role == "超级管理员" || role == "admin" || name == "admin";}由于没有基于 Claims 的严格 RBAC 模型,角色完全是一个可以被 Update 接口随意修改的数据库字符串。
2. 伪安全:用 Replace 暴力删字符的“安慰剂”防注入
在公共工具类中,开发者写了这么一段防注入代码:
public static string IsSafeStr(string str){ if (string.IsNullOrEmpty(str)) return ""; return str.Replace("'", "") .Replace(";", "") .Replace("(", "") .Replace(")", "") .Replace("select", "") .Replace("and", "");}为什么说这是最典型的反面教材?
- 底层已有参数化查询:该项目查询全部走 EF Core LINQ,ORM 底层天然使用
@p0参数化传递,原本就不会触发传统 SQL 拼接注入。 - 破坏合法业务数据:用户在提交备注输入
项目A (临时测试)时,括号会被强制剥离成项目A 临时测试;如果输入英文单词standard,中间的and会被剔除变成stard。 - 制造虚假安全感:以为做了黑名单过滤就能防注入,却完全无视了真正危险的未授权与越权访问漏洞。
3. 具体加固代码实现
3.1 专用入参 DTO 与 Argon2id 密码哈希
彻底废弃直接接收数据实体的做法,设计专用注册 DTO,并使用强哈希函数替代明文:
public class UserRegisterDto{ [Required, StringLength(30, MinimumLength = 3)] public string Username { get; set; }
[Required, StringLength(64, MinimumLength = 8)] public string Password { get; set; } // 坚决不暴露 Role、Status 字段}
// 密码加密加固public static class PasswordHasher{ public static string HashPassword(string password) { byte[] salt = RandomNumberGenerator.GetBytes(16); var argon2 = new Konscious.Security.Cryptography.Argon2id(Encoding.UTF8.GetBytes(password)) { Salt = salt, DegreeOfParallelism = 8, Iterations = 4, MemorySize = 65536 // 64 MB }; byte[] hash = argon2.GetBytes(32); return $"{Convert.ToBase64String(salt)}.{Convert.ToBase64String(hash)}"; }}3.2 安全响应头中间件实现
在 Kestrel 管道最前沿注入安全响应头,防御点击劫持与 MIME 嗅探:
public class SecurityHeadersMiddleware{ private readonly RequestDelegate _next; public SecurityHeadersMiddleware(RequestDelegate next) => _next = next;
public async Task InvokeAsync(HttpContext context) { var headers = context.Response.Headers; headers["X-Content-Type-Options"] = "nosniff"; headers["X-Frame-Options"] = "DENY"; headers["Referrer-Policy"] = "strict-origin-when-cross-origin"; headers["X-XSS-Protection"] = "1; mode=block"; headers["Content-Security-Policy"] = "default-src 'self'; frame-ancestors 'none';"; await _next(context); }}3.3 恒定时序校验防用户枚举
登录失败时必须消除时序差异:
public async Task<bool> ValidateLoginAsync(string username, string password){ var user = await _context.Users.FirstOrDefaultAsync(u => u.Username == username);
// 即使用户不存在,也执行一次伪哈希计算,抹平时序差异 string targetHash = user?.PasswordHash ?? DummyHash; bool isValid = PasswordHasher.Verify(password, targetHash);
return user != null && isValid;}4. 小结
代码审计中最忌讳的是“看起来做了防御,其实全在裸奔”。
- 绝不能相信前端传来的任何控制类参数;
- 永远不要手写黑名单 Replace 去防 SQL 注入;
- 认证 Token 必须由环境变量提供高熵密钥,Claims 内必须包含明确的角色声明并在服务端严格校验。
分享
如果这篇文章对你有帮助,欢迎分享给更多人!
源码审计实操:硬编码 JWT、注册直传角色提权与安慰剂防注入的代码级复盘
https://blog.luozili.work/posts/code-audit-bad-security-design-patterns/ 部分信息可能已经过时
相关文章 智能推荐
1
拒绝云端裸奔:前端工具链中“三级数据安全边界”的设计与实现
前端与工程化 探讨在前端工具链与日常办公辅助工装中,如何彻底摆脱“所有文件无脑上传公网云端”的安全隐患。详细解析“浏览器纯本地沙箱 (Green) / 内网离线计算 (Blue) / 外部受控审计 (Yellow)”三级数据安全边界的设计原则,并分享离线版式安全转换、300 DPI 密集排版渲染以及浏览器端 ONNX 智能抠图的硬核工程落地。
2
低代码画布的微内核隔离:基于 Shadow DOM 与 Proxy 的 Child-Realm 沙箱设计
前端与工程化 详细剖析现代低代码可视化设计器在渲染第三方组件与用户自定义脚本时的沙箱隔离难题。对比传统 iframe 方案的通信与弹窗挂载缺陷,深度拆解基于 Web Components 的 Shadow DOM 样式隔离、ES Proxy 拦截全局 window 的 Virtual Realm 沙箱,以及基于不可变 ChangeSet 的状态提交机制。
3
在旗舰手机跑端侧 AI 智能体有多难?YOLO 视觉反向 MCP、端侧沙箱与端侧 TTS 性能实测
人工智能与端侧计算 记录在旗舰级移动设备上构建端侧多模态 AI 智能体(Edge AI Agent)的完整架构与实测经历。深入拆解基于本地 YOLO 的结构化视觉上下文注入、云端大模型反向调用移动端 MCP 工具的设计,以及在天玑 9300 芯片上对端侧 GPT-SoVITS 自回归语音合成模型进行的严苛基准实测,真实呈现为什么当前端侧实时语音交互依然面临严峻的算力挑战。
4
手搓 AI 乐队:5 个大模型协作生成完整分轨音频与动态混音的工程实现
人工智能与多媒体 详细拆解一套基于 5 个 LLM 协同的自动化音乐生成管线。从 Composer 的和弦进行与 8 小节呼应句乐理输出、Arranger 展开为 5 轨乐器模式,到基于 numpy/scipy 的 FM 调制合成与 ADSR 包络发生器,再到动态计算侧链压缩与立体声声相的混音算法,直接输出高质量分轨与成品音频。
5
交换机端口显示 Never Flap 却断网?记一次物理层 PHY 载波与配线架线序排查
网络工程与排查 记录一次离奇局域网断网故障的排查全过程。在三层网关完全通畅的情况下,登录底层二层交换机发现对应 VLAN 的 15 个接口全部为 DOWN 且标注 Last link flapping: Never。深入剖析以太网 PHY 芯片 FLP 自动协商脉冲与载波检测机理,并还原配线架 T568A 与面板 T568B 错位导致差分信号回波损耗超标的物理层硬核破案细节。





