2

テキストボックスと送信ボタンがあるフォームがあります。サーバー側の検証をテストするために、フォームで送信された値を変更するためにburpツールを使用しています。

サーバー側の検証は%、burpツールから入力された文字がない限り、正常に機能%します。文字を入力すると、サーバーは次の例外を表示します。

2012-12-11 11:37:07,860 WARN  [org.apache.tomcat.util.http.Parameters] (ajp-0.0.0.0-8109-19) Parameters: Character decoding failed. Parameter skipped.
java.io.CharConversionException: EOF
                at org.apache.tomcat.util.buf.UDecoder.convert(UDecoder.java:83)
                at org.apache.tomcat.util.buf.UDecoder.convert(UDecoder.java:49)
                at org.apache.tomcat.util.http.Parameters.urlDecode(Parameters.java:429)
                at org.apache.tomcat.util.http.Parameters.processParameters(Parameters.java:412)
                at org.apache.tomcat.util.http.Parameters.processParameters(Parameters.java:363)
                at org.apache.catalina.connector.Request.parseParameters(Request.java:2562)
                at org.apache.catalina.connector.Request.getParameter(Request.java:1060)
                at org.apache.catalina.connector.RequestFacade.getParameter(RequestFacade.java:355)
                at org.displaytag.filter.ResponseOverrideFilter.doFilter(ResponseOverrideFilter.java:118)
                at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
                at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
                at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:235)
                at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
                at org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:190)
                at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:92)
                at org.jboss.web.tomcat.security.SecurityContextEstablishmentValve.process(SecurityContextEstablishmentValve.java:126)
                at org.jboss.web.tomcat.security.SecurityContextEstablishmentValve.invoke(SecurityContextEstablishmentValve.java:70)
                at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
                at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
                at org.jboss.web.tomcat.service.jca.CachedConnectionValve.invoke(CachedConnectionValve.java:158)
                at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
                at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:330)
                at org.apache.coyote.ajp.AjpProcessor.process(AjpProcessor.java:436)
                at org.apache.coyote.ajp.AjpProtocol$AjpConnectionHandler.process(AjpProtocol.java:384)
                at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
                at java.lang.Thread.run(Thread.java:662)

%文字でフォームを送信すると、フォームは正しく%25に変更されますが、このサーバー側をどのように処理しますか?

4

4 に答える 4

1

%発生している問題は、デコーダーがシンボルで始まるときに、有効でデコード可能なパラメーターを予期していることです。フォームを送信するとき、入力パラメーター%は正しくエンコードされ%25ます。このリクエストをBurpで編集すると、私が知る限り、このエンコードは行われません。Burpは喜んでサーバーに%シンボルを送信し、サーバー側の検証ではこれがエンコードされた値の始まりであると想定しますが、基本的にパラメーターが壊れているため、デコードに失敗します。

私の最善の推測は、%値だけを送信しないことです(つまり、数値を伴わずに)。これは、null参照でメソッドを呼び出すようなものです。いくつかのことが機能しません。パーセント記号が独自の入力検証によって承認または拒否されるかどうかを確認しようとしていると思いますが、最初にデコードを実行する限り、エンコードする必要があります。

于 2013-04-26T11:15:08.793 に答える
0

このテスト(URLエンコードされたデータであると想定される生の「%」を含む)では、アプリが不正な入力を適切に拒否することをテストしています。それは何も悪いことではありません。

げっぷを使用してデータをプッシュする場合は、データをエンコードする必要があります。つまり、「%」が必要な場所に「%25」を入力する必要があります。

于 2013-04-29T07:32:05.553 に答える
0

スタックトレースを詳しく調べると、(HTTPテキストをJava要求オブジェクトに解析する際に)関係するすべてのクラスはフレームワークからのものであり、ユーザーがそれを制御することはできません。

これの背後にある基本的な前提は次のとおりです。サーバーは、受信したデータが常にURLエンコードされていると常に想定し、したがって常にデコードを試みます。

