Webシステムで使われるセッションは、利用者にとっても、開発者にとっても便利な機能です。しかし、CSRF対策がされていないサイトは、攻撃にさらされているかもしれません。
そこで今回は、PHP初心者に知ってほしいCSRF脆弱性と、CSRF対策について解説します。
目次
CSRF脆弱性とは何か
CSRFとは、クロスサイトリクエストフォージェリ(cross-site request forgeries)の略で、Webアプリケーションにおける脆弱性のひとつです。次から、このCSRF脆弱性について解説します。
意図しないリクエストを偽造する
Webブラウザにはセッションという機能があり、その機能を活用することで、認証を省略してサービスを受けることが可能です。しかし、Webサーバー側のセッション管理に脆弱性がある場合、ユーザーの意図しないリクエストがWebサーバーで処理されてしまいます。
例えば、CSRF脆弱性があるサイトにログインしている状態で、悪意のあるサイトを開いたケースをイメージしてください。
悪意のあるサイトは、JavaScriptなどを介して、利用者のWebブラウザから、そのログイン済のサイトに登録している個人情報を書き換える偽のリクエストを送信させます。この場合、CSRF脆弱性があるサイトでは、偽のリクエストを処理してしまいます。
CSRF脆弱性の原因
先ほどの例のように、CSRF脆弱性とは、セッションをチェックしてそれが有効だった場合、そのリクエストを処理してしまうことです。しかも、そのリクエストが正規の利用者のWebブラウザから送信されるため、ログなどで後からチェックすることができません。
PHPで開発するWebサービスに限らず、アカウントの認証に厳しくチェックするものの、それを通ってしまえば簡単なセッションのチェックのみでアクセスを許可してしまうケースがたくさんあります。
そしてそのようなWebサービスにはCSRF脆弱性が含まれており、攻撃にされる可能性があると考えてください。
CSRF脆弱性の悪用例
CSRF脆弱性を利用した攻撃として、よく挙げられる例が意図しない商品の購入です。しかも、知らない住所に送付され、メールアドレスも改竄されてしまえば、クレジットカードの明細をチェックしない限り気が付かずに過ごしてしまいます。
また、SNSに勝手に投稿されてアカウントが使えなくなったり、犯罪に巻き込まれる可能性もあります。さらに、CSRF脆弱性が原因でWebサービスを停止せざるえないケースもありうるので、CSRF対策を取りましょう。
CSRF対策のポイント
先ほど解説したように、CSRF脆弱性をそのままにはできません。しっかり対策してください。次から、PHPのCSRF対策のポイントについて解説します。
Webサーバーで対策可能
先ほども解説したように、CSRF攻撃で使われるリクエストは、正規ユーザーのWebブラウザから送信されているので、送信経路による対策はできません。
しかし、CSRF攻撃で使われるリクエストは、攻撃者が勝手に作成したものです。そのため、Webサーバー側で、正規のリクエストと偽造されたリクエストかを識別できれば、CSRF対策が可能です。
攻撃者が推測できない情報を入れる
攻撃者は、あらかじめ偽のリクエストを送信するページを入手し、それを基に偽のリクエストを作成します。もし、リクエストを送信するページに毎回違うコードを埋め込んおけば、そのコードをチェックすることで、偽のリクエストを除外することが可能です。
なお、このような処理は、PHPのセッションの機能で簡単に作成できます。また、セキュリティ対策用のライブラリを活用することで、より安全性の高いWebシステムの構築も可能です。
乱数を使ったトークンを作る方法
先ほど説明した攻撃者が推測できない情報として用いられるのが、乱数を使ったトークンです。フォームを開いた際に、乱数を使って毎回違うトークンを作成し、それをフォームに埋め込むことで、CSRF対策が可能です。
なお、このような1回だけ有効なワンタイムトークンをPHPで作成する場合、通常はopenssl_random_pseudo_bytes()関数を使います。また、このように作成したトークンは、利用者から見えないようにhidden属性でフォームに埋め込みます。
CSRF対策例
次から、CSRFの脆弱性を含むPHPの処理の例と、対策した場合の例を紹介します。
CSRFの脆弱性を含むPHPの例
次の例は、PHPのセッション機能を利用し、正常な処理と不正なリクエストとを切り分ける処理です。具体的には、既に認証済みの場合、アカウントIDがセッションに格納されているので、その有無をチェックして正常な処理に移ります。
1 2 3 4 5 6 7 8 9 10 11 |
<?php if(isset[$_SESSION['id']) { /* * ここから正常な処理 */ } else { /* * 不正なリクエストへの処理 */ } ?> |
この例はCSRFの脆弱性を含んでいるので、攻撃によりデータの改竄などの被害が発生します。
CSRF対策したPHPの例
最も簡単なCSRF対策は、フォームの中にトークンを埋め込み、それをセッションに格納したトークンと比較して正常なリクエストか不正なリクエストかを判断する方法です。
次にその簡単な例で、予めフォームにtokenを埋め込んでおき、そのtokenをセッションに格納した例です。乱数を使い、フォームを表示する毎に毎回違うtokenを作ることで、CSRFに対策することが可能です。
1 2 3 4 5 6 7 8 9 10 11 12 |
<?php if( isset($_POST["token"]) && ( $_POST["token"] === $_SESSION['token'] ) ) { /* * ここから正常な処理 */ } else { /* * 不正なリクエストへの処理 */ } ?> |
トークンを作る処理の例
先ほどのCSRF対策したPHPの例で使用するトークンを作成する処理の例を次に紹介します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
トークンを生成する処理の例 <?php session_start(); $token_byte = openssl_random_pseudo_bytes(16); $csrf_token = bin2hex($token_byte); $_SESSION['token'] = $csrf_token; ?> トークンを埋め込んだフォームの例 <form method="POST" action="(送信先のPHPファイル)"> <input type="hidden" name="token" value="<?php echo $token; ?>"> ... </form> |
openssl_random_pseudo_bytes()関数は、引数で指定された長さの疑似ランダムなバイト文字列を生成する関数です。そのため、そのままでは使えないので、bin2hex()関数で16進数のASCII文字列に変換して使用します。
CSRF対策で使われるライブラリ
既に運用しているWebシステムでは、PHPのプログラムを書き換えて、CSRF対策を施すことが難しいケースもあります。しかし、これを放置する訳にはいきません。
そこで、そのようなWebシステムで検討してほしいのが、PHPシステム向けのCSRF対策ライブラリです。仕様はライブラリによっても違いますが、フォームを生成するPHPでライブラリを読み込むだけで、CSRF対策を組み込んでくれます。
PHP向けのCSRF対策ライブラリへのリンク
OWASP CSRFProtector
csrf-magic
まとめ
CSRFの脆弱性が放置されていると、利用者が被害を受けるだけでなく、サイトの信頼性も損なわれます。そのため、しっかりと対策しなければなりません。
PHPを利用してれいば、今回紹介したように、比較的簡単に対策できます。また、修正が難しいシステムでも、ライブラリを活用すれば、対策は可能です。CSRF脆弱性について理解し、しっかりとCSRF対策できるスキルを身に付けてください。