Modificar parámetro de solicitud con filtro servlet

Se está ejecutando una aplicación web existente en Tomcat 4.1. Hay un problema de XSS con una página, pero no puedo modificar la fuente. Decidí escribir un filtro de servlet para desinfectar el parámetro antes de que lo vea la página.

Me gustaría escribir una clase de filtro como esta:

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

Pero ServletRequest.setParameter no existe.

¿Cómo puedo cambiar el valor del parámetro de solicitud antes de pasar la solicitud por la cadena?

Como ha notado, HttpServletRequest no tiene un método setParameter. Esto es deliberado, ya que la clase representa la solicitud tal como vino del cliente, y la modificación del parámetro no representaría eso.

Una solución es utilizar la clase HttpServletRequestWrapper , que le permite ajustar una solicitud con otra. Puede subclasificar eso y anular el método getParameter para devolver su valor sanitizado. A continuación, puede pasar la solicitud envuelta a chain.doFilter lugar de la solicitud original.

Es un poco feo, pero eso es lo que la API servlet dice que debes hacer. Si intentas pasar algo más a doFilter , algunos contenedores servlet se quejarán de que has violado la especificación y se rehusarán a manejarlo.

Una solución más elegante es más trabajo: modifique el servlet / JSP original que procesa el parámetro, de modo que espere un atributo de solicitud en lugar de un parámetro. El filtro examina el parámetro, lo desinfecta y establece el atributo (utilizando request.setAttribute ) con el valor sanitizado. Sin subclases ni falsificaciones, pero sí requiere que modifique otras partes de su aplicación.

Para el registro, aquí está la clase que terminé escribiendo:

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

Escriba una clase simple que subcalisa HttpServletRequestWrapper con un método getParameter () que devuelve la versión desinfectada de la entrada. Luego pase una instancia de su HttpServletRequestWrapper a Filter.doChain() lugar del objeto de solicitud directamente.

Pruebe request.setAttribute("param",value); . Funcionó bien para mí

Por favor encuentre esta muestra de código:

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

Puede usar Expresión regular para desinfección. Dentro del filtro antes de llamar al método chain.doFilter (solicitud, respuesta) , llame a este código. Aquí está el código de muestra:

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

Tuve el mismo problema (cambiar un parámetro de la solicitud HTTP en el filtro). Terminé usando un ThreadLocal . En el Filter tengo:

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

En mi procesador de solicitudes ( HttpServlet , controlador JSF o cualquier otro procesador de solicitudes HTTP), HttpServlet el valor del hilo actual:

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

Ventajas:

  • más versátil que pasar parámetros HTTP (puede pasar objetos POJO)
  • ligeramente más rápido (no es necesario analizar la URL para extraer el valor de la variable)
  • más elegante que el HttpServletRequestWrapper
  • el scope de la variable es más amplio que solo la solicitud HTTP (el scope que tiene al hacer request.setAttribute(String,Object) , es decir, puede acceder a la variable en otras filtrers.

Desventajas:

  • Puede utilizar este método solo cuando el hilo que procesa el filtro sea el mismo que procesa la solicitud HTTP (este es el caso en todos los servidores basados ​​en Java que conozco). En consecuencia, esto no funcionará cuando
    • haciendo un redireccionamiento HTTP (porque el navegador hace una nueva solicitud HTTP y no hay forma de garantizar que será procesada por el mismo hilo)
    • procesar datos en hilos separados , por ejemplo, al usar java.util.stream.Stream.parallel , java.util.concurrent.Future , java.lang.Thread .
  • Debe poder modificar el procesador / aplicación de solicitud

Algunas notas secundarias:

  • El servidor tiene un grupo de subprocesos para procesar las solicitudes HTTP. Como esto es un grupo:

    1. un subproceso de este grupo de subprocesos procesará muchas solicitudes HTTP, pero solo una a la vez (por lo que necesita limpiar su variable después del uso o definirla para cada solicitud HTTP = prestar atención al código como if (value!=null) { THREAD_VARIABLE.set(value);} porque reutilizará el valor de la solicitud HTTP anterior cuando el value sea ​​nulo: los efectos secundarios están garantizados).
    2. No hay garantía de que dos solicitudes serán procesadas por el mismo hilo (puede ser el caso pero no tiene garantía). Si necesita mantener los datos del usuario de una solicitud a otra solicitud, sería mejor usar HttpSession.setAttribute()
  • El JEE @RequestScoped utiliza internamente un ThreadLocal , pero el uso de ThreadLocal es más versátil: puede usarlo en contenedores que no sean JEE / CDI (por ejemplo, en aplicaciones JRE multiproceso)
Intereting Posts