Dos----拒绝服务攻击,一般是构造特殊的输入,使得后台的处理耗时远超正常水平,随着请求越来越多,后台服务越发疲于奔命,最后因资源耗尽,无法再接受新的请求,最终造成拒绝服务的效果。
特殊输入例如:
分页查询时传一个很大的pageSize;
入参是一个很大的集合。
对于前者,做一个公共函数检查pageSize的大小,确定合理的范围即可。
对于后者,往往是在post请求里,可以做一个filter来处理所有的incoming request。参考了一些资料,样例代码如下:
@Component
public class BodySizeLimitFilter implements Filter {
private static final List<String> METHODS_WITH_BODY = Collections.unmodifiableList(
Arrays.asList("POST", "PUT", "OPTIONS", "DELETE", "PATCH"));
private static final String CONTENT_LENGTH_HEADER = "Content-Length";
private static final int TOO_LARGE_STATUS = 413;
@Value("${reqbody.maxsize:4096}")
private long contentLengthLimit;
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
if (!METHODS_WITH_BODY.contains(httpServletRequest.getMethod())) {
chain.doFilter(httpServletRequest, httpResponse);
return;
}
long contentSize = getContentLength(httpServletRequest);
if (contentSize > contentLengthLimit) {
resetRespAndSetStatus(httpResponse, TOO_LARGE_STATUS);
} else {
chain.doFilter(request, response);
}
}
private void resetRespAndSetStatus(HttpServletResponse response, int status) {
response.reset();
response.setStatus(status);
}
private long getContentLength(HttpServletRequest httpRequest) {
String contentLength = httpRequest.getHeader(CONTENT_LENGTH_HEADER);
return safeToLong(contentLength, 0);
}
}
不过,考虑到content-length也是可以被篡改的,还是要在rest入口处做一下集合长度的校验。这里可使用springboot的@Valid注解。
对于前端POST调用传过来的requestBody参数,在函数声明里用jaxrs规范的@Valid注解处理,比如:
@Path("/test")
@Consumes({ "application/json;charset=UTF-8" })
@Named
public class TestRestService {
@POST
@Path("/hello")
@Consumes({ "application/json" })
@Produces({ "application/json" })
public ServiceResponse<String> test(@Valid TestParam testParam) {
throw new BaseException("i18n.test.example");
}
同时,在TestParam数据类里用@Size等注解标记要校验的字段:
@Data
public class TestParam {
@Size(max = 10, min = 2, message = "[strs]size between 2~10")
private List<String> strs;
@Size(max = 6, min = 1, message = "[singleStr]length between 1~6")
private String singleStr;
@Min(value = 0)
@Max(value = 1)
private int num;
}
这样spring框架就会自动为我们检查TestParam成员中list类型的长度、整型的大小、字符串类型的长度等。一旦有错误,就会抛出ConstraintViolationException异常,我们设法截获该异常,将错误信息填到Response中,给前台展示。
顺带说一下,校验时,spring controller类上的@Validated注解其实可以不要的。若调用内部spring bean时需做参数校验,则必须在spring bean的类定义上加@Validated注解(@Valid注解不能用于类),然后再在要校验的方法参数前加@Valid等注解。
对于GET调用的参数,校验起来更为简单,直接用@Size等注解修饰函数参数即可:
@GET
@Path("/helloPrimitive")
@Consumes({ "application/json" })
@Produces({ "application/json" })
public ServiceResponse<String> testPrimitive(@QueryParam("id") @Max(value = 10) int id,
@QueryParam("desc") @Size(max=15) String desc) {
throw new BaseException("i18n.test.example");
}