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

最新情報

2022.10.05

Cookie Prefixのバイパス

MBSDで、主にWebアプリスキャナの開発をしている寺田です(稀に脆弱性調査とセキュリティ診断もしています)。最近、Cookie Prefixのバイパスに関する情報に何度か接することがあったので、記事にしてみます。

Cookie Prefixとは何か

バイパスの話の前に、そもそもCookie Prefixとはどういうものか見てみましょう。

Cookie Injection

Cookie PrefixはCookie Injectionと呼ばれる攻撃への対策です。Cookie Injectionとは、ある対象サイト向けのCookieを、他人のブラウザにインジェクトしようとする攻撃です。

Cookie Injectionの方法は主に以下の2つです。下では対象サイトを「https://www.example.jp」としています。

  • 非HTTPSの通信(通信経路上の攻撃者が制御できる)から被害者のブラウザにCookieをセットする
    例: 被害者を http://www.example.jp にアクセスさせて、通信経路上の攻撃者がSet-Cookie付き(Secure属性無し)の応答を返す
  • 関連するドメイン(攻撃者が一定の制御を持つサブドメイン等)から被害者のブラウザにCookieをセットする
    例: attacker-controlled.example.jp から Domain=example.jpのCookieをセットすると、www.example.jpにもそのCookieが送られる

これらの方法で植え付けられるCookieは、本来あるべきSecure属性がなかったり、本来のものと違うDomain属性を持っていたりします。しかし、ブラウザからサーバに送られるのはCookie名と値だけであるため、サーバはそういう差異に気付くことが出来ません。

被害者のブラウザにCookieを植え付けることで、攻撃者は以下のような攻撃を行えるかもしれません。

  • Session Fixation
  • Cookieを利用したXSSやOpen Redirect
  • サービス利用の妨害

攻撃の詳細はCookies Lack Integrity: Real-World Implications(Xiaofeng Zheng et al. 2015年)を参照していただくとして、ここではポイントだけ記します。

上のSession Fixationは、ログイン処理等における通常のSession Fixationに加えて、攻撃者アカウントでログイン済みのセッションIDを被害者に使わせるタイプの攻撃(Authenticated-as-Attacker)を含みます。このタイプの攻撃の典型的なパターンは次のようなものです。

  • 攻撃者は自身のアカウントでログインしてセッションIDを得る。
  • 攻撃者は、非HTTPSの通信やサブドメインのページを使い、そのセッションIDを被害者のブラウザにセットする。
  • 被害者は、アカウントに紐付く何らかの情報をサイト上で入力して、サブミットしてしまう。
  • 被害者は攻撃者アカウントのセッションIDを持っているため、サブミットした情報は攻撃者アカウントに紐づいてサイトのDBに保存される。
  • 攻撃者は自身のアカウントでログインして、DBに保存された被害者の情報を参照する。

このタイプの攻撃への対策として昔から知られているのは、ユーザが別人としてログインしていることに気付けるよう、画面にユーザID等を表示するというものです。しかし、被害者に与えるCookieのPathやDomain属性を本来のものと変えることで、気付くのが非常に難しい攻撃ができる場合があります(Zheng氏らのペーパーでCookie Shadowingと呼んでいる方法)。

こうした攻撃に対処するため、2016年頃に作られたのがCookie Prefixです。特別なPrefixを持つCookieについては、非HTTPSやサブドメインからのセットが出来ないようにする仕組みです。XSSや通常のSession FixationについてはCookie Prefix以外の根本的な対策がありますが、それがないAuthenticated-as-Attackerの攻撃については、Cookie Prefixを使って対策するのが簡単だと思います。

Cookie Prefix

現在Prefixとして__Secure-, __Host-の2つが定義されています。

__Secure-

このPrefixが付いているCookieは、セキュア属性が付いていなければなりません(現代のブラウザでは、HTTPS等のセキュアなチャネルでセットされなければならないことも意味する)。このPrefixは、非HTTPSでのCookieのセットを防ぐものです。

__Host-

このPrefixが付いているCookieは、そのDomain属性が空でなければなりません(いわゆるHost-specificでなければならない)。加えて、Path属性が「/」であり、Secure属性付きでなければなりません。このPrefixは、HTTPSのサブドメインからのCookieのセットを防ぐものです。

それぞれ、条件をまとめると以下になります。

Secure属性Path属性Domain属性
__Secure-必要(条件無し)(条件無し)
__Host-必要/付いていてはならない

ブラウザは、Set-Cookiedocument.cookieによるCookieのセットの際にこの条件をチェックして、条件を満たさない時はそのCookieの受け入れを拒否します。

