首页 文章

使用servlet过滤器修改请求参数

提问于
浏览
100

现有的Web应用程序正在Tomcat 4.1上运行 . 页面存在XSS问题,但我无法修改源代码 . 我决定编写一个servlet过滤器来在页面看到之前清理参数 .

我想写一个像这样的Filter类:

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 回答

  • 0

    编写一个简单的类,使用getParameter()方法对 HttpServletRequestWrapper 进行子类化,该方法返回输入的已清理版本 . 然后直接将 HttpServletRequestWrapper 的实例传递给 Filter.doChain() 而不是请求对象 .

  • 0

    为了记录,这是我最后写的课程:

    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) {
        }
    }
    
  • 10

    正如您所注意到的 HttpServletRequest 没有setParameter方法 . 这是故意的,因为类表示来自客户端的请求,并且修改参数不代表该请求 .

    一种解决方案是使用 HttpServletRequestWrapper 类,它允许您将一个请求与另一个请求包装起来 . 您可以对其进行子类化,并覆盖 getParameter 方法以返回已清理的值 . 然后,您可以将该包装请求传递给 chain.doFilter 而不是原始请求 .

    它是's a bit ugly, but that' s servlet API说你应该做什么 . 如果您尝试将任何其他内容传递给 doFilter ,则某些servlet容器会抱怨您违反了规范,并拒绝处理它 .

    更优雅的解决方案是更多工作 - 修改处理参数的原始servlet / JSP,以便它需要请求属性而不是参数 . 过滤器检查参数,对其进行清理,并使用已清理的值设置属性(使用 request.setAttribute ) . 没有子类化,没有欺骗,但确实需要您修改应用程序的其他部分 .

  • 0

    试试 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
            }
        }
    
  • 115

    您可以使用 Regular Expression 进行清理 . 在调用 chain.doFilter(request, response) 方法之前的内部过滤器中,调用此代码 . 这是示例代码:

    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();   
        }
    }
    
  • 67

    我遇到了同样的问题(从Filter中的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的服务器都是这种情况) . 因此,这 will not work when

    • doing a HTTP redirect (因为浏览器执行了新的HTTP请求,并且无法保证它将由同一个线程处理)

    • processing data in separate threads ,例如使用 java.util.stream.Stream.paralleljava.util.concurrent.Futurejava.lang.Thread 时 .

    • 您必须能够修改请求处理器/应用程序

    一些旁注:

    • 服务器有一个线程池来处理HTTP请求 . 由于这是游泳池:

    • a来自此线程池的线程将处理许多HTTP请求,但一次只能处理一个(因此您需要在使用后清理变量或为每个HTTP请求定义它)=注意代码如 if (value!=null) { THREAD_VARIABLE.set(value);} 因为您将重用 value 为空时前一个HTTP请求的值:保证副作用) .

    • 无法保证同一个线程将处理两个请求(可能是这种情况,但您无法保证) . 如果您需要将用户数据从一个请求保留到另一个请求,那么最好使用HttpSession.setAttribute()

    • JEE @RequestScoped内部使用 ThreadLocal ,但使用 ThreadLocal 更通用:您可以在非JEE / CDI容器中使用它(例如在多线程JRE应用程序中)

相关问题