2013-07-11 8 views
8

Un fornitore ha pubblicato dati XML su HTTPS all'interno di una variabile modulo denominata XMLContent sul mio server applicazioni Coldfusion. Recentemente mi sono spostato a una versione più recente del server delle applicazioni e quelle richieste hanno generato 500 errori del server. Viene generato l'errore perché il contenuto di un secondo parametro di forma non è urlencoded correttamente, ma non ho comunque bisogno di tale parametro. (Ho contattato il fornitore per risolvere il problema, ma sono costretto a correggerlo, se possibile.)Utilizzare il filtro servlet per rimuovere un parametro del modulo dai dati registrati

Come utilizzare un filtro servlet per rimuovere tutto tranne il parametro del modulo named: XMLContent Ho provato vari tentativi per rimuovere esplicitamente il parametro offensivo "TContent" ma non viene mai rimosso.

Un frammento di dati che vengono ricevuti:

XMLContent=%3C%3Fxml+version%3D%221.0%22+encoding%3D%22UTF-8%22%3F%3E%0A%3CCheck+xmlns%3D%22http .........&TContent=<!--?xml version="1.0" encoding="UTF-8"?--><check xmlns="http........... 

Il codice che ho provato:

import java.io.IOException; 
import java.util.Collections; 
import java.util.Enumeration; 
import java.util.HashMap; 
import java.util.List; 
import java.util.Map; 

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; 


import java.util.*; 



public class MultipartFilter implements Filter { 

// Init ---------------------------------------------------------------- 

    public FilterConfig filterConfig; 

// Actions ------------------------------------------------------------- 


public void init(FilterConfig filterConfig) throws ServletException { 
    this.filterConfig = filterConfig; 
} 

/** 
* Check the type request and if it is a HttpServletRequest, then parse the request. 
* @throws ServletException If parsing of the given HttpServletRequest fails. 
* @see javax.servlet.Filter#doFilter(
*  javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) 
*/ 
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
    throws ServletException, IOException 
{ 
    // Check type request. 
    if (request instanceof HttpServletRequest) { 
     // Cast back to HttpServletRequest. 
     HttpServletRequest httpRequest = (HttpServletRequest) request; 

     // Parse HttpServletRequest. 
     HttpServletRequest parsedRequest = parseRequest(httpRequest); 

     // Continue with filter chain. 
     chain.doFilter(parsedRequest, response); 
    } else { 
     // Not a HttpServletRequest. 
     chain.doFilter(request, response); 
    } 
} 

/** 
* @see javax.servlet.Filter#destroy() 
*/ 
public void destroy() { 
    this.filterConfig = null; 
} 



private HttpServletRequest parseRequest(HttpServletRequest request) throws ServletException { 

    // Prepare the request parameter map. 
    Map<String, String[]> parameterMap = new HashMap<String, String[]>(); 

    // Loop through form parameters. 
Enumeration<String> parameterNames = request.getParameterNames(); 

    while (parameterNames.hasMoreElements()) { 
    String paramName = parameterNames.nextElement(); 
    String[] paramValues = request.getParameterValues(paramName); 

      // Add just the XMLContent form parameter 
    if (paramName.equalsIgnoreCase("xmlcontent")) { 

     parameterMap.put(paramName, new String[] { paramValues[0] }); 

    } 
} 

    // Wrap the request with the parameter map which we just created and return it. 
    return wrapRequest(request, parameterMap); 
} 



// Utility (may be refactored to public utility class) --------------- 

/** 
* Wrap the given HttpServletRequest with the given parameterMap. 
* @param request The HttpServletRequest of which the given parameterMap have to be wrapped in. 
* @param parameterMap The parameterMap to be wrapped in the given HttpServletRequest. 
* @return The HttpServletRequest with the parameterMap wrapped in. 
*/ 
private static HttpServletRequest wrapRequest(
    HttpServletRequest request, final Map<String, String[]> parameterMap) 
{ 
    return new HttpServletRequestWrapper(request) { 
     public Map<String, String[]> getParameterMap() { 
      return parameterMap; 
     } 
     public String[] getParameterValues(String name) { 
      return parameterMap.get(name); 
     } 
     public String getParameter(String name) { 
      String[] params = getParameterValues(name); 
      return params != null && params.length > 0 ? params[0] : null; 
     } 
     public Enumeration<String> getParameterNames() { 
      return Collections.enumeration(parameterMap.keySet()); 
     } 
    }; 
    } 
} 
+0

