Изменить параметр запроса с помощью фильтра сервлетов


существующее веб-приложение работает на Tomcat 4.1. Существует проблема XSS со страницей, но я не могу изменить источник. Я решил написать фильтр сервлета для очистки параметра, прежде чем он будет виден на странице.

Я хотел бы написать класс фильтра такой:

import java.io.*;
import javax.servlet.*;

public final class XssFilter implements Filter {

  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
      throws IOException, ServletException
  {
    String badValue = request.getParameter("dangerousParamName");
    String goodValue = sanitize(badValue);
    request.setParameter("dangerousParamName", goodValue);
    chain.doFilter(request, response);
  }

  public void destroy() {
  }

  public void init(FilterConfig filterConfig) {
  }
}

но ServletRequest.setParameter не существует.

Как я могу изменить значение параметра запроса перед передачей запроса вниз по цепочке?

6 99

6 ответов:

как вы отметили HttpServletRequest не имеет метода setParameter. Это преднамеренно, так как класс представляет запрос, как он пришел от клиента, и изменение параметра не будет представлять это.

одним из решений является использование HttpServletRequestWrapper класс, который позволяет обернуть один запрос с другой. Вы можете подкласс и переопределить getParameter метод для возврата вашего санированного значения. Затем вы можете передать этот завернутый запрос в chain.doFilter вместо оригинального запрос.

это немного некрасиво, но это то, что сервлет API говорит, что вы должны сделать. Если вы попытаетесь передать что-нибудь еще doFilter, некоторые контейнеры сервлетов будут жаловаться, что вы нарушили спецификацию, и откажутся ее обрабатывать.

более элегантное решение - это больше работы-измените исходный сервлет / JSP, который обрабатывает параметр, чтобы он ожидал запроса атрибут вместо параметра. Фильтр проверяет параметр, дезинфицирует его и устанавливает атрибут (используя request.setAttribute) С санированной значение. Нет подклассов, нет спуфинга, но требует, чтобы вы изменили другие части вашего приложения.

для записи, вот класс я закончил писать:

import java.io.IOException;

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 javax.servlet.http.HttpServletRequestWrapper;

public final class XssFilter implements Filter {

    static class FilteredRequest extends HttpServletRequestWrapper {

        /* These are the characters allowed by the Javascript validation */
        static String allowedChars = "+-0123456789#*";

        public FilteredRequest(ServletRequest request) {
            super((HttpServletRequest)request);
        }

        public String sanitize(String input) {
            String result = "";
            for (int i = 0; i < input.length(); i++) {
                if (allowedChars.indexOf(input.charAt(i)) >= 0) {
                    result += input.charAt(i);
                }
            }
            return result;
        }

        public String getParameter(String paramName) {
            String value = super.getParameter(paramName);
            if ("dangerousParamName".equals(paramName)) {
                value = sanitize(value);
            }
            return value;
        }

        public String[] getParameterValues(String paramName) {
            String values[] = super.getParameterValues(paramName);
            if ("dangerousParamName".equals(paramName)) {
                for (int index = 0; index < values.length; index++) {
                    values[index] = sanitize(values[index]);
                }
            }
            return values;
        }
    }

    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        chain.doFilter(new FilteredRequest(request), response);
    }

    public void destroy() {
    }

    public void init(FilterConfig filterConfig) {
    }
}

напишите простой класс, который подклассы HttpServletRequestWrapper с помощью метода getParameter (), который возвращает очищенную версию входных данных. Затем передайте экземпляр вашего HttpServletRequestWrapper до Filter.doChain() вместо объекта запроса.

попробовать request.setAttribute("param",value);. Это сработало отлично для меня.

пожалуйста, найдите этот пример кода:

private void sanitizePrice(ServletRequest request){
        if(request.getParameterValues ("price") !=  null){
            String price[] = request.getParameterValues ("price");

            for(int i=0;i<price.length;i++){
                price[i] = price[i].replaceAll("[^\dA-Za-z0-9- ]", "").trim();
                System.out.println(price[i]);
            }
            request.setAttribute("price", price);
            //request.getParameter("numOfBooks").re
        }
    }

можно использовать Регулярные Выражения для обеззараживания. Внутренний фильтр перед вызовом цепи.doFilter(запрос, ответ) метод, назвать этот код. Вот пример кода:

for (Enumeration en = request.getParameterNames(); en.hasMoreElements(); ) {
String name = (String)en.nextElement();
String values[] = request.getParameterValues(name);
int n = values.length;
    for(int i=0; i < n; i++) {
     values[i] = values[i].replaceAll("[^\dA-Za-z ]","").replaceAll("\s+","+").trim();   
    }
}

у меня была та же проблема (изменение параметра из HTTP-запроса в фильтре). Я в конечном итоге с помощью ThreadLocal<String>. В Filter Я:

class MyFilter extends Filter {
    public static final ThreadLocal<String> THREAD_VARIABLE = new ThreadLocal<>();
    public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) {
        THREAD_VARIABLE.set("myVariableValue");
        chain.doFilter(request, response);
    }
}

в моем процессоре запросов (HttpServlet, контроллер JSF или любой другой процессор HTTP-запросов), я получаю текущее значение потока обратно:

...
String myVariable = MyFilter.THREAD_VARIABLE.get();
...

плюсы:

  • более универсален, чем передача параметров HTTP (вы можете передавать объекты POJO)
  • немного быстрее (не нужно парсить URL для извлечения значения переменной)
  • более элегантный, чем HttpServletRequestWrapper шаблон
  • область переменных шире, чем просто HTTP-запрос (область, которую вы имеете при выполнении request.setAttribute(String,Object), т. е. вы можете получить доступ к переменной в других фильтрах.

недостатки:

  • вы можете использовать этот метод только тогда, когда поток, который обрабатывает фильтр, совпадает с тем, который обрабатывает HTTP-запрос (это имеет место во всех Java-системах серверы я знаю). Следовательно, это не будет работать, когда
    • выполнение перенаправления HTTP (потому что браузер делает новый HTTP-запрос и нет никакого способа гарантировать, что он будет обработан тем же потоком)
    • обработка данных в отдельных потоках, например, при использовании java.util.stream.Stream.parallel,java.util.concurrent.Future,java.lang.Thread.
  • вы должны быть в состоянии изменить запрос процессор / приложение

некоторые примечания:

  • сервер имеет пул потоков для обработки запросов HTTP. Так как это пул:

    1. поток из этого пула потоков будет обрабатывать много HTTP-запросов, но только один за раз (поэтому вам нужно либо очистить переменную после использования, либо определить ее для каждого HTTP-запроса = обратите внимание на такой код, как if (value!=null) { THREAD_VARIABLE.set(value);} потому что вы будете повторно использовать значение из предыдущего HTTP запрос когда value равно нулю: побочные эффекты гарантированы).
    2. нет никакой гарантии, что два запроса будут обработаны одним и тем же потоком (это может быть так, но у вас нет гарантии). Если вам нужно сохранить пользовательские данные от одного запроса к другому, было бы лучше использовать HttpSession.setAttribute()
  • в нас @RequestScoped внутренне использует ThreadLocal, но через ThreadLocal более универсален: вы можете использовать его в Контейнеры JEE / CDI (например, в многопоточных приложениях JRE)