web-dev-qa-db-ja.com

CSRFトークンとは何ですか?その重要性は何ですか?またそれはどのように機能しますか?

私はアプリケーションを書いていますが(Django、そういうことです)、実際には「CSRFトークン」とは何か、そしてそれがどのようにデータを保護するのかを知りたいだけです。 CSRFトークンを使用しない場合、投稿データは安全ではありませんか?

479
Shawn

簡単な言葉でのクロスサイトリクエストフォージェリ(CSRF)

  • 現在www.mybank.comでオンラインバンキングにログインしていると仮定します
  • mybank.comからの送金の結果、(概念的に)http://www.mybank.com/transfer?to=<SomeAccountnumber>;amount=<SomeAmount>という形式のリクエストが発生すると想定します。 (ログインによって暗示されるため、アカウント番号は必要ありません。)
  • www.cute-cat-pictures.orgにアクセスしますが、悪意のあるサイトであることを知りません。
  • そのサイトの所有者が上記のリクエストの形式を知っていて(簡単!)mybank.comに正しくログインしていると推測した場合(運が必要です!)、ページにhttp://www.mybank.com/transfer?to=123456;amount=10000のようなリクエストを含めることができます(ここで、123456はケイマン諸島のアカウントの番号であり、10000は以前にあなたが思っていた金額glad所有)。
  • Youwww.cute-cat-pictures.orgページを取得したため、yourブラウザはその要求をします。
  • 銀行はこのリクエストの発信元を認識できません。Webブラウザはwww.mybank.com Cookieとともにリクエストを送信し、完全に正当に見えます。あなたのお金があります!

これが世界ですCSRFトークンなし

より良いもののためにwithCSRFトークン

  • 転送要求は、3番目の引数http://www.mybank.com/transfer?to=123456;amount=10000;token=31415926535897932384626433832795028841971で拡張されます。
  • このトークンは、推測するのが不可能な巨大な乱数であり、mybank.comが提供するときに独自のWebページに含めます。誰かにページを提供するたびにdifferentです。
  • 攻撃者はトークンを推測できず、Webブラウザに降伏するように説得することはできません(ブラウザが正常に動作する場合...)。したがって、攻撃者はnot有効なリクエストを作成できます。間違ったトークン(またはトークンなし)のリクエストはwww.mybank.comによって拒否されるためです。

結果:10000通貨単位を保持します。その一部をウィキペディアに寄付することをお勧めします。

(あなたのマイレージは異なる場合があります。)

読む価値のあるコメントから編集:

HTTPアクセス制御のため、www.cute-cat-pictures.orgからのスクリプトは、通常www.mybank.comからのCSRF対策トークンにアクセスできないことに注意する価値があります。このメモは、他のWebサイトからAPIを使用できないという理由だけで、理由を知らずにWebサイトの応答ごとにヘッダーAccess-Control-Allow-Origin: *を不当に送信する一部の人々にとって重要です。

1357
Lutz Prechelt

はい、投稿データは安全です。しかし、そのデータの由来はそうではありません。これにより、攻撃者のWebページを閲覧しながら、誰かがあなたのサイトにログインするためにJSを使用するユーザーをだますことができます。

それを防ぐために、Djangoはクッキーとフォームデータの両方でランダムなキーを送ります。その後、ユーザーがPOSTを実行すると、2つのキーが同一かどうかが確認されます。ユーザーがだまされた場合、サードパーティのウェブサイトはあなたのサイトのクッキーを取得することができないので、認証エラーが発生します。

210

サイトはフォームページを作成するときに一意のトークンを生成します。このトークンは、データをサーバーにポスト/取得するために必要です。

トークンはサイトによって生成され、フォームを含むページが生成されたときにのみ提供されるため、他のサイトではフォームを模倣できません - トークンがないため、サイトに投稿できません。

65
tkone

例を挙げましょう…

A.comでホストされている、単純化されたTwitterのようなWebサイトがあるとします。サインインしたユーザーは、POSTリクエストとしてサーバーに送信され、送信ボタンを押すと公開されるフォームにテキスト(ツイート)を入力できます。サーバー上では、ユーザーは一意のセッションIDを含むCookieによって識別されるため、サーバーは誰がツイートを投稿したのかを認識できます。

フォームはそれと同じくらい単純かもしれません:

 <form action="http://a.com/Tweet" method="POST">
 <input type="text" name="Tweet">
 <input type="submit">
 </form>

今、想像してみて、悪者がこのフォームを悪意のあるWebサイトにコピーして貼り付けたところ、b.comと言ったことがあります。フォームはまだ機能します。ユーザーがあなたのTwitterにサインインしている限り(つまり彼らはa.comのための有効なセッションクッキーを持っている)、POSTリクエストは http://a.com/Tweetに送信されるでしょう。 ユーザーが送信ボタンをクリックしたときに通常どおり処理されます。

これまでのところ、フォームが正確に何をするのかについてユーザーに認識されている限り、これは大きな問題ではありません。

 <form action="https://example.com/Tweet" method="POST">
 <input type="hidden" name="Tweet" value="Buy great products at 
 http://b.com/#iambad">
 <input type="submit" value="Click to win!">
 </form>

