JSON 安全考虑

JSON 安全概述

虽然 JSON 是一种相对安全的数据格式,但在实际使用中仍然存在多种安全风险。了解这些风险并采取适当的防护措施至关重要。

💉

JSON 注入

恶意数据注入到 JSON 中

高风险
🔄

JSON 劫持

通过数组重写窃取数据

中风险
📄

XXE 攻击

XML 外部实体攻击

高风险
🌐

JSONP 安全

跨域请求的安全风险

中风险

⚠️ 重要提醒

永远不要信任来自客户端的 JSON 数据。所有来自外部源的 JSON 数据都应该经过严格的验证和清理。

JSON 注入攻击

风险描述

JSON 注入发生在攻击者能够将恶意内容注入到 JSON 数据结构中,可能导致数据泄露、代码执行或其他安全漏洞。

注入示例

// 假设从用户输入构建 JSON
String userInput = "恶意用户\"; alert('XSS'); //";
String json = "{\"name\": \"" + userInput + "\"}";

// 生成的 JSON:
// {"name": "恶意用户"; alert('XSS'); //"}

// 如果直接使用 eval() 解析:
var data = eval('(' + json + ')');
// 这会执行 alert('XSS')

防护措施

使用安全的 JSON 解析方法,永远不要使用 eval() 来解析 JSON。

// 安全的做法 - 使用 JSON.parse()
try {
    var data = JSON.parse(jsonString);
} catch (e) {
    // 处理解析错误
    console.error('Invalid JSON');
}

// 在服务器端验证和清理数据
public String sanitizeJsonInput(String input) {
    // 移除或转义危险字符
    return input
        .replace("\\", "\\\\")
        .replace("\"", "\\\"")
        .replace("'", "\\'")
        .replace("\n", "\\n")
        .replace("\r", "\\r");
}

JSON 劫持 (JSON Hijacking)

风险描述

JSON 劫持是一种利用浏览器特性窃取 JSON 数据的攻击方式。攻击者通过重写 JavaScript 数组构造函数来窃取敏感数据。

攻击原理

// 恶意网站上的代码
var stolenData = [];

// 重写 Array 构造函数
Array = function() {
    // 当敏感数据作为数组返回时,这里会被调用
    stolenData = arguments;
};

// 通过 script 标签加载敏感 JSON 数据
var script = document.createElement('script');
script.src = 'https://api.example.com/sensitive-data';
document.body.appendChild(script);

防护措施

防止 JSON 劫持的最佳实践:

  • 永远不要以数组形式返回敏感 JSON 数据
  • 在 JSON 响应前添加前缀
  • 使用 POST 请求而不是 GET 请求获取敏感数据
  • 设置合适的 CORS 策略
// 不安全的响应 (容易受到劫持)
[{"id": 1, "name": "张三", "email": "zhangsan@example.com"}]

// 安全的响应 - 添加前缀
while(1);[{"id": 1, "name": "张三", "email": "zhangsan@example.com"}]

// 在客户端处理时移除前缀
fetch('/api/sensitive-data')
  .then(response => response.text())
  .then(text => {
    // 移除安全前缀
    const jsonString = text.replace('while(1);', '');
    return JSON.parse(jsonString);
  });

XXE 攻击 (XML External Entity)

风险描述

虽然 JSON 本身不支持外部实体,但某些 JSON 解析库可能支持从 JSON 到 XML 的转换,或者在处理包含 XML 数据的 JSON 时可能受到 XXE 攻击。

攻击示例

// 恶意 JSON 包含 XML 外部实体
{
  "xmlData": "<?xml version=\"1.0\"?>\n" +
              "<!DOCTYPE foo [\n" +
              "<!ELEMENT foo ANY >\n" +
              "<!ENTITY xxe SYSTEM \"file:///etc/passwd\" >]>\n" +
              "<foo>&xxe;</foo>"
}

防护措施

防止 XXE 攻击的最佳实践:

  • 禁用 XML 外部实体处理
  • 使用安全的 XML 解析器配置
  • 验证和过滤输入数据
  • 避免在 JSON 中直接嵌入 XML 数据
// Java - 安全的 XML 解析配置
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

// 禁用外部实体
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);

// 其他安全设置
factory.setXIncludeAware(false);
factory.setExpandEntityReferences(false);

// Python - 安全的 XML 解析
from defusedxml import defused_etree as ET

# 使用 defusedxml 防止 XXE
tree = ET.parse(xml_string)

JSONP 安全考虑

风险描述

JSONP (JSON with Padding) 允许跨域请求,但存在严重的安全风险,包括:

  • CSRF (跨站请求伪造) 攻击
  • 数据泄露
  • 恶意代码执行

JSONP 工作原理

// 客户端请求
function handleData(data) {
    console.log(data);
}

var script = document.createElement('script');
script.src = 'https://api.example.com/data?callback=handleData';
document.head.appendChild(script);

// 服务器响应
handleData({"name": "张三", "age": 30});

防护措施

JSONP 安全最佳实践:

  • 使用 CORS 替代 JSONP
  • 验证回调函数名称
  • 设置 Content-Type 为 application/javascript
  • 实施 CSRF 保护
// 安全的 JSONP 实现 - 验证回调函数
public String handleJsonpRequest(String callback) {
    // 验证回调函数名称是否合法
    if (!callback.matches("^[a-zA-Z_$][0-9a-zA-Z_$]*$")) {
        // 返回错误或使用默认回调
        callback = "callback";
    }
    
    String data = "{\"name\": \"张三\", \"age\": 30}";
    return callback + "(" + data + ");";
}

