Web安全之JSON注入攻击原理与防御详解
字数 2181 2025-12-09 03:09:04
Web安全之JSON注入攻击原理与防御详解
1. 什么是JSON注入攻击?
JSON注入是一种针对Web应用程序的注入型攻击,攻击者通过向应用程序提交精心构造的恶意JSON数据,意图破坏应用程序的逻辑、窃取数据或执行未授权操作。与SQL注入、XSS等传统注入不同,JSON注入主要发生在:
- 服务器端:应用程序解析JSON数据时,未经验证或净化就将其用于动态代码执行(如
eval)、数据库查询、系统命令调用或反序列化操作。 - 客户端:JavaScript代码使用
eval()或JSON.parse()后未经验证直接操作DOM或执行函数。
核心风险:JSON作为一种轻量级数据交换格式,本身不是可执行代码,但当它与动态代码执行、反序列化或字符串拼接等不安全操作结合时,就会产生注入漏洞。
2. 攻击原理与常见场景
场景一:JSON动态解析为代码执行(服务器端)
某些编程语言或框架提供动态执行JSON内容的功能,例如:
// 危险示例:Node.js中使用eval解析JSON字符串(实际应使用JSON.parse)
const userInput = '{"name": "Alice", "age": "20; console.log(\'Hacked\')"}';
eval('var obj = ' + userInput); // 执行了额外的代码console.log('Hacked')
攻击方式:
- 攻击者提交JSON:
{"name": "test", "age": "20; process.exit(1)"},导致服务崩溃。 - 利用字符串拼接构造非法JSON,如插入额外键值对或破坏JSON结构。
场景二:JSONP(JSON with Padding)回调函数注入
JSONP用于跨域获取数据,通过<script>标签加载外部JSON数据,并指定回调函数名:
<script src="http://api.example.com/data?callback=handleResponse"></script>
攻击方式:
- 攻击者修改
callback参数为恶意代码:?callback=alert('XSS');//。 - 服务器返回:
alert('XSS');//({"data": "value"});,导致XSS攻击。
场景三:不安全的反序列化
许多语言支持将JSON反序列化为对象(如Python的pickle、Java的Jackson/Gson、PHP的json_decode+对象注入):
# Python示例:使用json.loads但后续将对象用于危险操作
import json
data = json.loads(user_input) # 如果user_input包含恶意类或属性,可能触发副作用
攻击方式:
- 利用反序列化时的特性(如PHP的
__wakeup、Java的readObject),在反序列化过程中执行任意代码。
场景四:NoSQL注入(通过JSON操作)
NoSQL数据库(如MongoDB)使用JSON格式查询,如果未经验证拼接查询:
// 危险示例:拼接用户输入到查询
const query = { username: req.body.username, password: req.body.password };
db.users.find(query);
攻击方式:
- 提交JSON:
{"username": "admin", "password": {"$ne": null}},绕过密码验证。
3. 攻击步骤详解(以NoSQL注入为例)
假设登录接口接收JSON:{"username": "user", "password": "pass"},后端使用MongoDB查询:
app.post('/login', (req, res) => {
const user = db.users.findOne({ username: req.body.username, password: req.body.password });
});
攻击过程:
- 探测:发送正常JSON,确认接口响应。
- 注入尝试:将
password改为操作符,提交:{"username": "admin", "password": {"$ne": ""}} - 结果:查询变为
db.users.findOne({username: "admin", password: {$ne: ""}}),只要password非空即返回用户数据,绕过登录。
4. 防御策略
策略一:输入验证与净化
- 严格模式:使用
JSON.parse()替代eval(),但需确保JSON格式合法。 - 白名单验证:对JSON键值进行类型、长度、格式检查(如用户名只允许字母数字)。
- 库函数转义:使用
JSON.stringify()生成JSON字符串,避免手动拼接。
策略二:安全反序列化
- 禁用危险特性:如PHP中
json_decode()避免与__wakeup结合;Java中使用Jackson的@JsonCreator而非默认构造器。 - 使用安全库:如Python的
simplejson、Java的Gson(默认不支持任意类反序列化)。
策略三:JSONP安全
- 验证回调函数名:只允许字母数字和下划线,拒绝特殊字符。
- 设置Content-Type:响应头添加
Content-Type: application/javascript; charset=utf-8,避免浏览器误解析。 - 使用CORS替代JSONP:现代应用优先使用CORS实现跨域。
策略四:NoSQL查询防御
- 参数化查询:使用ORM或驱动提供的安全方法(如Mongoose的
findOne直接传对象,但避免用户控制操作符)。 - 类型转换:将用户输入强制转换为字符串或指定类型。
- 操作符过滤:禁止查询中出现
$where、$ne等危险操作符。
策略五:输出编码与安全头
- 设置HTTP头:
Content-Type: application/json避免浏览器执行响应内容。 - XSS防护:即使JSON注入成功,也要对输出到HTML的内容进行编码。
5. 最佳实践示例
// 安全示例:使用JSON.parse并验证数据
function safeParse(jsonString) {
try {
const data = JSON.parse(jsonString);
// 验证必需字段和类型
if (typeof data.username !== 'string' || data.username.length > 50) {
throw new Error('Invalid username');
}
return data;
} catch (e) {
return null;
}
}
// MongoDB查询防御:使用显式查询构造
app.post('/login', (req, res) => {
const username = String(req.body.username); // 强制类型转换
const password = String(req.body.password);
db.users.findOne({ username: username, password: password }); // 避免操作符注入
});
6. 总结
JSON注入的本质是将不受信任的数据作为代码或查询逻辑的一部分。防御关键在于:
- 不信任任何输入:对JSON数据做严格验证和类型检查。
- 避免动态执行:禁用
eval()、Function()等危险函数。 - 使用安全API:优先使用标准库的JSON解析和序列化方法。
- 最小化攻击面:关闭不必要的反序列化功能或操作符支持。
通过结合输入验证、输出编码和安全配置,可有效防护JSON注入攻击。