Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/354.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/json/15.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 使用servlet过滤器修改请求参数_Java_Servlet Filters - Fatal编程技术网

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

Java 使用servlet过滤器修改请求参数,java,servlet-filters,Java,Servlet Filters,现有的web应用程序正在Tomcat4.1上运行。页面存在XSS问题,但我无法修改源代码。我决定编写一个servlet过滤器,在页面看到参数之前对其进行清理 我想写一个如下的过滤器类: import java.io.*; import javax.servlet.*; public final class XssFilter implements Filter { public void doFilter(ServletRequest request, ServletResponse r

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

我想写一个如下的过滤器类:

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
不存在


在将请求向下传递到链之前,如何更改请求参数的值?

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

正如您所指出的
HttpServletRequest
没有setParameter方法。这是经过深思熟虑的,因为类表示来自客户端的请求,而修改参数并不表示这一点

一种解决方案是使用
HttpServletRequestWrapper
类,它允许您用一个请求包装另一个请求。您可以将其子类化,并重写
getParameter
方法以返回经过清理的值。然后,您可以将包装好的请求传递给
chain.doFilter
,而不是原始请求

这有点难看,但ServletAPI说您应该这样做。如果您试图将任何其他内容传递给
doFilter
,一些servlet容器会抱怨您违反了规范,并拒绝处理它


更优雅的解决方案是更多的工作—修改处理参数的原始servlet/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) {
    }
}
import java.io.IOException;
导入javax.servlet.Filter;
导入javax.servlet.FilterChain;
导入javax.servlet.FilterConfig;
导入javax.servlet.ServletException;
导入javax.servlet.ServletRequest;
导入javax.servlet.ServletResponse;
导入javax.servlet.http.HttpServletRequest;
导入javax.servlet.http.HttpServletRequestWrapper;
公共最终类XssFilter实现了过滤器{
静态类FiltereRequest扩展了HttpServletRequestWrapper{
/*这些是Javascript验证所允许的字符*/
静态字符串allowedChars=“+-0123456789#*”;
公共筛选器请求(ServletRequest请求){
超级((HttpServletRequest)请求);
}
公共字符串清理(字符串输入){
字符串结果=”;
对于(int i=0;i=0){
结果+=输入字符(i);
}
}
返回结果;
}
公共字符串getParameter(字符串参数名){
字符串值=super.getParameter(paramName);
如果(“危险的参数名”。等于(参数名)){
值=消毒(值);
}
返回值;
}
公共字符串[]getParameterValues(字符串paramName){
字符串值[]=super.getParameterValues(paramName);
如果(“危险的参数名”。等于(参数名)){
对于(int index=0;index
尝试
请求.setAttribute(“参数”,值)。这对我很管用

请查找此代码示例:

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
        }
    }
private void sanitizePrice(ServletRequest){
if(request.getParameterValues(“price”)!=null){
字符串价格[]=request.getParameterValues(“价格”);

对于(int i=0;i您可以使用正则表达式进行清理。在调用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();   
    }
}
for(枚举en=request.getParameterNames();en.hasMoreElements();){
字符串名称=(字符串)en.nextElement();
字符串值[]=request.getParameterValues(名称);
int n=值。长度;
对于(int i=0;i
我也遇到了同样的问题(在筛选器中更改HTTP请求的参数)。我最终使用了
线程本地
。在
筛选器中,我有:

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);
    }
}
优点:

  • 比传递HTTP参数更通用(您可以传递POJO对象)
  • 稍微快一点(无需解析URL来提取变量值)
  • HttpServletRequestWrapper
    样板更优雅
  • 变量范围比HTTP请求(执行
    request.setAttribute(String,Object)
    时的范围)更宽,也就是说,您可以在其他筛选器中访问变量
缺点:

  • 只有当处理筛选器的线程与处理HTTP请求的线程相同时(我所知道的所有基于Java的服务器都是这种情况),才可以使用此方法。因此,当
    • 执行HTTP重定向(因为浏览器执行新的HTTP请求,并且无法保证sam将处理该请求
      ...
      String myVariable = MyFilter.THREAD_VARIABLE.get();
      ...
      
      //import ../../Constants;
      
      public class RequestFilter implements Filter {
      
          private static final Logger logger = LoggerFactory.getLogger(RequestFilter.class);
      
          @Override
          public void init(FilterConfig filterConfig) throws ServletException {
          }
      
          @Override
          public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
              throws IOException, ServletException {
              try {
                  CustomHttpServletRequest customHttpServletRequest = new CustomHttpServletRequest((HttpServletRequest) servletRequest);
                  filterChain.doFilter(customHttpServletRequest, servletResponse);
              } finally {
                  //do something here
              }
          }
      
      
      
          @Override
          public void destroy() {
      
          }
      
           public static Map<String, String[]> ADMIN_QUERY_PARAMS = new HashMap<String, String[]>() {
              {
                  put("diagnostics", new String[]{"false"});
                  put("skipCache", new String[]{"false"});
              }
          };
      
          /*
              This is a custom wrapper over the `HttpServletRequestWrapper` which 
              overrides the various header getter methods and query param getter methods.
              Changes to the request pojo are
              => A custom header is added whose value is a unique id
              => Admin query params are set to default values in the url
          */
          private class CustomHttpServletRequest extends HttpServletRequestWrapper {
              public CustomHttpServletRequest(HttpServletRequest request) {
                  super(request);
                  //create custom id (to be returned) when the value for a
                  //particular header is asked for
                  internalRequestId = RandomStringUtils.random(10, true, true) + "-local";
              }
      
              public String getHeader(String name) {
                  String value = super.getHeader(name);
                  if(Strings.isNullOrEmpty(value) && isRequestIdHeaderName(name)) {
                      value = internalRequestId;
                  }
                  return value;
              }
      
              private boolean isRequestIdHeaderName(String name) {
                  return Constants.RID_HEADER.equalsIgnoreCase(name) || Constants.X_REQUEST_ID_HEADER.equalsIgnoreCase(name);
              }
      
              public Enumeration<String> getHeaders(String name) {
                  List<String> values = Collections.list(super.getHeaders(name));
                  if(values.size()==0 && isRequestIdHeaderName(name)) {
                      values.add(internalRequestId);
                  }
                  return Collections.enumeration(values);
              }
      
              public Enumeration<String> getHeaderNames() {
                  List<String> names = Collections.list(super.getHeaderNames());
                  names.add(Constants.RID_HEADER);
                  names.add(Constants.X_REQUEST_ID_HEADER);
                  return Collections.enumeration(names);
              }
      
              public String getParameter(String name) {
                  if (ADMIN_QUERY_PARAMS.get(name) != null) {
                      return ADMIN_QUERY_PARAMS.get(name)[0];
                  }
                  return super.getParameter(name);
              }
      
              public Map<String, String[]> getParameterMap() {
                  Map<String, String[]> paramsMap = new HashMap<>(super.getParameterMap());
                  for (String paramName : ADMIN_QUERY_PARAMS.keySet()) {
                      if (paramsMap.get(paramName) != null) {
                          paramsMap.put(paramName, ADMIN_QUERY_PARAMS.get(paramName));
                      }
                  }
                  return paramsMap;
              }
      
              public String[] getParameterValues(String name) {
                  if (ADMIN_QUERY_PARAMS.get(name) != null) {
                      return ADMIN_QUERY_PARAMS.get(name);
                  }
                  return super.getParameterValues(name);
              }
      
              public String getQueryString() {
                  Map<String, String[]> map = getParameterMap();
                  StringBuilder builder = new StringBuilder();
                  for (String param: map.keySet()) {
                      for (String value: map.get(param)) {
                          builder.append(param).append("=").append(value).append("&");
                      }
                  }
                  builder.deleteCharAt(builder.length() - 1);
                  return builder.toString();
              }
          }
      }
      
       private final class CustomHttpServletRequest extends HttpServletRequestWrapper {
      
          private final Map<String, String[]> queryParameterMap;
          private final Charset requestEncoding;
      
          public CustomHttpServletRequest(HttpServletRequest request) {
              super(request);
              queryParameterMap = getCommonQueryParamFromLegacy(request.getParameterMap());
      
              String encoding = request.getCharacterEncoding();
              requestEncoding = (encoding != null ? Charset.forName(encoding) : StandardCharsets.UTF_8);
          }
      
          private final Map<String, String[]> getCommonQueryParamFromLegacy(Map<String, String[]> paramMap) {
              Objects.requireNonNull(paramMap);
      
              Map<String, String[]> commonQueryParamMap = new LinkedHashMap<>(paramMap);
      
              commonQueryParamMap.put(CommonQueryParams.PATIENT_ID, new String[] { paramMap.get(LEGACY_PARAM_PATIENT_ID)[0] });
              commonQueryParamMap.put(CommonQueryParams.PATIENT_BIRTHDATE, new String[] { paramMap.get(LEGACY_PARAM_PATIENT_BIRTHDATE)[0] });
              commonQueryParamMap.put(CommonQueryParams.KEYWORDS, new String[] { paramMap.get(LEGACY_PARAM_STUDYTYPE)[0] });
      
              String lowerDateTime = null;
              String upperDateTime = null;
      
              try {
                  String studyDateTime = new SimpleDateFormat("yyyy-MM-dd").format(new SimpleDateFormat("dd-MM-yyyy").parse(paramMap.get(LEGACY_PARAM_STUDY_DATE_TIME)[0]));
      
                  lowerDateTime = studyDateTime + "T23:59:59";
                  upperDateTime = studyDateTime + "T00:00:00";
      
              } catch (ParseException e) {
                  LOGGER.error("Can't parse StudyDate from query parameters : {}", e.getLocalizedMessage());
              }
      
              commonQueryParamMap.put(CommonQueryParams.LOWER_DATETIME, new String[] { lowerDateTime });
              commonQueryParamMap.put(CommonQueryParams.UPPER_DATETIME, new String[] { upperDateTime });
      
              legacyQueryParams.forEach(commonQueryParamMap::remove);
              return Collections.unmodifiableMap(commonQueryParamMap);
      
          }
      
          @Override
          public String getParameter(String name) {
              String[] params = queryParameterMap.get(name);
              return params != null ? params[0] : null;
          }
      
          @Override
          public String[] getParameterValues(String name) {
              return queryParameterMap.get(name);
          }
      
          @Override
          public Map<String, String[]> getParameterMap() {
                  return queryParameterMap; // unmodifiable to uphold the interface contract.
              }
      
              @Override
              public Enumeration<String> getParameterNames() {
                  return Collections.enumeration(queryParameterMap.keySet());
              }
      
              @Override
              public String getQueryString() {
                  // @see : https://stackoverflow.com/a/35831692/9869013
                  // return queryParameterMap.entrySet().stream().flatMap(entry -> Stream.of(entry.getValue()).map(value -> entry.getKey() + "=" + value)).collect(Collectors.joining("&")); // without encoding !!
                  return queryParameterMap.entrySet().stream().flatMap(entry -> encodeMultiParameter(entry.getKey(), entry.getValue(), requestEncoding)).collect(Collectors.joining("&"));
              }
      
              private Stream<String> encodeMultiParameter(String key, String[] values, Charset encoding) {
                  return Stream.of(values).map(value -> encodeSingleParameter(key, value, encoding));
              }
      
              private String encodeSingleParameter(String key, String value, Charset encoding) {
                  return urlEncode(key, encoding) + "=" + urlEncode(value, encoding);
              }
      
              private String urlEncode(String value, Charset encoding) {
                  try {
                      return URLEncoder.encode(value, encoding.name());
                  } catch (UnsupportedEncodingException e) {
                      throw new IllegalArgumentException("Cannot url encode " + value, e);
                  }
              }
      
              @Override
              public ServletInputStream getInputStream() throws IOException {
                  throw new UnsupportedOperationException("getInputStream() is not implemented in this " + CustomHttpServletRequest.class.getSimpleName() + " wrapper");
              }
      
          }