ユーザーの1人が悪意のあるユーザーのWebサイトにアクセスして「Click to win!」ボタンをクリックすると、フォームがWebサイトに送信され、ユーザーはCookieのセッションIDによって正しく識別され、隠されたツイートが表示されます。公開しました。

さらに悪いことに、JavaScriptを使って自分のWebページを開いたらすぐに無邪気なユーザーにこのフォームを送信させるようにします。これは基本的にクロスサイトリクエストフォージェリです。

フォームはどこからでもどこへでも簡単に送信できます。一般的にはこれが共通の機能ですが、フォームが属するドメインからの送信のみを許可することが重要な場合がもっとあります。

WebアプリケーションがPOSTリクエストとGETリクエストを区別しない場合(たとえばPHPでは$ _POSTではなく$ _REQUESTを使用するなど)、状況はさらに悪くなります。しないでください。データ変更要求は、 http://a.com/tweet?tweet=This+is+really+bad のように簡単に送信でき、悪質なWebサイトや電子メールにも埋め込むことができます。

フォームを自分のWebサイトからのみ送信できるようにするにはどうすればよいですか。これが、CSRFトークンが登場する場所です。CSRFトークンは、ランダムな、推測しにくい文字列です。保護したいフォームがあるページでは、サーバーはランダムな文字列であるCSRFトークンを生成し、それを隠しフィールドとしてフォームに追加します。また、セッションに保存するか、Cookieを設定することによって何らかの形で覚えておきます。値を含みます。これでフォームは次のようになります。

    <form action="https://example.com/Tweet" method="POST">
    <input type="hidden" name="csrf-token" 
    value="nc98P987bcpncYhoadjoiydc9ajDlcn">
    <input type="text" name="Tweet">
    <input type="submit">
    </form>

ユーザーがフォームを送信すると、サーバーは投稿されたフィールドcsrf-token(名前は関係ありません)の値をサーバーが記憶しているCSRFトークンと比較するだけです。両方の文字列が等しい場合、サーバーはフォームの処理を続けます。それ以外の場合、サーバーは直ちにフォームの処理を中止し、エラーを返します。

なぜこれが機能するのですか?上記の例の悪者がCSRFトークンを取得できない理由はいくつかあります。

隠しフィールドの値はユーザーごとに異なるため、静的ソースコードを私たちのページから別のWebサイトにコピーしても意味がありません。悪意のあるWebサイトが現在のユーザーのCSRFトークンを知らないと、サーバーは常にPOSTリクエストを拒否します。

悪意のあるユーザーの悪意のあるページはユーザーのブラウザによって異なるドメイン(a.comではなくb.com)から読み込まれるため、悪意のあるユーザーはJavaScriptをコーディングすることができません。あなたのウェブサイトこれは、WebブラウザがデフォルトでクロスドメインAJAXリクエストを許可しないためです。

ドメインが一致しないため、悪者もあなたのサーバーによって設定されたクッキーにアクセスすることができません。

クロスサイトリクエストフォージェリに対していつ保護する必要がありますか? GET、POST、および上記の他の要求方法を混同しないようにするには、すべてのPOST要求をデフォルトで保護することをお勧めします。

PUTリクエストとDELETEリクエストを保護する必要はありません。上記で説明したように、標準のHTMLフォームはこれらの方法を使用してブラウザから送信することはできないためです。

一方、JavaScriptは実際には他の種類の要求を出すことができます。 jQueryの$ .ajax()関数を使用しますが、AJAXが機能するためのリクエストは一致する必要があります(そうでない場合は明示的にWebサーバーを設定しない限り)。

つまり、AJAXリクエストであってもPOSTリクエストにCSRFトークンを追加する必要さえない場合が多いのですが、WebでCSRFチェックのみをバイパスするようにする必要があります。 POSTリクエストが実際にAJAXリクエストである場合はアプリケーション。 X-Requested-WithのようなAJAXリクエストに通常含まれるヘッダの存在を探すことでそれを行うことができます。別のカスタムヘッダーを設定して、サーバー側でその存在を確認することもできます。ブラウザは通常のHTMLフォーム送信にカスタムヘッダを追加しないので安全です(上記参照)。これにより、Bad Guy氏がフォームでこの動作をシミュレートすることはなくなります。

AJAXリクエストに疑問がある場合、何らかの理由でX-Requested-Withのようなヘッダを確認できないため、生成されたCSRFトークンをJavaScriptに渡してAJAXに追加します。 ]リクエスト。これを行うにはいくつかの方法があります。通常のHTMLフォームのようにペイロードに追加するか、AJAXリクエストにカスタムヘッダーを追加します。あなたのサーバーが入ってくるリクエストの中でそれを探す場所を知っていて、それをセッションやクッキーから覚えている元の値と比較することができる限り、あなたはソートされます。

16
Dan

その根底にあるのは、リクエストがサイトの実際のユーザーから来ていることを確認することです。フォーム用にcsrfトークンが生成され、ユーザーのセッションに関連付けられている必要があります。これはサーバーに要求を送信するために使用され、そこでトークンがそれらを検証します。これはcsrfから保護する1つの方法です。もう1つは参照元ヘッダーをチェックすることです。

5
gladysbixly