本サイトは、快適にご利用いただくためにクッキー(Cookie)を使用しております。
Cookieの使用に同意いただける場合は「同意する」ボタンを押してください。
なお本サイトのCookie使用については、「個人情報保護方針」をご覧ください。

最新情報

2015.07.30

LWSとHTTPヘッダインジェクション

 今回のトピックは「HTTPヘッダインジェクション」です。

 HTTPヘッダインジェクションは、リクエストパラメータの操作等により、HTTPのレスポンスヘッダに改行文字(CR,LF)を挿入し、ヘッダフィールドを追加したり、ボディを操作したり、新たな偽のHTTPレスポンスを作り出したりする(HTTPレスポンス分割)攻撃、あるいは脆弱性です。

 状況にもよりますが、この脆弱性は、被害者のブラウザにおいて、脆弱性のあるページのオリジンで不正なJavaScriptを実行する攻撃につながりえます。つまり被害としては、クロスサイトスクリプティングと同様になりうるということになります。

 この脆弱性は古くから知られており、近年は特に目新しいトピックもなかったのですが、2014〜2015年に状況の変化があったので、今回取り上げてみたいと思います。

 タイトルにあるように、本記事の主なテーマは「LWS」とHTTPヘッダインジェクションとの関連です。LWSについては耳慣れない方も多いと思いますので、まずはLWSの説明から始めたいと思います。




■LWSとはなにか?


 HTTP/1.1の仕様であるRFC 2616(↓)は、リクエスト・レスポンスヘッダの値の部分を複数行に折り返すことを許しています。この折り返しに使用されるのがLWS(Linear White Space)です。



 同RFCにおいてLWSは下記の拡張BNFで定義されています。


LWS = [CRLF] 1*( SP | HT)

 日本語の表現に直すと、改行(CRLF)の後ろに、SP(スペース 0x20)またはHT(水平タブ 0x09)が1つ以上続くものがLWS、ということになります。

 具体的なLWSの使用例を見てみましょう。


X-Some-Header: aaaaa[CRLF]
[SP]bbbb:2[CRLF]
[SP]ccc:3[CRLF]

 RFC 2616の解釈に従うと、上記の2-3行目は、1行目のヘッダの値が折り返された部分とみなされます。つまり、2-3行目は、「X-Some-Header」という名前を持つ単一のヘッダフィールドの値の一部(aaaaaの続き)として扱われます。

 このLWSに関する規定は、1990年代に策定されたHTTP/1.0の標準であるRFC 1945(↓)を引き継いだものです。さらにLWSの起源を遡ると1980年代のEメールの仕様にたどり着きます。





■LWSの何が問題か


 上記のようにLWSそのものは新しいものではありませんが、執筆時(2015年)においても一部のブラウザやサーバは仕様通りにLWSを解釈しません。つまり、同一のヘッダが環境により異なる解釈を受ける可能性がある訳で、ここに攻撃者が付け入るわずかな隙ができます。

 具体的に言うと問題となるのはIE(〜IE11)です。IEはLWSをRFC通りに解釈せず、上記の例では「bbbb:2」「ccc:3」を新たなヘッダフィールドとして解釈しようとします。このIEの挙動は、意図しないHTTPヘッダインジェクションを引き起こす可能性があります。

 具体的な例を下記のPHPプログラムで見てみましょう。


<?php
header("X-Some-Header: ". $_GET['foo']);
?>

 このプログラムは、クエリストリングの「foo」パラメータを、header関数を用いてレスポンスヘッダに出力します。少し古いPHP(バージョン5.3系等)でこれを動かし、下記のクエリストリングを与えてやるとします。


?foo=test%0D%0A%20Set-Cookie:%20xxx=123
(%0D -> CR, %0A -> LF, %20 -> SP)

 上記のリクエストを送信すると、サーバは下記のようなヘッダを出力します。


X-Some-Header: test[CRLF]
[SP]Set-Cookie: xxx=123

 IEはLWSの後の「Set-Cookie: xxx=123」の部分を、X-Some-Headerの一部ではなく、独立したヘッダとして(通常のSet-Cookieヘッダとして)認識してしまいます。これは、他のメジャーブラウザでは見られないIE独自の挙動です。つまりLWSを使うことで「IE限定のHTTPヘッダインジェクション攻撃」が成立することになります。

 なお、この攻撃例では新たなヘッダ(Set-Cookie)を付け加えているだけですが、LWSを2つ連続させるとHTTPボディを操作することも可能です。

 ここで一つ強調しておきたいのは、HTTPヘッダインジェクションに対してPHPが全く無策なのかというと、そうではないということです。実際、バージョン5.1.2以降のPHPは、通常の改行をヘッダに出力できないようにする安全策を採っています。しかし、RFCの規定を尊重して、改行がLWSを構成する場合に限ってはヘッダへの改行の出力を許しています。

 つまり、これはPHPにとって意図通りの挙動であり、なおかつRFCの規定にも合致するものです。したがってこの件はPHP側の問題というよりはむしろ、ブラウザ(IE)側の誤解釈に起因する問題ととらえるのが自然ではないかと思います。

 LWSを使った攻撃方法は2012年にONsec Labにより公開されました(↓)。それ以前にも一部の研究者の間では知られていた方法で、筆者は2012年以前に元同僚から聞いてこの種の攻撃を知った記憶があります。





