web-dev-qa-db-ja.com

リクエストごとの新しいCSRFトークンかどうか。

だから私は周りを読んでいて、CSRFトークンを持っていることについて本当に混乱していました、私がリクエストごとに、またはちょうど1時間または何かごとに新しいトークンを生成する必要があるかどうか?

$data['token'] = md5(uniqid(Rand(), true));
$_SESSION['token'] = $data['token'];

しかし、1時間ごとにトークンを生成する方が良いとしましょう。その後、2つのセッションが必要になります。トークン、有効期限、

そして、私はそれをどのようにフォームに進めますか? echo $ _SESSION ['token']を非表示の値フォームに配置し、送信時に比較しますか?

34
John

フォームリクエストごとに行う場合-基本的にCSRF攻撃が発生する機能を削除し、別の一般的な問題を解決できます:複数のフォームの送信

簡単に言えば、アプリケーションは、ユーザーが送信前にフォームを要求した場合にのみフォーム入力を受け入れます。

通常のシナリオ:ユーザーAがWebサイトにアクセスし、フォームAを要求すると、フォームAとフォームAの一意のコードが与えられます。ユーザーがフォームAを送信するとき、ユーザーAは一意のコードを含める必要がありますこれはフォームA専用でした。

CSRF攻撃のシナリオ:ユーザーAがあなたのWebサイトにアクセスし、フォームAを要求します。一方、彼らは別の「悪い」サイトにアクセスし、CSRF攻撃を試みて、偽のフォームBを提出してもらいます。 。

ただし、ウェブサイトは、ユーザーAがフォームBを要求したことはないことを知っています。したがって、ユーザーがフォームAに固有のコードを持っている場合でも、フォームBは拒否されます。フォームBに固有のコードがなく、フォームAのコードのみであるためです。ユーザーは安全で、夜は安眠できます。

しかし、(上記のように)1時間続く汎用トークンとしてそれを行うと、上記の攻撃が機能する可能性があります。その場合、CSRF保護ではあまり達成できません。これは、アプリケーションがフォームBが最初に要求されたことがないことを知らないためです。これは一般的なトークンです。 CSRF防止の全体的なポイントは、各フォームトークンをそのフォームに固有にすることです。

編集:詳細情報を求めたため: 1-フォームリクエストごとに行う必要はありません。1時間/セッションごとに行うことができます。ポイントは、ユーザーに与えられる秘密の値です、そして返品時に再提出されました。この値は別のWebサイトで認識されていないため、偽のフォームを送信できません。

したがって、リクエストごと、またはセッションごとにトークンを生成します。

// Before rendering the page:
$data['my_token'] = md5(uniqid(Rand(), true));
$_SESSION['my_token'] = $data['my_token'];

// During page rendering:
<input type="hidden" name="my_token" id="my_token" value="<? php echo $_SESSION['my_token']?>" />

// After they click submit, when checking form:
if ($_POST['my_token'] === $_SESSION['my_token'])
{
        // was ok
}
else
{
          // was bad!!!
}

そして、それは「フォームごと」なので、最初のフォーム送信後にトークンをワイプできるので、フォームが2回送信されることはありません。

34
Laurence

一般に、ユーザーごとまたはセッションごとに1つのトークンがあれば十分です。トークンは1つの特定のユーザー/セッションにのみバインドされ、グローバルに使用されないことが重要です。

トークンが攻撃サイトに漏えいしたり取得されたりする可能性がある場合(XSSなど)、トークンの有効性を特定の時間枠、特定のフォーム/ URL、または特定の使用量に制限できます。 。

ただし、有効性を制限すると、誤検知が発生し、使いやすさが制限されるという欠点があります。 g。正当なリクエストは、トークンリクエストが古すぎるか、トークンがすでに頻繁に使用されているために、無効化されたばかりのトークンを使用する場合があります。

したがって、私の推奨事項は、ユーザー/セッションごとに1つのトークンを使用することです。さらにセキュリティが必要な場合は、フォーム/ URLごとに1つのトークンをユーザー/セッションごとに使用します。これにより、1つのフォーム/ URLのトークンが漏洩した場合でも、他のトークンは安全です。

13
Gumbo

あなたの質問への答えは、次のとおりです。

また、時限トークンにセッションを使用する必要はありません。サーバー上のサーバー時間と秘密鍵を使用するだけで済みます。

しかし、1時間ごとにトークンを生成する方が良いとしましょう。その後、2つのセッションが必要になります。トークン、有効期限、

いいえ、時間枠のトークンを生成できるルーチンが必要です。 30分ごとに時間を分割するとします。フォームに現在の30分のトークンを1つ作成します。

その後フォームが送信され、トークンを現在および過去30分間に対して検証します。したがって、トークンは30分から1時間まで有効です。

$token = function($tick = 0) use($secret, $hash) {
    $segment = ((int) ($_SERVER['REQUEST_TIME'] / 1800)) + $tick;
    return $hash($secret . $segment);
};
2
hakre

両方の方法を使用できます。

1)リクエストごとにランダムなトークン。同期されていないトークンの問題を解決するには、サーバー送信イベント http://www.w3.org/TR/eventsource/ またはwebsockets http://www.w3 .org/TR/websockets / 各ページのトークンをリアルタイムで更新する通信技術。

2)実装が簡単なトークンをユーザーセッションごとに使用できます(1時間ごとに実行する必要はありません)。同期に問題はありませんが、トークンが強すぎない場合は推測できます。トークンが非常にランダムである場合、悪意のある人が実際に偽造リクエストを行うことは非常に困難です。

追伸最初の方法は最も安全ですが、実装が難しく、より多くのリソースを使用します。

0
Cata Cata