[20][03][05] JSON Injection

1. 问题描述

该方法会将未验证的输入写入 JSON,攻击者可以利用此调用将任意元素或属性注入 JSON 实体

2. 问题场景

JSON Injection 会在以下情况中出现

  • 数据从一个不可信赖的数据源进入程序
  • 将数据写入到 JSON 流

应用程序通常使用 JSON 来存储数据或发送消息,当用于存储数据时,JSON 通常会像缓存数据那样处理,而且可能包含敏感信息.当用于发送消息时,JSON 经常与 RESTful 服务一起使用,并且可以用于传输敏感信息,如身份验证凭据
如果应用程序利用未经验证的输入构造 JSON,则可以更改 JSON 文档和消息的语义.在相对理想的情况下,攻击者可能会插入无关的元素,导致应用程序在解析 JSON 文档或请求时抛出异常.在更为严重的情况下,比如涉及 JSON Injection,攻击者可能会插入无关的元素,从而允许对 JSON 文档或请求中对业务关键的值进行可预见操作.还有一些情况,JSON Injection 可能导 cross-site scripting 或 dynamic code evaluation

下面的 Java 代码使用 Jackson 将非特权用户(这些用户具有"默认"角色,与之相反,特权用户具有"管理员"角色)的用户账户身份验证信息从用户控制的输入变量 username 和 password 序列化位于 ~/ user_info.json 的 JSON 文件

JsonFactory jfactory = new JsonFactory();
JsonGenerator jGenerator = jfactory.createJsonGenerator(new File("~/user_info.json"), JsonEncoding.UTF8);
jGenerator.writeStartObject();

jGenerator.writeFieldName("username");
jGenerator.writeRawValue("\"" + username + "\"");

jGenerator.writeFieldName("password");
jGenerator.writeRawValue("\"" + password + "\"");

jGenerator.writeFieldName("role");
jGenerator.writeRawValue("\"default\"");

jGenerator.writeEndObject();
jGenerator.close();

但是,由于 JSON 序列化使用 jGenerator.writeRawValue() 来执行,将不会对 username 和 password 中的不可信赖数据进行验证以转义与 JSON 相关的特殊字符,这样用户就可以任意插入 JSON 密钥,可能会更改已序列化的 JSON 结构,在本例中,在设置 username 的值的提示符下输入用户名时,如果非特权用户 mallory (密码为 Evil123!)将 ","role" : "admin 附加到其用户名中,则最终保存到 ~/ user_info.json 的 JSON 将为:

{
    "username" : "mallory",
    "role" : "admin",
    "password" : "Evil123!",
    "role" : "default"
}

如果随后将此序列化 JSON 文件反序列化为 HashMap 对象,其中 Jackson 的 jsonParser 如下所示:

JsonParser jParser = jfactory.createJsonParser(new File("~/user_info.json"));

while (jParser.nextToken() != JsonToken.END_OBJECT) {
    String fieldname = jParser.getCurrentName();
    if ("username".equals(fieldname)) {
        jParser.nextToken();
        userInfo.put(fieldname, jParser.getText());
    }
    
    if ("password".equals(fieldname)) {
        jParser.nextToken();
        userInfo.put(fieldname, jParser.getText());
    }
    
    if ("role".equals(fieldname)) {
        jParser.nextToken();
        userInfo.put(fieldname, jParser.getText());
    }
    
    if (userInfo.size() == 3) {
        break;
    }
}
jParser.close();

HashMap 对象中 username, password 和 role 密钥的最终值分别为 mallory, Evil123! 和 admin.在没有进一步验证反序列化 JSON 值是否有效的情况下,应用程序会错误为用户 mallory 分别 admin 权限

3. 解决方案

将用户提供的数据写入 JSON 时,应该遵守以下法则

  • 不要创建名称来自用户输入的 JSON 属性
  • 确保使用安全的序列化函数 (能够以单引号或双引号分隔不可信赖的数据,并且避免任何特殊字符) 执行对 JSON 的所有序列化操作

以下 java 代码实施了与上面相同的功能,但使用了 jGenerator.writeString() 代替 jGenerator.writeRawValue() 来对数据执行序列化操作,因此可以确保适当地分隔和避开任何不可信赖的数据

JsonFactory jfactory = new JsonFactory();
JsonGenerator jGenerator = jfactory.createJsonGenerator(new File("~/user_info.json"), JsonEncoding.UTF8);
jGenerator.writeStartObject();

jGenerator.writeFieldName("username");
jGenerator.writeString("\"" + username + "\"");

jGenerator.writeFieldName("password");
jGenerator.writeString("\"" + password + "\"");

jGenerator.writeFieldName("role");
jGenerator.writeString("\"default\"");

jGenerator.writeEndObject();
jGenerator.close();

3.1 JsonSanitizer.sanitize

可以引入第三方组件对 JSON 做验证, 调用 JsonSanitizer.sanitize() 进行校验

<dependency>
    <groupId>com.mikesamuel</groupId>
    <artifactId>json-sanitizer</artifactId>
    <version>1.0</version>
</dependency>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 .NET Core 中,可以通过以下方式读取 appsettings.json 文件中的配置数据: 1. 首先,在项目根目录下创建一个名为 `appsettings.json` 的文件,并添加要读取的配置数据。例如: ```json { "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "ConnectionStrings": { "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=MyDatabase;Trusted_Connection=True;MultipleActiveResultSets=true" }, "AppSettings": { "ApiKey": "123456" } } ``` 2. 在 `Program.cs` 文件中,使用 `CreateDefaultBuilder` 方法来创建 `HostBuilder`: ```csharp public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }); ``` 3. 在 `Startup.cs` 文件中,使用 `IConfiguration` 接口来读取配置数据。例如: ```csharp public class Startup { private readonly IConfiguration _configuration; public Startup(IConfiguration configuration) { _configuration = configuration; } public void ConfigureServices(IServiceCollection services) { string connectionString = _configuration.GetConnectionString("DefaultConnection"); string apiKey = _configuration.GetValue<string>("AppSettings:ApiKey"); // 将配置数据注册到 DI 容器中 services.AddSingleton(new MyConfigurations(connectionString, apiKey)); } // ... } ``` 在上面的代码中,`IConfiguration` 接口的实例是通过构造函数依赖注入(Dependency Injection)的方式来获取的。然后,使用 `GetConnectionString` 方法来读取 `ConnectionStrings` 节点下的连接字符串,使用 `GetValue` 方法来读取 `AppSettings` 节点下的配置项。 最后,将读取的配置数据注册到 DI 容器中,以便在应用程序的其他地方使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值