基于Servlet 非Spring Boot 增加 Request body Xss

159 阅读1分钟

web.xml

<filter>
    <filter-name>XssEscape</filter-name>
    <filter-class>*.XSSSanitizeFilters</filter-class>
</filter>
<filter-mapping>
    <filter-name>XssEscape</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
</filter-mapping>

XSSSanitizeFilters.java

import java.io.IOException;
import java.util.Map;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringEscapeUtils;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;

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;
            SanitizationRequestWrapper sanitizeRequest = new SanitizationRequestWrapper(request);
            if (null != sanitizeRequest.getBody()) {
                    try {
                            sanitizeJson(sanitizeRequest);
                    } catch (IOException 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 {
            JsonParser parser = new JsonParser();
            JsonElement obj = (JsonElement) parser.parse(sanitizeRequest.getReader());
            obj=clean(obj);
            sanitizeRequest.setBody(obj.toString().getBytes());
    }

    private JsonElement clean(JsonElement elem) {
            if (elem.isJsonPrimitive()) { // Base case - we have a Number, Boolean or String
                    JsonPrimitive primitive = elem.getAsJsonPrimitive();
                    if (primitive.isString()) {
                            // Escape all String values
                            return new JsonPrimitive(StringEscapeUtils.escapeHtml4(primitive.getAsString()));
                    } else {
                            return primitive;
                    }
            } else if (elem.isJsonArray()) { // We have an array - GSON requires handling this separately
                    JsonArray cleanArray = new JsonArray();
                    for (JsonElement arrayElement : elem.getAsJsonArray()) {
                            cleanArray.add(clean(arrayElement));
                    }
                    return cleanArray;
            } else { // Recursive case - iterate over JSON object entries
                    JsonObject obj = elem.getAsJsonObject();
                    JsonObject clean = new JsonObject();
                    for (Map.Entry<String, JsonElement> entry : obj.entrySet()) {
                            // Encode the key right away and encode the value recursively
                            clean.add(StringEscapeUtils.escapeHtml4(entry.getKey()), clean(entry.getValue()));
                    }
                    return clean;
            }
    }
    }

SanitizationRequestWrapper.java

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

import org.apache.commons.io.IOUtils;

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");
            }
    }
}

stackoverflow.com/questions/5…

stackoverflow.com/questions/5…

stackoverflow.com/questions/1…

xz.aliyun.com/t/4067