一、API成批分配漏洞介绍
批量分配:在API的业务对象或数据结构中,通常存在多个属性,攻击者通过篡改属性值的方式,达到攻击目的。比如通过设置user.is_admin和user.is_manager的值提升用户权限等级;假设某API的默认接口调用参数为{"user_name":"user","is_admin":0},而恶意攻击者修改请求参数,提交值为{"user_name":"attacker","is_admin":1},通过修改参数is_admin的值来提升为管理员权限。
应用程序中潜藏的安全漏洞是一种非常大的隐患,攻击者会通过某一个安全漏洞窃取站点内的用户隐私等重要信息。在扫描应用程序时,我们需要对多项内容进行配置。那么,AppScan扫描应用程序需要URL吗?是需要的。在新版的AppScan还可以扫除API成批分配的问题,我们需要AppScan扫出API成批分配问题解决方案来解决。
APPScan 10.0.7新增的扫描功能 HCL AppScan® Enterprise 中的新增功能 (hcltechsw.com),增加了批量分配API的规则。
AppScan扫描应用程序需要URL吗
AppScan扫描应用程序是否需要URL,那么我们就要看AppScan是如何扫描应用程序的。
1.启动扫描位置
图1:主界面
这里我们使用的是AppScan 10.0.8版本,启动软件后,单击主界面中【web应用程序】便可启动扫描进程。
2.配置扫描
图2:配置界面
在配置界面中第一项便是要求填写【URL和服务器】,在图2中【从该URL开始扫描】位置输入目标URL即可。
3.完全扫描模式下输入URL
图3:完全扫描
当我们启动【完全扫描】模式时,在【探索】位置我们可以对【URL和服务器】进行配置,配置方式和【扫描web应用程序】的配置方式一致。
4.web API
AppScan主界面中除了【web应用程序】和【完全配置】外,还有【web API】,web API的配置是不需要的填写URL的。
图4:扫描web API
web API虽然无需填写URL,但需要填写客户机和描述文件。
漏洞条件:
1、接口类型为application/json ,参数传值、form表单等类型暂未受影响。
2、请求json参数不是接收参数的javabean及其父类中的任意属性。
3、接口HTTP状态码为200
请求json参数不是接收参数的javabean及其父类中的任意属性。
意思就是:我javaben里面没有这个参数,你却传递过来了,例如我只需要pageNum和pageSize 你还传了role:admin 那么这样就有可能导致致特权升级、数据篡改、绕过安全机制。
二、AppScan扫出API成批分配问题
可利用性
利用通常需要了解业务逻辑、对象关系和 API 结构。 在 API 中利用批量分配更容易,因为按照设计,它们公开了应用程序的底层实现以及属性名称。
安全弱点
现代框架鼓励开发人员使用自动将客户端输入绑定到代码变量和内部对象的函数。 攻击者可以使用这种方法来更新或覆盖开发人员从未打算公开的敏感对象的属性。
影响
利用该漏洞可能会导致权限升级、数据篡改、绕过安全机制等。
API 是否容易受到攻击
现代应用程序中的对象可能包含许多属性。 其中一些属性应由客户端直接更新(例如,user.first_name 或 user.address),而另一些则不应该(例如,user.is_vip 标志)。
如果 API 端点自动将客户端参数转换为内部对象属性,而不考虑这些属性的敏感性和暴露级别,则该端点容易受到攻击。 这可能允许攻击者更新他们不应访问的对象属性。
敏感属性的示例
权限相关属性:user.is_admin、user.is_vip 只能由管理员设置。
与流程相关的属性:user.cash 只能在付款验证后在内部设置。
内部属性:article.created_time 只能由应用程序内部设置。
攻击场景示例
场景#1
乘车共享应用程序为用户提供了编辑其个人资料的基本信息的选项。 在此过程中,API 调用将使用以下合法 JSON 对象发送到 PUT /api/v1/users/me:
{“user_name” : “inons”,“age” : 24}
请求 GET /api/v1/users/me 包含一个额外的credit_balance 属性:
{"user_name" : "inons", "age":24, "credit_balance" : 10}
攻击者使用以下有效负载重放第一个请求:
{“user_name” : “攻击者”,“age” : 60,“credit_balance” : 99999}
由于端点容易受到大规模分配的影响,攻击者无需付费即可获得积分。
场景#2
视频共享门户允许用户上传和下载不同格式的内容。 探索 API 的攻击者发现端点 GET /api/v1/videos/{video_id}/meta_data 返回带有视频属性的 JSON 对象。 其中一个属性是“mp4_conversion_params”:“-v codec h264”,这表示应用程序使用 shell 命令来转换视频。
攻击者还发现端点 POST /api/v1/videos/new 容易受到批量分配的影响,并允许客户端设置视频对象的任何属性。 攻击者设置恶意值如下:“mp4_conversion_params”:“-v codec h264 && format C:/”。 一旦攻击者将视频下载为 MP4,该值将导致 shell 命令注入。
攻击者利用批量分配漏洞
当来自客户端的手动修改不可变内部对象属性的请求不受 API 端点限制时,就会出现 API 批量分配漏洞。
攻击者可以利用此漏洞,通过构建 HTTP 请求来升级用户权限、绕过安全机制或使用任何其他方法使 API 端点以非设计的方式工作。
注意:批量分配和过多数据暴露在 OWASP API Sec 2019 中是一个单独的风险类别,现在已合并到名为“损坏对象属性级别授权”的新风险类别中。
如何预防
如果可能,请避免使用自动将客户端输入绑定到代码变量或内部对象的函数。
仅将应由客户端更新的属性列入白名单。
使用内置功能将客户端不应访问的属性列入黑名单。
如果适用,请显式定义并强制执行输入数据有效负载的架构。
增加反序列化配置。
三、成批分配漏洞解决方案
方案1:增加反序列化配置方案
1、反序列化所使用的框架的配置
jackson
如果SpringBoot使用的默认jackson做的序列化,可以考虑对jackson配置来解决传冗余参的问题。
可以直接在配置文件中增加配置
spring:jackson:serialization:# 某些类对象无法序列化的时候,是否报错fail_on_empty_beans: truedeserialization:# json对象中有不存在的属性时候,是否报错fail_on_unknown_properties: true
或自建配置类
@Configuration
public class JacksonConverters {@Beanpublic HttpMessageConverters JacksonHttpMessageConverters() {MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();ObjectMapper objectMapper = new ObjectMapper();//省略其他配置开始//反序列化的时候如果多了其他属性,抛出异常objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true);//省略其他配置结束}}
fastjson
如果SpringBoot使用的是fastjson做的序列化,在2.0.42版本中增加了解决方案,可以考虑对fastjson配置来解决传冗余参的问题。
/*** @author admin*/
@Configuration
public class Fastjson2Config {/*** 使用fastJson解析*/@Beanpublic HttpMessageConverters fastJsonHttpMessageConverters() {FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();FastJsonConfig config = new FastJsonConfig();/** API成批分配利用问题(反序列化的时候如果多了其他属性)修复方法:* 第一:json反序列化组件设置,如:fastjson与jackson* 第二:签名验证** fastjson 2.0.42增加了ErrorOnUnknownProperties修复API成批分配利用问题,反序列化的时候如果多了其他属性,抛出异常* https://github.com/alibaba/fastjson2/issues/1944** JSONObject jsonObject = JSONObject.of("id", 123, "name", "xxx");* String str = jsonObject.toJSONString();* assertEquals(jsonObject.getIntValue("id"), JSON.parseObject(str, Bean.class).id);* assertThrows(JSONException.class, () -> JSON.parseObject(str, Bean.class, JSONReader.Feature.ErrorOnUnknownProperties));* assertEquals(jsonObject.getIntValue("id"), jsonObject.toJavaObject(Bean.class).id);* assertThrows(JSONException.class, () -> jsonObject.toJavaObject(Bean.class, JSONReader.Feature.ErrorOnUnknownProperties));** jackson中可以加入* spring:* jackson:* deserialization:* fail-on-unknown-properties: true* 或* //反序列化的时候如果多了其他属性,抛出异常* objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true);** 统一异常捕获或者返回处增加非200状态码**/config.setDateFormat("yyyy-MM-dd HH:mm:ss");config.setReaderFeatures(JSONReader.Feature.ErrorOnUnknownProperties);config.setWriterFeatures(JSONWriter.Feature.WriteMapNullValue, JSONWriter.Feature.PrettyFormat);converter.setDefaultCharset(StandardCharsets.UTF_8);List<MediaType> supportedMediaTypes = new ArrayList<>();supportedMediaTypes.add(MediaType.APPLICATION_JSON);converter.setFastJsonConfig(config);converter.setSupportedMediaTypes(supportedMediaTypes);return new HttpMessageConverters(converter);}}
2、统一异常捕获或者返回处增加非200状态码
/*** 捕获反序列化异常HttpMessageNotReadableException,增加500状态码返回* @param request 请求* @param exception 异常对象* @return 响应*/
@ExceptionHandler(value = HttpMessageNotReadableException.class)
public ResponseEntity<Map<String, Object>> methodHttpMessageNotReadableExceptionHandler(HttpServletRequest request, HttpMessageNotReadableException exception) {//按需重新封装需要返回的错误信息WebRequest webRequest = new ServletWebRequest(request);Map<String, Object> body = errorAttributes.getErrorAttributes(webRequest, ErrorAttributeOptions.defaults());body.put(DATA, "convert exception message to JSON");body.put(STATUS, HttpStatus.INTERNAL_SERVER_ERROR.value());body.put(MESSAGE, HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase());body.put(SUCCESS,false);return new ResponseEntity<>(body, HttpStatus.INTERNAL_SERVER_ERROR);
}
或者在其他异常拦截方法上增加状态码注解
@ResponseStatus()
方案二:接口使用参数签名机制
如HMac, HMac Springboot, 微信支付参数签名
#前端请求
请求 URL: http://localhost/cars/query
请求方法: POST
HTTP状态码:200
playload:{"color":"red","company":"ltl","seats":"2-2"} #正常请求
Header:sign:ErOVBda4VMFdX9aixigRslAjY0rhT7lLxy
#后端controller
@PostMapping(value = "/query")
public BaseResponse query(@RequestBody Car car) {String signFront = request.header("sign");String signBackend = SignUtils.handler(car);if(!signBackend.equals(signFront)) {throws new ServiceErrorException("签名异常"); }}
Jackson类库解决方案
Solution - Jackson @JsonView
We can create JSON view like below:
public class View {public static class Editable {}public static class Viewable extends Editable {}public static class Internal extends Viewable {}}
Then annotate our mode class:
@JsonIgnoreProperties(ignoreUnknown = true)
public class Model implements Serializable {@JsonView(View.Editable.class)protected String editableField;@JsonView(View.Viewable.class)protected String viewableField; @JsonView(View.Internal.class)protected String internalField;}
At last, we annotate out jax-rs resource with @JsonView annotation.
@GET
@Produces(MediaType.APPLICATION_JSON )
@JsonView(View.Viewable.class)
public Iterable<Model> search() {}
@GET
@Path("{id}")
@Produces(MediaType.APPLICATION_JSON )
@JsonView(View.Viewable.class)
public Model getModel(@PathParam("id") final String id) {}
@POST
@Consumes({MediaType.APPLICATION_JSON})
public Response add(@JsonView(View.Editable.class) final Model model) {}
Spring MVC provides data binder that we can specify what fields are not allowed.
禁用字段:
@InitBinder
public void initBinder(WebDataBinder binder) {binder.setDisallowedFields(DISALLOWED_FIELDS);
}
允许字段:
@Controller
public class UserController {@InitBinderpublic void initBinder(WebDataBinder binder, WebRequest request) {binder.setAllowedFields(["userid", "password", "email"]);}...}
四、总结
以上便是,AppScan扫描应用程序需要URL吗,AppScan扫出API成批分配问题解决方案的内容。AppScan扫描应用程序是需要URL的,同时AppScan完全扫描也是需要URL的,在新版的软件中,只有web API模式是无需URL的。新版的AppScan可以扫出API成批分配的问题,对于此类问题,我们只需要执行反序列化配置方案便可拦截API成批分配的问题。更多有关AppScan使用技巧,尽在AppScan中文网站!