ブラウザがチェックしているため、サーバ側ではPrefixが付いたCookieについて、その条件が満たされたもの(__Secure-ならばHTTPSでセキュア属性が付いてセットされたもの)とみなす、というセキュリティモデルです。サーバ側でやらなければならないのは、Cookieを読み書きする時にPrefixをCookie名に付けるだけなので、比較的簡単に導入できます。

Cookie Prefixの仕様は、RFC 6265 bisにて定められています。現在はすべてのメジャーブラウザ(Chrome, Safari, Firefox)がこの仕様をサポートしています。

参考まで、弊社の最近の診断データを見ると、Cookie Prefixが使われているサイトは全体の1-2%程度に留まりました。これは、用途が分かりにくいセキュリティ機能であるということと、上記のような攻撃のリスクは小さいとみなされていることが理由かもしれません。リスクについてはそのとおりで、攻撃者が通信経路上にいる OR サブドメインを制御できる、という攻撃の条件があるために、高いリスク判定にはしづらい問題です。

ちなみに、(既にお分かりだと思いますが)Cookie Prefixは他人のCookieを操作することを防ぐものであり、攻撃者自身が操作したCookieを送ることを防ぐものではありません。したがって、Cookieを使ったSQLインジェクション攻撃等を防ぐ効果はありません。

HSTSとの違い

Cookie Prefixと似た効果を持つものに、HSTS(HTTP Strict Transport Security)があります。以下はHSTSの設定例です。

【応答ヘッダ】
Strict-Transport-Security: max-age=31536000; includeSubDomains

HSTSを使うと、そのドメイン(includeSubDomainsが付いている場合はサブドメインを含む)でHTTPS接続を強制できるため、__Secure-と似た効果(非HTTPSからのCookieセットを防ぐ)が期待できます。ただし以下の制限があります。

  • 全てのページがHTTPSで提供されているサイトでないと使用できない。
  • includeSubDomainsを含める必要がある。
    サブドメインを含めて全ページがSSL化されている必要がある(参考)。
  • 最上位ドメインで設定する必要がある。
    弊社サイト(www.mbsd.jp)で言うと、https://mbsd.jp(www無し)でHSTSを有効にしないと、http://mbsd.jp/へのCookieのセットを防げない(参考)。
  • 初回利用時や期限が切れた状態では機能しない。
    Preloadを使うと初回利用時の制限はない。通信経路上の攻撃者がNTP時刻を改竄して期限をバイパスできる可能性がある(参考1,参考2) ※。
  • サブドメインのHTTPSのページからのCookieのセットを防げない。
    Cookie Prefix(__Host-)であれば防げる。

※ 筆者もMacBookで検証しましたが、NTPの時刻改竄によるHTSTのバイパスは基本的に可能なようです。

Cookie Prefixのバイパス

だいぶ前置きが長くなりましたが、本題のバイパスについてです。ここでは4種類の攻撃/脆弱性を紹介します。

大文字・小文字問題(仕様)

一般に、ブラウザやサーバ側のCookie解析器はCookie名の大小文字を区別します。つまり、名前がFOOのCookieとfooのCookieは通常は別のものと扱われます。Cookie Prefixの仕様もprefixの大小文字を区別して扱うように規定しています。

If a cookie's name begins with a case-sensitive match for the string __Secure-, then the cookie will have been set with a Secure attribute.

しかし、一部にはCookie名の大小文字を区別しないサーバがあります(代表的なものはASP.NETです)。そういったサーバには以下のような攻撃が可能です。

ブラウザ ----------> http://example.jp/ (HTTP)  中間者          <---------- Set-Cookie: __SeCuRe-foo=1234   HTTPSでない+Secure属性も無いが、__Secure Prefix付きではないのでブラウザはCookieのセットを許す。 ブラウザ ----------> https://example.jp/Default.aspx (HTTPS)                      Cookie: __SeCuRe-foo=1234   ASP.NETのRequest.Cookies["__Secure-foo"] は 1234。これをPrefix付きの安全なCookieだと誤認する。

上の攻撃は@ddworken氏がHTTP WGに立てたissueに投稿したものです(2022年8月)。

ASP.NETにも正しいCookie名を得る方法はあるため、必ずバイパスできる訳ではありませんが、通常のCookieの取得方法(Request.Cookies["Cookie名"])をしていると上記の攻撃が可能になる、ということです。

これまで長らくCookie名の大小文字を区別する実装と区別しない実装があり、すなわち大小文字の扱いに一貫性が無い状態であったわけですが、Prefixによって名前にセキュリティ的な意味を持たせるようにしたため、これまで隠れていた問題が顕在化したというところでしょう。

イレギュラーな処理をするサーバのせいとも言えますが、Issueでの議論を見ると、大小文字を区別しないで比較するようにCookie Prefixの仕様が変更されるようです。