デコードが失敗した場合、それはHTTPプロトコルの基本的な信条が守られていないことを意味し、したがってユーザーにそれを処理する機会を与えることなく要求を完全に拒否します。

したがって、私見では、サーバーがそれ自体で正しく処理しているため、この状態を処理することはできません。

編集:

上記の@Raviによって提案された解決策の1つは、問題を解決するように見えます。同じコードを試しましたが、例外をキャッチできませんでした。

深く掘り下げると、その例外(java.io.CharConversionException)がユーザー定義のコードで処理できない理由が明らかになりました。

java.io.CharConversionExceptionスローされますが、呼び出し階層で伝播されることはありません。これが理由です、

スタックトレース内

        at org.apache.tomcat.util.buf.UDecoder.convert(UDecoder.java:83)
        at org.apache.tomcat.util.buf.UDecoder.convert(UDecoder.java:49)
        at org.apache.tomcat.util.http.Parameters.urlDecode(Parameters.java:429)
        at org.apache.tomcat.util.http.Parameters.processParameters(Parameters.java:412)

クラス内のメソッドのソースを見てください( grepcodeから取得したコード)processParameters()org.apache.tomcat.util.http.Parameters

public void processParameters( byte bytes[], int start, int len, 
                                   String enc ) {
        int end=start+len;
        int pos=start;

        if( debug>0 ) 
            log( "Bytes: " + new String( bytes, start, len ));

        do {
            boolean noEq=false;
            int valStart=-1;
            int valEnd=-1;

            int nameStart=pos;
            int nameEnd=ByteChunk.indexOf(bytes, nameStart, end, '=' );
            // Workaround for a&b&c encoding
            int nameEnd2=ByteChunk.indexOf(bytes, nameStart, end, '&' );
            if( (nameEnd2!=-1 ) &&
                ( nameEnd==-1 || nameEnd > nameEnd2) ) {
                nameEnd=nameEnd2;
                noEq=true;
                valStart=nameEnd;
                valEnd=nameEnd;
                if( debug>0) log("no equal " + nameStart + " " + nameEnd + " " + new String(bytes, nameStart, nameEnd-nameStart) );
            }
            if( nameEnd== -1 ) 
                nameEnd=end;

            if( ! noEq ) {
                valStart= (nameEnd < end) ? nameEnd+1 : end;
                valEnd=ByteChunk.indexOf(bytes, valStart, end, '&');
                if( valEnd== -1 ) valEnd = (valStart < end) ? end : valStart;
            }

            pos=valEnd+1;

            if( nameEnd<=nameStart ) {
                log.warn("Parameters: Invalid chunk ignored.");
                continue;
                // invalid chunk - it's better to ignore
            }
            tmpName.setBytes( bytes, nameStart, nameEnd-nameStart );
            tmpValue.setBytes( bytes, valStart, valEnd-valStart );

            try {
                addParam( urlDecode(tmpName, enc), urlDecode(tmpValue, enc) );
            } catch (IOException e) {
                // Exception during character decoding: skip parameter
                log.warn("Parameters: Character decoding failed. " + 
                         "Parameter skipped.", e);
            }

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

        } while( pos<end );
    }

このメソッドには、すべての要求パラメーターを解析するためのdo-whileループがあります。メソッドのループの最後にあるtry-catchブロックを見てください。というコメントがあります

// Exception during character decoding: skip parameter

