Задать вопрос
@Galaxy773

Как предотвращать XSS в Spring?

Я только начал работать со Spring и у меня возник вопрос, как в нем лучше всего предотвращать атаки XSS?

На данный момент я использую фильтр, который нашел в интернете.
Он не сделан для multipart, поэтому пока я просто отключил для него фильтрацию, иначе "ломаются" бинарные файлы.

Мне нужен фильтр, который бы предотвращал атаки для обычных текстовых запросов и multipart.
Так же у меня на сайте есть wysiwyg editor и нужно, чтобы его тоже корректно фильтровало, оставляя только допустимые символы/теги.

Фильтр, который я использую сейчас:

XSSFilter:
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class XSSFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        //todo: fix
        if (request.getContentType() != null && request.getContentType().startsWith("multipart/form-data")) {
            chain.doFilter(request, response);
            return;
        }

        XSSRequestWrapper wrappedRequest = new XSSRequestWrapper ((HttpServletRequest) request);

        String body = IOUtils.toString(wrappedRequest.getReader());
        if (!body.isBlank()) {
            body = XSSUtils.stripXSS(body);
            wrappedRequest.resetInputStream(body.getBytes());
        }

        chain.doFilter(wrappedRequest, response);
    }
}


XSSRequestWrapper:
public class XSSRequestWrapper extends HttpServletRequestWrapper {

    private byte[] rawData;
    private HttpServletRequest request;
    private ResettableServletInputStream servletStream;

    public XSSRequestWrapper(HttpServletRequest request) {
        super(request);
        this.request = request;
        this.servletStream = new ResettableServletInputStream();
    }

    public void resetInputStream(byte[] newRawData) {
        rawData = newRawData;
        servletStream.stream = new ByteArrayInputStream(newRawData);
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        if (rawData == null) {
            rawData = IOUtils.toByteArray(this.request.getReader(), Charsets.UTF_8);
            servletStream.stream = new ByteArrayInputStream(rawData);
        }
        return servletStream;
    }

    @Override
    public BufferedReader getReader() throws IOException {
        if (rawData == null) {
            rawData = IOUtils.toByteArray(this.request.getReader(), Charsets.UTF_8);
            servletStream.stream = new ByteArrayInputStream(rawData);
        }
        return new BufferedReader(new InputStreamReader(servletStream));
    }

    private class ResettableServletInputStream extends ServletInputStream {

        private InputStream stream;

        @Override
        public int read() throws IOException {
            return stream.read();
        }

        @Override
        public boolean isFinished() {
            return false;
        }

        @Override
        public boolean isReady() {
            return false;
        }

        @Override
        public void setReadListener(ReadListener readListener) {
        }
    }

    @Override
    public String[] getParameterValues(String parameter) {
        String[] values = super.getParameterValues(parameter);
        if (values == null) {
            return values;
        }
        int count = values.length;
        String[] encodedValues = new String[count];
        for (int i = 0; i < count; i++) {
            encodedValues[i] = XSSUtils.stripXSS(values[i]);
        }
        return encodedValues;
    }

    @Override
    public String getParameter(String parameter) {
        String value = super.getParameter(parameter);
        return XSSUtils.stripXSS(value);
    }

    @Override
    public String getHeader(String name) {
        String value = super.getHeader(name);
        return XSSUtils.stripXSS(value);
    }

    @Override
    public Enumeration<String> getHeaders(String name) {
        List<String> result = new ArrayList<>();
        Enumeration<String> headers = super.getHeaders(name);
        while (headers.hasMoreElements()) {
            String header = headers.nextElement();
            String[] tokens = header.split(",");
            for (String token : tokens) {
                result.add(XSSUtils.stripXSS(token));
            }
        }
        return Collections.enumeration(result);
    }
}


XSSUtils:
@UtilityClass
public class XSSUtils {

    public String stripXSS(String value) {
        if (value == null) {
            return null;
        }

        value = ESAPI.encoder()
                .canonicalize(value)
                .replaceAll("\0", "");
        return Jsoup.clean(value, Safelist.none());
    }
}


Заранее извиняюсь, я очень мало разбираюсь в данном вопросе.
  • Вопрос задан
  • 115 просмотров
Подписаться 1 Средний Комментировать
Пригласить эксперта
Ответы на вопрос 1
mayton2019
@mayton2019
Bigdata Engineer
Зачем делать что-то для галочки? Если ты внедрил функцию то ты должен понимать те кейсы в которых она
работат. Попробуй сам воспроизведи XSS-атаку на себя и проверь как она работает.

Иначе будет в исходниках балласт который ты себе объяснить не можешь и newcomer придет и будет спрашивать про исходник а ты и ему не объяснишь.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы