Jsoniter参数走私浅析
1. Jsoniter简介
Jsoniter(Json-iterator)是一个高性能的JSON解析库,为Java和Go语言提供了简单而高效的API来进行JSON的序列化和反序列化操作。相比标准库,Jsoniter在性能上有显著优势,但也带来了一些潜在的安全风险。
2. 参数走私原理
参数走私(Parameter Smuggling)是指利用不同组件对输入数据解析的差异,构造特殊输入使系统产生不一致的解析结果,从而绕过安全检查或产生非预期行为。
在Jsoniter中,参数走私主要源于其对JSON数据的宽松解析策略,特别是:
- 对重复键的处理
- 对注释的支持
- 对尾随逗号的容忍
- 对非标准JSON格式的解析
3. Jsoniter特有的参数走私场景
3.1 重复键处理
Jsoniter默认情况下会保留最后一个出现的键值对,而其他解析器可能保留第一个或抛出异常。
攻击示例:
{
"user": "admin",
"user": "guest"
}
不同解析器可能得到不同的user值,可能导致权限绕过。
3.2 注释支持
Jsoniter支持JavaScript风格的注释,而标准JSON规范不支持。
攻击示例:
{
"user": "admin" /* 这是管理员 */,
"role": "user"
}
如果前端过滤了role字段但后端支持注释,攻击者可构造:
{
"user": "admin",
"role": "admin" /*,
"role": "user" */
}
3.3 尾随逗号
Jsoniter允许对象和数组中的尾随逗号,而严格解析器会拒绝。
攻击示例:
{
"user": "admin",
"role": "user",
}
可能被某些WAF忽略,但Jsoniter会正常解析。
3.4 非标准数字格式
Jsoniter支持十六进制、八进制和二进制格式的数字。
攻击示例:
{
"id": 0x10, // 16
"permission": 010 // 8
}
可能导致类型混淆或数值误解。
3.5 单引号字符串
Jsoniter支持单引号字符串,而标准JSON只支持双引号。
攻击示例:
{
'user': 'admin'
}
可能绕过基于标准JSON的输入验证。
4. 防御措施
4.1 配置严格模式
JsonIterator iter = JsonIterator.parse(json);
iter.config(Feature.AllowComment, false);
iter.config(Feature.AllowSingleQuotes, false);
iter.config(Feature.AllowTrailingComma, false);
4.2 输入验证
- 使用标准JSON解析器进行初步验证
- 对关键字段进行白名单验证
- 检查JSON结构的规范性
4.3 输出编码
在将JSON数据传递给其他组件前,使用严格模式重新序列化:
String safeJson = JsonStream.serialize(obj);
4.4 上下文感知
根据使用场景配置不同的解析策略:
- 内部API可以宽松
- 外部接口必须严格
5. 实际案例分析
5.1 权限绕过
假设系统通过JSON中的role字段进行权限检查:
{
"user": "alice",
"role": "user"
}
攻击者可构造:
{
"user": "alice",
"role": "user",
"role": "admin"
}
Jsoniter会取最后一个role值"admin",可能导致权限提升。
5.2 WAF绕过
假设WAF过滤<script>标签:
{
"content": "<script>alert(1)</script>"
}
攻击者可利用注释:
{
"content": "<scr" + "ipt>alert(1)</script>",
"content": "safe text" /*
}
6. 测试方法
- 发送包含重复键的JSON,观察行为
- 尝试使用注释、尾随逗号等非标准格式
- 比较不同解析器的输出差异
- 检查宽松解析是否导致安全控制失效
7. 总结
Jsoniter的高性能特性带来了潜在的安全风险,特别是在异构系统中,不同组件使用不同解析器时。开发者应当:
- 了解使用的JSON解析器的特性
- 在安全敏感场景使用严格模式
- 实施多层防御,不依赖单一安全控制
- 定期审计JSON处理逻辑的安全性
通过合理配置和防御措施,可以在享受Jsoniter性能优势的同时避免参数走私风险。