こんにちは、サイオステクノロジー技術部 武井です。今回は、OAuthやOpenID Connectで使われるstateパラメーターについて書いてみました。どうして、stateパラメータが必要なのか、stateパラメーターがないとどんな悪いことが起こるのか整理しました。
stateパラメーターとは?
stateパラメーターは、CSRF(クロスサイトリクエストフォージェリ)対策で付与します。CSRFとはユーザー本人がいとしないリクエストを、悪意のある第三者によって強制する攻撃のことです。だいぶ昔には、mixiの「はまちちゃん」で有名になりましたが、OAuthやOpenID Connectにも同様のリスクがあります。その対策がstateパラメーターになります。ただ、stateパラメーターで対策するのは、「なりすまされ」というのであって、ここが今までのCSRF対策とイマイチ違いがわかりにくい点になります。(少なくとも私は理解に苦しみました^^;)
stateパラメーターの説明
stateパラメーターのない世界と、ある世界ではどのように違うのかを説明することで、stateパラメーターの有効性を説明します。とあるシステムを例に上げることとします。そのシステムの登場人物は以下のとおりとなります。
ストレージサービス・・・クラウド上のオンラインストレージサービスです。ユーザーは自分の領域に任意のファイルを格納することが出来ます。 | |
サービスA・・・ストレージサービスを利用するサービスです。OAuth認証を用いて、ユーザーにストレージサービスを利用させます。 | |
Aさん・・・サービスAの正規な利用者です。 | |
悪い人・・・ユーザーAさんに対してCSRF攻撃を行おうとする攻撃者です。 |
stateパラメーターのない世界
stateパラメーターのない世界では、どのような悪いことが起こるのかを説明します。
まず、以下のようなシステムを想定します。ストレージサービスは外部にAPIを公開しており、そのAPIでファイルをアップロードできます。そしてAPIの認証はOAuthで行います。サービスAはOAuth認証を用いて、ユーザーがサービスAにアップロードした画像をストレージサービスにもアップロードします。悪い人は、まず、サービスAにアクセスして、OAuth連携を開始します。
悪い人は、ストレージサービスの認可画面にリダイレクトされます。「はい」をクリックします。
すると、認可コードをクエリパラメーターに付与した、サービスAへのリダイレクトが発生します。しかし、悪い人は、ここで、リダイレクト先のURLだけ取得をし、サービスAにリダイレクトさせないようにします。この実現方法は色々ありますが、独自のHTTPクライアントのプログラムを開発して、Locationヘッダからリダイレクト先のURLだけを抜き取り、実際のリダイレクトはさせないようにすればよいと思います。
悪い人は、先程取得したURLをメールなどの手段でAさんに送付して、AさんにそのURLにアクセスさせます。すると、アクセストークンを取得する主体は、悪い人からAさんに移り、悪い人のアクセストークンを取得するプロセスは、今後、Aさんのセッションで行われることとなります。ここがキモです。
サービスAは、認可コードをストレージサービスに渡して、アクセストークンを要求します。
ストレージサービスは、悪い人のストレージにアクセスするためのアクセストークンをサービスAに返します。なぜそうなるかというと、アクセストークンを取得するために発行された認可コードは、悪い人によって発行されたものだからです。
そして、そのアクセストークンは、ユーザーAのデータベースに格納されます。なぜそうなるかというと、認可コードをサービスAに渡したのは、他ならぬAさんだからです。
つまり、Aさんは、悪い人のアクセストークンを取得し、悪い人のストレージに自由にアクセスできるようになりました。他人のストレージを自由にアクセスできるわけですから、あまり害はないように思えますが、ここで一番まずのは、Aさんが知らない間にこのような状況に陥ってしまったことです。これは、「乗っ取り」ではなくて「乗っ取られ」といわれるもので、例えば、他の人に漏らしたくないような情報を自分のフォルダにアップロードしたつもりが、実は悪い人のフォルダに知らない間にアップロードしていることになり、悪い人はAさんのプライベートを見ることが出来てしまうわけです。
stateパラメーターのある世界
stateパラメーターがあると、先程のような被害を防ぐことが出来ます。先程のケースで、stateパラメーターを付与した場合にどの様になるかを説明してみます。
悪い人は、まず、サービスAにアクセスして、OAuth連携を開始します。ここまでは、先程と一緒です。
悪い人は、ストレージサービスの認可画面にリダイレクトされます。先ほどと違うのは、サービスAがリダイレクト先のURLのパラメーターにstate=xyzという値を付与していることです。さらに、このとき、サービスAは、このstateの値を悪い人のセッションに紐づけます(Javaでいえば、request.
すると、認可コードをクエリパラメーターに付与した、サービスAへのリダイレクトが発生します。しかし、悪い人は、ここで、リダイレクト先のURLだけ取得をし、サービスAにリダイレクトさせないようにします。先ほどと違うのは、ストレージサービスは、1つ前のプロセスで渡されたstateパラメーターをそのまま同じように、リダイレクト先のURLに付与しているところです。
先程と同様に、悪い人は、先程取得したURLをメールなどの手段でAさんに送付して、AさんにそのURLにアクセスさせます。
「stateパラメーターのない世界」と決定的に違うのがこのプロセスです。サービスAは、URLに付与されているstateパラメーターと、アクセスしてきたユーザーのセッションに格納されているstateパラメーターを比較して、一致していなければ不正なアクセスとして、処理を中断します。認可コードを取得したのは悪い人なので、stateパラメーターは悪い人のセッションの中に格納されていますが、認可コードをもとにアクセストークンを要求したのはAさんです。もちろん、Aさんのセッションの中には、stateパラメーターの値が入っていないので、下記のように不一致となります。このような理路により、CSRF攻撃を防御することが可能です。
最後に
いかがでしたでしょうか?OAuthやOpenID Connectには、このようにセキュリティを考慮した仕組みがいくつかありますので、今後も、ご紹介していきたいと思います。