■新たな動き - 新RFCとPHPのリリース


 実はここまでの話はちょっと古い話です。冒頭で触れたように、昨年(2014年)〜今年(2015年)に掛けて、この問題をめぐる状況に変化が生じました。

 まず、2014年6月にHTTP/1.1の新たな仕様であるRFC 7230がリリースされました(↓)。新たなRFCにおいて、LWSは「obs-fold」と表記されています。「obs」はobsoleteの略であり、これはLWSが新RFCにおいて廃止されたことを意味しています。



 同RFCの3.2.4は、LWSを含むHTTPメッセージを一部の例外を除いて送ってはならない(MUST NOT)と規定しました。つまり、LWSを出力するPHPの挙動はRFCには準拠しない状態となったわけです。

 新RFCにより一転してPHP側の旗色が悪くなった訳ですが、PHPは比較的早くこの状況に対処しました。具体的には、2015年2月のリリース(PHP 5.4.38, 5.5.22, 5.6.6)で、LWSを含むヘッダをエラーとして出力できないようにする対処を行いました(↓)。




 しかし、PHPのこの変更は脆弱性の修正ではなく、通常のバグ修正という扱いでした。一部ではセキュリティへの影響が議論されて知られていますが(↓)、PHPユーザ/セキュリティコミュニティへの周知は充分では無いと思われるので、注意喚起の意味でも本ブログで取り上げています。



 繰り返すと、PHPにおいてLWSの対処がされているのは、5.4.38, 5.5.22, 5.6.6以降のheader関数です。これより古いバージョン(5.3系を含む)の同関数はLWS問題の影響を受けるため、外部からの入力をそのままヘッダ値の中に埋め込むような使い方をしている場合は、PHPをアップグレードするかアプリケーション側で対処する必要があります。

 なおLWSの問題があったのはPHPだけではないようですが、弊社の診断サービスで多く検出したのはPHP環境のアプリケーションであったと思います。その意味で、LWSを使ったHTTPヘッダインジェクションの問題は、2014年のRFC改定と、それを受けた2015年のPHPの修正をもって、おおむね収束する方向に向かった、と言えると思います。




■新RFCとブラウザ


 残るはブラウザ(IE)側の問題です。

 上記のように、新たなRFC(RFC 7230)はLWSを含むHTTPメッセージの送信を原則禁止しました。同時に、このRFCは、サーバから受け取ったメッセージに含まれるLWS(obs-fold)を、SPとして解釈するようブラウザに求めています(MUST)。

 したがって、IEがRFCに準拠しない状態は新RFC下においても続いているといえます。むしろ、LWSのブラウザでの解釈が「MUST」と明確な記述になったという点でいえば、IEの挙動がRFC非準拠であることは以前よりも明確になったと言えるかもしれません。

 この状況を踏まえて、2014年10月に筆者からMicrosoftに、IEの脆弱性としてLWSの件を報告をしましたが、残念ながら「Won't Fix」との回答でした。現状では、MicrosoftがIEの修正を行う予定はないということになります。Microsoftとしては、IEの動作がRFCに準拠していないことは理解しつつも、稀にしか問題にならない挙動であり、またそもそもWebサイト側で対処されるべき問題である、とのスタンスでした。




■その他の攻撃方法


 HTTPヘッダインジェクションに関して注意が必要なのはLWSだけではありません。

 例えば、RFCはCRLFだけではなく単体のLF(CRを伴わない)をヘッダの区切りとして使用することを認めており、実際のところ多くのブラウザはLF単体を区切りと認識します。つまり、WebアプリケーションがCRLFのみに対処している場合、HTTPヘッダインジェクションに脆弱となります。

 さらに、RFCには明示的に認める記述は無いようですが、多くのブラウザはCR単体をヘッダの区切りとみなします(IE、Chrome、Safari、Opera)。かつて、PHPのheader関数はCR単体を含む文字列を改行とは認識せず、エラーとしてはじいていなかったため、PHPとこれらのブラウザの組合せでCRを使った攻撃が可能でした。

 この手の問題は、ブラウザの問題なのか、PHP等のプラットフォームの問題なのか、はたまたWebアプリケーションの問題なのか、明確な答えが無いところではありますが、筆者は2011年にPHPの脆弱性としてCRの件の報告を行いました(PHP bug #54006)。最終的には別の方が同じ問題をPHPに報告し(↓)、2012年の5.3.11のリリースにより修正されました。



 その甲斐もあってか、最近では診断サービスでCRによるHTTPヘッダインジェクションを見る頻度は減りつつあります。




■まとめと対策


 今回は、HTTPヘッダインジェクションにまつわる最近の動向を取り上げました。HTTPの仕様はよりセキュリティを意識したものになり、またPHP等のプラットフォーム側の対応も進み、HTTPヘッダインジェクションの影響を受けるWebアプリケーションは徐々に減りつつあります。

 一方で、数は減ってはいるものの、この脆弱性は今なお弊社の診断サービスで検出され続けている脆弱性でもあります。冒頭で述べたように、クロスサイトスクリプティングと同様の被害につながる可能性がある脆弱性であり、確実な対策が望まれます。

 対策は比較的単純です。HTTPヘッダに出力するデータにCRとLFのいずれもが含まれないように、これらをチェックしてエラーとするか、必要な場合はスペースへの置換等をすることで対策できます。

 使用するプラットフォームがこれらを行っている場合は、Webアプリケーション開発者側で対策を意識する必要はありません。PHPの例でいうと、前述のように5.4系以上の最近のバージョンで、LWSを含む対処がなされています。一方、プラットフォーム側で対処されていない場合は、Webアプリケーション側で上記の対策を実装する必要があります。

 なお、新RFC下では、改行をLWSに置換したり、LWSだけを素通しさせたりする対処は、名目上も必要がなくなっています。むしろこういった処理は、一時期のPHPがそうであったように、RFCに準拠しない、脆弱性なWebアプリケーションを作り出すことにつながってしまいます。

プロフェッショナルサービス事業部
寺田健