// 更好的选择 - 使用 CORS
// 服务器设置响应头
response.setHeader("Access-Control-Allow-Origin", "https://trusted-domain.com");
response.setHeader("Access-Control-Allow-Methods", "GET, POST");
response.setHeader("Access-Control-Allow-Headers", "Content-Type");

深度嵌套攻击

风险描述

攻击者可能发送深度嵌套的 JSON 对象,导致解析器消耗大量内存或引发栈溢出,从而造成拒绝服务 (DoS) 攻击。

攻击示例

// 深度嵌套的 JSON (简化表示)
{
  "a": {
    "b": {
      "c": {
        "d": {
          // ... 继续嵌套数千层
        }
      }
    }
  }
}

防护措施

防止深度嵌套攻击:

  • 设置最大解析深度限制
  • 限制 JSON 数据大小
  • 使用流式解析器处理大型数据
  • 实施超时机制
// JavaScript - 自定义解析器检查深度
function safeJsonParse(jsonString, maxDepth = 100) {
    let depth = 0;
    
    const reviver = function(key, value) {
        if (typeof value === 'object' && value !== null) {
            depth++;
            if (depth > maxDepth) {
                throw new Error('JSON too deeply nested');
            }
        }
        return value;
    };
    
    return JSON.parse(jsonString, reviver);
}

// Java Jackson - 配置深度限制
JsonFactory factory = new JsonFactory();
factory.enable(JsonParser.Feature.STRICT_DUPLICATE_DETECTION);

ObjectMapper mapper = new ObjectMapper(factory);
mapper.configure(JsonParser.Feature.AUTO_CLOSE_SOURCE, true);
// 设置其他安全限制...

敏感数据泄露

风险描述

JSON 响应中可能意外包含敏感信息,如密码、API 密钥、内部系统信息等,这些信息可能被攻击者利用。

常见泄露场景

泄露类型 示例 风险等级
调试信息 {"debug": true, "internal_ip": "192.168.1.1"} 中风险
完整错误信息 {"error": "SQL Syntax Error: SELECT * FROM users"} 高风险
敏感字段 {"user": {"password": "plaintext", "ssn": "123-45-6789"}} 高风险
内部标识符 {"user_id": 123, "api_key": "sk_live_..."} 高风险

防护措施

防止敏感数据泄露:

  • 实施数据脱敏和过滤
  • 使用专门的 DTO (数据传输对象)
  • 在生产环境禁用调试信息
  • 记录和监控数据访问
// 安全的用户数据响应
public class UserDTO {
    private Long id;
    private String username;
    private String email;
    // 不包含密码等敏感字段
    
    // getter 和 setter...
}

// 使用 Jackson 注解过滤敏感字段
public class User {
    private Long id;
    private String username;
    private String email;
    
    @JsonIgnore // 不序列化密码字段
    private String password;
    
    @JsonIgnore // 不序列化内部标识符
    private String internalId;
}

// 自定义序列化器进行数据脱敏
public class SensitiveDataSerializer extends JsonSerializer<String> {
    @Override
    public void serialize(String value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        // 对敏感数据进行脱敏
        String masked = value.substring(0, Math.min(2, value.length())) + "***";
        gen.writeString(masked);
    }
}

内容安全策略 (CSP)

防护措施

内容安全策略 (CSP) 可以帮助缓解多种 JSON 相关攻击,特别是 XSS 攻击。

CSP 配置示例

// HTTP 响应头中的 CSP 配置
Content-Security-Policy: default-src 'self';
                       script-src 'self' 'unsafe-inline';
                       object-src 'none';
                       base-uri 'self';
                       connect-src 'self' https://api.example.com;

// Spring Security 配置 CSP
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .headers()
                .contentSecurityPolicy("default-src 'self'; script-src 'self' 'unsafe-inline'; object-src 'none';");
    }
}

// Nginx 配置 CSP
add_header Content-Security-Policy "default-src 'self';";

CSP 指令说明

指令 作用 推荐值
default-src 默认资源加载策略 'self'
script-src 控制 JavaScript 执行 'self' (避免 'unsafe-inline')
connect-src 控制 AJAX、WebSocket 连接 限制到可信域名
object-src 控制 Flash、Java 等插件 'none'

安全演示

在下面的演示中,您可以测试 JSON 安全验证功能。

输入 JSON 数据

安全过滤结果

JSON 安全清单

开发阶段

  • 使用安全的 JSON 解析库
  • 验证所有输入数据
  • 实施适当的错误处理
  • 避免在错误消息中泄露敏感信息
  • 使用参数化查询防止注入

部署阶段

  • 配置适当的内容安全策略 (CSP)
  • 设置安全的 HTTP 头
  • 实施速率限制
  • 启用 HTTPS
  • 配置适当的 CORS 策略

监控和维护

  • 定期更新依赖库
  • 监控异常请求模式
  • 实施日志记录和审计
  • 定期进行安全测试
  • 建立安全事件响应计划

最佳实践对比

不安全做法 安全做法 说明
使用 eval() 解析 JSON 使用 JSON.parse() eval() 会执行任意代码
信任所有客户端数据 验证和清理所有输入 客户端数据可能被篡改
返回完整错误信息 返回通用错误消息 防止信息泄露
使用 JSONP 跨域 使用 CORS CORS 更安全可控
无限制的 JSON 大小 设置大小和深度限制 防止 DoS 攻击