特殊文字問題(ブラウザ)

次はつい先日公開された(2022年9月)、ブラウザ側のCookie名の解釈の問題です。

まずはFirefoxの脆弱性(CVE-2022-40958)です。

CVE-2022-40958: Bypassing Secure Context restriction for cookies with __Host and __Secure prefix (CVE-2022-40958)

サイト上の説明は以下です。

By injecting a cookie with certain special characters, an attacker on a shared subdomain which is not a secure context could set and thus overwrite cookies from a secure context, leading to session fixation and other attacks.

これ以上の詳細はまだ公開されていませんが、Firefoxのソースコードを見ると、特殊な記号を使った通常とは異なる形式のCookieにすると、Prefixに関するCookie属性のチェック処理が行われない、というものであったと思われます。

今月は、ChromeのCookie Prefix絡みの脆弱性(CVE-2022-2860)も修正されています。以下はCVEについている説明です。

Insufficient policy enforcement in Cookies in Google Chrome prior to 104.0.5112.101 allowed a remote attacker to bypass cookie prefix restrictions via a crafted HTML page.

こちらはFirefoxのものよりも情報量が少なく、筆者もChromiumのソースコードを調べられていないのですが、同種のバグなのかもしれません。

ちなみに、上のFirefoxとChromeのバグは、同じ方(Axel Chong氏)により報告されています。

エンコード問題(サーバ側)

大文字・小文字問題の例でもわかるように、Cookieの解釈はサーバ側のフレームワーク/言語によってかなり異なります。その中で割と共通的にみられるのはCookie名をURLデコードする挙動です。

下のように、URLデコードするサーバの挙動はバイパスに使える可能性があります。

Cookie: %5F%5FSecure-foo=123  => サーバ側で「%5F」が「_」にデコードされる

大文字・小文字の例と同じように、ブラウザ側ではPrefix付きとはみなされないCookieが、サーバ側ではみなされてしまうという問題です。

URLデコードを利用したバイパスは、PHPとRailsで脆弱性として修正されています。

PHP
[CVE-2020-7070] Sec Bug #79699 PHP parses encoded cookie names so malicious `__Host-` cookies can be sent

Ruby on Rails
[CVE-2020-8184] Percent-encoded cookies can be used to overwrite existing prefixed cookie names

この2つは同じ方(Matt Langlois氏)によって報告されています。

その他の問題(サーバ側)

エンコード以外にも、要求のCookieヘッダの扱いには様々なブレがあります。

  • 区切り文字の扱い ( ;  ,
  • クォートの扱い ( "  \
  • 特殊記号の扱い
  • 空白/制御文字の扱い
  • 非ASCII文字の扱い

この辺りの混乱は、Cookie仕様の歴史的経緯(元々Netscapeの簡易な仕様からはじまり、何度かRFCが改定された)とも関連しています。また、Prefixができる前に作られたソフトウェアの多くは、Cookie名にセキュリティ的な意味があるという前提で作られていない、つまり細かいことに頓着せずにCookieをparseしているという事情もあると思います。

Cookie仕様に関して言うと、2011年に作られたRFC 6265以降、(bisでの修正はあるものの)仕様は安定しています。しかしこのRFCも完全に準拠し辛いところがあります。例えば、この仕様ではCookieの値にカンマは使えないことになっています。これは、古いサーバの一部(例えばTomcat7)が、下のような要求のCookieを2つのCookieとしてみなしてしまうためであると思われます。

Cookie: AAA=BBB,__Secure-foo=1234

しかし、多くのブラウザは値にカンマを使えるようにしています。ブラウザベンダもRFC的にカンマが使えないことは認識しているはずですが、カンマを値に含むCookieは現実に多く使われているため、そうせざるを得ないのでしょう。そういう意味では、Cookieのカオスな世界はまだ完全には収束していないと言えるのかもしれません。

ちなみに、昔のRFCでは「カンマもCookieの区切り文字として認識すべき」(SHOULD)となっていました。それに従ったサーバ実装は少なかったと思いますが、その1つが当時のTomcatでした。現状のRFC 6265ではセミコロンだけが区切り文字であり、Tomcatの新しいバージョンはそれに従っているためカンマを区切りとして認識することはありません(Tomcat7は既にEOLですので、Cookie云々以前に使用しないことをお勧めします)。

なお、エンコードやカンマの件は一例で、この方面の脆弱性研究が進めば、もう少し色々なブラウザ/サーバ、あるいは仕様の問題が今後出てくるかもしれません(仕様にかかわるものはhttpwg/http-extensionsで見ることができます)。筆者もウォッチしていきたいと思います。

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