したがって、例外がスローされても伝播されない場合でも。警告メッセージとしてログに記録され、パラメータ値はnullに設定されます。したがって、その例外をキャッチすることは決してできないことは明らかです(java.io.CharConversionException

于 2013-05-02T13:58:59.917 に答える
0

%他の答えは、すべてのブラウザも同様にエンコードし、エンコードされていないプレーン文字を受け取らないのが理想的であるため、入力をエンコードするように依頼するのが安全であると考えると正しいです。

しかし、そうは言っても、それでもエラーページを表示することを主張する場合は、これをキャッチしてクライアントにエラーページを送信するか、リクエストが異なる方法で処理されるように属性が設定されてCharConversionExceptionいる同じURIにリクエストを転送するカスタムフィルターを作成することができます。errorMessage

これは、。という名前のフィルターパラメーターを介してここで構成されていますuseErrorPage

編集:@Santoshによって共有されCharConversionException、Tomcatによって実際に抑制されていることを明らかにするソースコードに照らして、パラメーター検証を挿入し、フィルターパラメーターを介して構成できる禁止文字のリストについてすべての要求パラメーターをチェックするようにフィルターを変更しました名前付きforbiddenChars

このフィルターをweb.xmlのDisplayTagのResponseOverrideFilterの上に配置して、すべてをインターセプトするようにします。フィルタに関しては、順序付けには副作用があります。

この編集は、サーブレットフィルタが非常に強力な概念であり、任意の要求を任意の方法で前処理または後処理できるという事実を強調する機会と見なします。ほとんどのWebフレームワーク(MVCのStruts 2など)は、フィルターを使用してエントリポイントとして機能しています。

したがって、フィルターで実行できないことはほとんどありません。アプリケーション全体の認証は可能ですが、ビジネスロジックが明らかにそこに行くべきではないなど、正しい目的でそれらを使用することを確認してください。

IOExceptionフィルター

public class CharConversionExpFilter implements Filter {

    private char[] forbiddenChars;  // ADDED
    private FilterConfig filterConfig;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;
        forbiddenChars = filterConfig.getInitParameter("forbiddenChars")
                               .replace(",", "").toCharArray(); // ADDED
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        String requestURI = ((HttpServletRequest) request).getRequestURI();
        try {
            validateParameters((HttpServletRequest) request);  // ADDED
            chain.doFilter(request, response);
        } catch (IOException e) {
            if (e instanceof CharConversionException) {
                if ("true".equalsIgnoreCase(filterConfig.getInitParameter("useErrorPage"))) {
                    if (response instanceof HttpServletResponse) {
                        ((HttpServletResponse) response).sendError(400,
                        "The request cannot be fulfilled due to bad input.\nError:" + e.getMessage());
                    }
                } else {
                    request.setAttribute("errorMessage", e.getMessage());
                    filterConfig.getServletContext().getRequestDispatcher(requestURI).forward(request, response);
                }
            }
        }
    }

    // ADDED
    private void validateParameters(HttpServletRequest request) throws CharConversionException {
        Enumeration<String> parameterNames = request.getParameterNames();
        while (parameterNames.hasMoreElements()) {
            String parameter = request.getParameter(parameterNames.nextElement());
            if (parameter != null && parameter.length() > 0) {
                for (char forbidChar : forbiddenChars) {
                    if (parameter.indexOf(forbidChar) != -1) {
                        throw new CharConversionException(
                                String.format(
                                        "Parameter: [%s] contains the forbidden character [%c]",
                                        parameter, forbidChar));
                    }
                }
            }
        }
    }

    @Override
    public void destroy() {}
}


web.xml

<filter>
  <filter-name>CharConversionExpFilter</filter-name>
  <filter-class>servlet.filter.CharConversionExpFilter</filter-class>
  <init-param>
    <param-name>useErrorPage</param-name>
    <param-value>true</param-value>
  </init-param>
  <init-param> <!-- ADDED -->
    <param-name>forbiddenChars</param-name>
    <param-value>%,*,?,#,$</param-value> <!-- filtering wildcards and EL chars -->
  </init-param>
</filter>

<filter-mapping>
  <filter-name>CharConversionExpFilter</filter-name>
  <url-pattern>/*</url-pattern> <!-- * = ALL; "/servlet-name" if required -->
</filter-mapping>

<error-page>
  <error-code>400</error-code>
  <location>/errorPage.jsp</location> <!-- isErrorPage = true; use "exception" obj -->
</error-page>


これにより、ニーズに応じて解決策を見つけるのに十分な指針が得られることを願っています。(:構文の強調表示は、複数行のJavaコメントの開始として誤って解釈さ<url-pattern>/*れています。無視してください。)

于 2013-05-02T15:02:40.600 に答える