如何使用 Java 防止 Rest API JSON 中的 XSS 攻击或不可信数据?

How to prevent XSS attacks or untrusted data in Rest API JSON using Java?

我开发了一个 Rest API 应用程序并使用自定义 JWT 处理了身份验证和授权。 我想进一步使应用程序免受 XSS 攻击或对不受信任的数据进行验证,这些数据可以针对 JSON 请求的每个字段进行处理。

我能否在这方面获得一些帮助,以便在不触及内部业务验证的情况下,在请求的入口级别进行高效的数据处理?

为此,您需要使用 HTMLUtils 的 XSS 过滤器,它将过滤任何注入的脚本并阻止您的网站。请参考我的回答以获得完整的代码和实现。

需要覆盖 Servlet 过滤器中的 HttpServletRequest(如果您使用的是 Servlet)。

  1. 扩展存储 JSON 主体的 HttpServletRequestWrapper(目的是清理 JSON 主体)。

  2. 剥离/转义符合条件的JSON值

扩展“HttpServletRequestWrapper”

public class SanitizationRequestWrapper extends HttpServletRequestWrapper {
    
        public byte[] getBody() {
            return body;
        }
    
        public void setBody(byte[] body) {
            this.body = body;
        }
    
        private byte[] body;
    
        public SanitizationRequestWrapper(HttpServletRequest request) throws IOException {
            super(request);
            try {
                body = IOUtils.toByteArray(super.getInputStream());
            }catch (NullPointerException e){
    
            }
        }
    
        @Override
        public ServletInputStream getInputStream() throws IOException {
            return new ServletInputStreamImpl(new ByteArrayInputStream(body));
        }
    
        @Override
        public BufferedReader getReader() throws IOException {
            String enc = getCharacterEncoding();
            if (enc == null) enc = "UTF-8";
            return new BufferedReader(new InputStreamReader(getInputStream(), enc));
        }
    
        private class ServletInputStreamImpl extends ServletInputStream {
    
            private InputStream is;
    
            public ServletInputStreamImpl(InputStream is) {
                this.is = is;
            }
    
            public int read() throws IOException {
                return is.read();
            }
    
            public boolean markSupported() {
                return false;
            }
    
            public synchronized void mark(int i) {
                throw new RuntimeException(new IOException("mark/reset not supported"));
            }
    
            public synchronized void reset() throws IOException {
                throw new IOException("mark/reset not supported");
            }
        }
    }
    

清理请求主体的 Servlet 过滤器:

    public class XSSSanitizeFilters implements Filter {
            @Override
        public void destroy() {
        }
    
        @Override
        public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) throws IOException, ServletException {
            HttpServletRequest request = (HttpServletRequest) arg0;
            HttpServletResponse response = (HttpServletResponse) arg1;
            SanitizationRequestWrapper sanitizeRequest = new SanitizationRequestWrapper(request);
                if (null != sanitizeRequest.getBody()) {
                    try {
                        sanitizeJson(sanitizeRequest);
                    } catch (ParseException e) {
                        LOG.error("Unable to Sanitize the provided JSON .");
                    }
                    arg2.doFilter(sanitizeRequest, arg1);
    
                } else {
                    arg2.doFilter(arg0, arg1);
                }       
        }
    
        public void init(FilterConfig filterConfig) throws ServletException {
    
        }
    
        private void sanitizeJson(SanitizationRequestWrapper sanitizeRequest ) throws IOException, ParseException {
                JSONParser parser= new JSONParser();
                Object obj = parser.parse(sanitizeRequest.getReader());
                 ObjectMapper oMapper = new ObjectMapper();
                Map <String, Object> map = oMapper.convertValue(obj, Map.class);
                sanitizeRequest.setBody((new JSONObject(map)).toString().getBytes());
        }
    
       

如果您的 API 不接受任何 HTML 字符,那么您可以按照以下逻辑进行操作。

您可以使用编码净化输入有效载荷Html并将其与提供的有效载荷进行比较。

如果 Sanitized Payload 和 Provided payload 不匹配,则存在一些 Html 内容并直接抛出 Excpetion。

String unsanitizedPayload = IOUtils.toString(multiReadRequest.getReader());
String sanitizedPayload = Encode.forHtmlContent(unsanitizedPayload);

if(!unsanitizedPayload.equals(sanitizedPayload)) {
    throw new Exception("Improper Payload");
}

如果您使用的是 Spring,Spring 安全性可保证针对 XSS 攻击的基本保护级别。您也可以使用

@SafeHtml
private String value;

您还需要添加 org.jsoup 依赖项。

您不过滤或转义 restful API 中的数据。 API 应该与客户端无关。提供 XSS 保护是客户的责任。如果客户正确地完成了他们的工作,你最终会得到双重转义的数据。请记住,潜在客户可以是:

  • 移动应用程序
  • 后端 Web 服务器
  • 网络浏览器
  • 桌面应用程序
  • 嵌入式系统/物联网

以上只有有限数量的客户端和配置易受 XSS 攻击。