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