Ma non è tutto chiaro per me. È possibile modificare il prodotto di terze parti e aggiungervi un filtro Servlet? Se sì, allora non sembra complicato con un filtro. Hai già provato qualcosa? – LaurentG

+0

Sì, è possibile aggiungere un filtro servlet alla mia estremità, il fornitore mi sta inviando i dati ed è il mio server applicazioni Coldfusion a generare l'errore di stato 500. Ho provato con alcuni filtri diversi per farlo funzionare ma invano, invia sempre il secondo parametro del modulo. – Matt

+0

Non sono sicuro, se possibile con un filtro. Tuttavia è più che possibile con Apache HTTP Server: http://stackoverflow.com/questions/6622018/urlrewritefilter-remove-parameters o http://wiki.apache.org/httpd/RewriteQueryString –

risposta

6

Siamo di fronte queste situazioni ogni giorno durante la gestione locale. Se le impostazioni locali dell'utente sono diverse da quelle del browser, dobbiamo aggiornare la richiesta. Ma non è mutabile.

Soluzione: creare una classe wrapper richiesta. Copia parametri della richiesta già esistenti (quelli che si desidera) in esso e passare questa classe wrapper in filterchain.doFilter()

campione classe wrapper:

public class WrappedRequest extends HttpServletRequestWrapper 
{ 
    Map<String, String[]> parameterMap; 
    public WrappedRequest(final HttpServletRequest request) 
    { 
    //build your param Map here with required values 
    } 

    @Override 
    public Map<String, String[]> getParameterMap() 
    { 
    //return local param map 
    } 

    //override other methods if needed. 

} 

Ora nel codice del filtro, fare in seguito.

wrapRequest = new WrappedRequest(hreq); 
filterChain.doFilter(wrapRequest, servletResponse); 

Speriamo che risolva il problema.

5

approccio

Il codice segue l'approccio corretto:

  • in wrapRequest(), esso crea un'istanza HttpServletRequestWrapper e ignora i 4 metodi che scatenano richiesta analisi:

    • public String getParameter(String name)
    • public Map<String, String[]> getParameterMap()
    • public Enumeration<String> getParameterNames()
    • public String[] getParameterValues(String name)
  • metodo doFilter() invoca la catena del filtro mediante la richiesta avvolto, cioè filtri successivi, più il servlet di destinazione (URL mappato) sarà fornito richiesta avvolto.

Problemi

  • Calling uno dei 4 metodi getParameterXXX() metodi sul sottostante 'richiesta originale' innesca parsing implicito tutti parametri di richiesta (tramite un metodo interno server, ad esempio Request.parseRequestParameters or parsePameters). Questi 4 metodi sono l'unico modo per attivare tale analisi.
  • prima che la richiesta è avvolto, in parseRequest(), il codice chiama tali metodi su richiesta sottostante:

    request.getParameterNames(); 
    
    request.getParameterValues(paramName); 
    

Soluzione

È necessario controllare la logica di analisi. Leggere i byte grezzi dal flusso di input e fare la propria decodifica URL è troppo complesso, significherebbe sostituire un grande volume di codice del server strettamente accoppiato. Invece, l'approccio più semplice è quello di sostituire semplicemente il metodo che esegue la decodifica URL effettiva. Ciò significa che puoi lasciare attive le chiamate request.getParameterXXX menzionate nella sezione precedente.

