ぼちぼち日記

おそらくプロトコルネタを書いていることが多いんじゃないかと思います。

HTTP/2とTLSの間でapacheがハマった脆弱性(CVE-2016-4979)

0. 



apache2.4HTTP/2TLS(CVE-2016-4979)

 Firefox

HTTP/2TLSSPDY

Secondary Certificate Authentication in HTTP/2IETF

1. はじめに


apache2.4httpd(CVE-2016-4979)

CVE-2016-4979: X509 Client certificate based authentication can be bypassed when HTTP/2 is used

apache-2.4HTTP/2TLSCVSS37.5HIGH
HTTP/2(RFC7540)TLS1

IETFhttpbis WGHTTP/2 TLS

HTTP/2TLSHTTP/2

2. TLSクライアント認証をバイパスするapache-2.4の脆弱性(CVE-2016-4979)とは?


apache-2.4.20HTTP/2

 /client_auth apacheconfigFirefox47使Chrome

 / HTTP/2/client_auth  
辿 /client_auth Firefox

/client_auth 

apache httpd
--- a/modules/ssl/ssl_engine_kernel.c
+++ b/modules/ssl/ssl_engine_kernel.c
@@ -727,6 +727,7 @@ int ssl_hook_Access(request_rec *r)
                      * on this connection.
                      */
                     apr_table_setn(r->notes, "ssl-renegotiate-forbidden", "verify-client");
+                    SSL_set_verify(ssl, verify_old, ssl_callback_SSLVerify);
                     return HTTP_FORBIDDEN;
                 }
                 /* optimization */

mod_sslSSL verify_mode 

3. HTTP/2のTLSクライアント認証の課題


HTTP/2TCP/TLSHTTPHTTP/1.1 HTTP Head of Line Blocking 

TLSHTTPHelloRequestRenegotiation(TLS)RenegotiationTLSTLSKCI

HTTP/1.1TCP/TLSHTTP/2HTTPTCP/TLS
HTTP/2HTTP/1.1 renegotiation 

HTTP/2HTTPHTTP/2

4. HTTP/2接続後のTLSクライアント認証

このままではHTTP/2を使うとWebのリソースの一部をクライアント認証することができずに困ってしまいます。そこで、あまりスマートなやり方ではありませんが、クライアント認証が必要なリクエストをHTTP/1.1に逃がすことでまぁなんとか対応可能に落とし込みました。

HTTP/2のリクエストがクライアント認証が 必要なリソースへアクセスしたらサーバ側は、そのストリームをHTTP1.1 requiredのエラーコードでリセットします。クライアントはHTTP/1.1 requiredのリセット通知を受けると新たにHTTP/1.1の接続を張り、そのハンドシェイクを通じてサーバはクライアント証明書の検証を行ってアクセスを判断します。 これでなんとか回避可能です。

5. CVE-2016-4979の原因


CVE-2016-4979HTTP/1.1 verify_mode 

SSLverify_mode SSL_VERIFY_NONE mod_ssl verify_mode  verify_old  verify_mode  verify_mode (SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT:) renegotiation 

HTTP/2 renegotiation RST_STREAMHTTP/2TLSSSLverify_mode

        /* remember old state */
        verify_old = SSL_get_verify_mode(ssl);
        /* configure new state */
        verify = SSL_VERIFY_NONE;
        (中略)

        /* TODO: this seems premature since we do not know if there
         *       are any changes required.
         */
        SSL_set_verify(ssl, verify, ssl_callback_SSLVerify);
        SSL_set_verify_result(ssl, X509_V_OK);

        /* determine whether we've to force a renegotiation */
        if (!renegotiate && verify != verify_old) {
        (中略)
        }

 TODO 

FirefoxHTTP/2HTTP/1.1

FirefoxHTTP/2使 /client_auth SSL verify_mode  /client_auth HTTP/2
1HTTP/2 renegotiationHTTP/1.1 verify_mode SSL_VERIFY_NONEHTTP/1.1退

Chrome(51Stable)調Chromehttp/1.1 requiredRST_STREAMHTTP/2HTTP/1.1

6. Secondary Certificate Authentication in HTTP/2


HTTP/2TLSHTTP/2SPDYSPDY/3CREDENTIAL

GoogleChromeCREDENTIALHTTP/2

HTTP/2MSMozilla2BAIETFSecondary Certificate Authentication in HTTP/2 IETF

TLSHTTP/2