Non so su quale server si sta ospitando ColdFusion, ma la descrizione che segue è basata su Glassfish/Tomcat, ma può essere adattata. In fondo a questo post c'è il metodo di analisi della richiesta interna di Glassfish (una versione modificata di Tomcat). JD-decompiler è utile per questo armeggiare per convertire i file .class in .java.

  1. trovare la classe che fa la decodifica URL (per Glassfish questo è com.sun.grizzly.util.http.Parameters da grizzly-utils.jar come illustrato di seguito, per Tomcat questo è org.apache.tomcat.util.http.Parameters da tomcat-coyote.jar)
  2. Copia l'intero codice sorgente di una nuova classe somepackage.StrippedParameters
  3. Trova la riga di codice che decodifica il valore del parametro (in basso: value = urlDecode(this.tmpValue))
  4. Modificare questo codice in modo che decodifichi solo quando il nome del parametro corrisponde al parametro desiderato:

    if (decodeName) 
        name = urlDecode(this.tmpName); 
    else 
        name = this.tmpName.toString(); 
    // 
    // !! THIS IF STATEMENT ADDED TO ONLY DECODE DESIRED PARAMETERS, 
    // !! OTHERS ARE STRIPPED: 
    // 
    if ("XMLContent".equals(name)) { 
        String value; 
        String value; 
        if (decodeValue) 
         value = urlDecode(this.tmpValue); 
        else { 
         value = this.tmpValue.toString(); 
        } 
        try 
        { 
         addParameter(name, value); 
        } 
        catch (IllegalStateException ise) 
        { 
         logger.warning(ise.getMessage()); 
         break; 
        } 
    } 
    
  5. Ora la parte difficile: sostituire la classe predefinita Parameters con la classe StrippedParmeters appena prima della decodifica dell'URL. Una volta ottenuti i parametri, copiarli nuovamente nella classe del contenitore.Per Glassfish copiare i parseRequestParameters metodo nella sua implementazione HttpServletRequestWrapper (per Tomcat, il metodo corrispondente è parseParameters in classe org.apache.catalina.connector.Request in catalina.jar)

    • sostituire questa linea:

      Parameters parameters = this.coyoteRequest.getParameters(); 
      

      con:

      Parameters parameters = new somepackage.StrippedParameters(); 
      
    • nella parte inferiore del metodo, aggiungere:

      Parameters coyoteParameters = this.coyoteRequest.getParameters(); 
      for (String paramName : parameters.getParameterNames()) { 
          String paramValue = parameters.getParameterValue(paramName); 
          coyoteParameters.addParameter(paramName, paramValue); 
      } 
      

Glassfish Container Code - Modificata versione di Tomcat, con Grizzly sostituisce Coyote (Catalina Servlet Engine faccia ancora riferimento a oggetti Coyote, ma sono casi Grizzly deriso come Coyote)

package org.apache.catalina.connector; 
.... 
public class Request implements HttpRequest, HttpServletRequest { 
    .... 
    protected com.sun.grizzly.tcp.Request coyoteRequest; 
    ....  
    // This is called from the 4 methods named 'getParameterXXX' 
    protected void parseRequestParameters() { 
     Parameters parameters = this.coyoteRequest.getParameters(); 
     parameters.setLimit(getConnector().getMaxParameterCount()); 
     String enc = getCharacterEncoding(); 
     this.requestParametersParsed = true; 
     if (enc != null) { 
      parameters.setEncoding(enc); 
      parameters.setQueryStringEncoding(enc); 
     } else { 
      parameters.setEncoding("ISO-8859-1"); 
      parameters.setQueryStringEncoding("ISO-8859-1"); 
     } 
     parameters.handleQueryParameters(); 
     if ((this.usingInputStream) || (this.usingReader)) { 
      return; 
     } 
     if (!getMethod().equalsIgnoreCase("POST")) { 
      return; 
     } 
     String contentType = getContentType(); 
     if (contentType == null) { 
      contentType = ""; 
     } 
     int semicolon = contentType.indexOf(';'); 
     if (semicolon >= 0) 
      contentType = contentType.substring(0, semicolon).trim(); 
     else { 
      contentType = contentType.trim(); 
     } 
     if ((isMultipartConfigured()) && ("multipart/form-data".equals(contentType))) { 
      getMultipart().init(); 
     } 
     if (!"application/x-www-form-urlencoded".equals(contentType)) { 
      return; 
     } 
     int len = getContentLength(); 
     if (len > 0) { 
     int maxPostSize = ((Connector)this.connector).getMaxPostSize(); 
     if ((maxPostSize > 0) && (len > maxPostSize)) { 
      log(sm.getString("coyoteRequest.postTooLarge")); 
      throw new IllegalStateException("Post too large"); 
     } 
     try { 
      byte[] formData = getPostBody(); 
      if (formData != null) 
       parameters.processParameters(formData, 0, len); 
     } catch (Throwable t) { 
     } 
    } 
    } 
} 

package com.sun.grizzly.tcp; 

import com.sun.grizzly.util.http.Parameters; 
public class Request { 
    .... 
    private Parameters parameters = new Parameters(); 
    .... 
    public Parameters getParameters() { 
     return this.parameters; 
    } 
} 

package com.sun.grizzly.util.http; 

public class Parameters { 
    .... 
    public void processParameters(byte[] bytes, int start, int len) { 
     processParameters(bytes, start, len, getCharset(this.encoding)); 
    } 

    public void processParameters(byte[] bytes, int start, int len, Charset charset) { 
     if (debug > 0) { 
     try { 
      log(sm.getString("parameters.bytes", new String(bytes, start, len, "ISO-8859-1"))); 
     } catch (UnsupportedEncodingException e) { 
      logger.log(Level.SEVERE, sm.getString("parameters.convertBytesFail"), e); 
     } 
    } 
    int decodeFailCount = 0; 
    int end = start + len; 
    int pos = start; 
    while (pos < end) { 
     int nameStart = pos; 
     int nameEnd = -1; 
     int valueStart = -1; 
     int valueEnd = -1; 
     boolean parsingName = true; 
     boolean decodeName = false; 
     boolean decodeValue = false; 
     boolean parameterComplete = false; 
     do { 
      switch (bytes[pos]) { 
       case 61: 
        if (parsingName) { 
         nameEnd = pos; 
         parsingName = false; 
         pos++; valueStart = pos; 
        } else { 
         pos++; 
        } 
        break; 
       case 38: 
        if (parsingName) { 
         nameEnd = pos; 
        } else { 
         valueEnd = pos; 
        } 
        parameterComplete = true; 
        pos++; 
        break; 
       case 37: 
       case 43: 
        if (parsingName) 
         decodeName = true; 
        else { 
         decodeValue = true; 
        } 
        pos++; 
        break; 
       default: 
        pos++; 
      } 
     } while ((!parameterComplete) && (pos < end)); 
     if (pos == end) { 
      if (nameEnd == -1) 
       nameEnd = pos; 
      else if ((valueStart > -1) && (valueEnd == -1)) { 
       valueEnd = pos; 
      } 
     } 
     if ((debug > 0) && (valueStart == -1)) { 
      try { 
       log(sm.getString("parameters.noequal", Integer.valueOf(nameStart), 
       Integer.valueOf(nameEnd), 
       new String(bytes, nameStart, nameEnd - nameStart, "ISO-8859-1"))); 
      } catch (UnsupportedEncodingException e) { 
       logger.log(Level.SEVERE, sm.getString("parameters.convertBytesFail"), e); 
      } 
     } 

     if (nameEnd <= nameStart) { 
      if (logger.isLoggable(Level.INFO)) { 
       if (valueEnd >= nameStart) 
        try { 
         new String(bytes, nameStart, valueEnd - nameStart, "ISO-8859-1"); 
        } catch (UnsupportedEncodingException e) { 
         logger.log(Level.SEVERE, 
            sm.getString("parameters.convertBytesFail"), e); 
        } else { 
         logger.fine(sm.getString("parameters.invalidChunk", 
           Integer.valueOf(nameStart), Integer.valueOf(nameEnd), null)); 
        } 
       } 
      } else { 
       this.tmpName.setCharset(charset); 
       this.tmpValue.setCharset(charset); 
       this.tmpName.setBytes(bytes, nameStart, nameEnd - nameStart); 
       this.tmpValue.setBytes(bytes, valueStart, valueEnd - valueStart); 
    if (debug > 0) 
     try { 
     this.origName.append(bytes, nameStart, nameEnd - nameStart); 
     this.origValue.append(bytes, valueStart, valueEnd - valueStart); 
     } 
     catch (IOException ioe) { 
     logger.log(Level.SEVERE, sm.getString("parameters.copyFail"), ioe); 
     } 
    try 
    { 
     String name; 
     String name; 
     if (decodeName) 
     name = urlDecode(this.tmpName); 
     else 
     name = this.tmpName.toString(); 
     String value; 
     String value; 
     if (decodeValue) 
     value = urlDecode(this.tmpValue); 
     else { 
     value = this.tmpValue.toString(); 
     } 
     try 
     { 
     addParameter(name, value); 
     } 
     catch (IllegalStateException ise) 
     { 
     logger.warning(ise.getMessage()); 
     break; 
     } 
    } catch (IOException e) { 
     decodeFailCount++; 
     if ((decodeFailCount == 1) || (debug > 0)) { 
     if (debug > 0) { 
      log(sm.getString("parameters.decodeFail.debug", this.origName.toString(), this.origValue.toString()), e); 
     } 
     else if (logger.isLoggable(Level.INFO)) { 
      logger.log(Level.INFO, sm.getString("parameters.decodeFail.info", this.tmpName.toString(), this.tmpValue.toString()), e); 
     } 
     } 

    } 

    this.tmpName.recycle(); 
    this.tmpValue.recycle(); 

    if (debug > 0) { 
     this.origName.recycle(); 
     this.origValue.recycle(); 
    } 
    } 
} 
if ((decodeFailCount > 1) && (debug <= 0)) 
    logger.info(sm.getString("parameters.multipleDecodingFail", Integer.valueOf(decodeFailCount))); 

}

Problemi correlati