web-dev-qa-db-ja.com

PHPを使用してCSRFトークンを適切に追加する方法

Webサイトのフォームにセキュリティを追加しようとしています。フォームの1つはAJAXを使用し、もう1つは単純な「お問い合わせ」フォームです。CSRFトークンを追加しようとしています。私が抱えている問題は、トークンが表示されるだけですHTMLの「値」の中にある場合があります。それ以外の場合、値は空です。AJAXフォームで使用しているコードは次のとおりです。

PHP:

if (!isset($_SESSION)) {
    session_start();
$_SESSION['formStarted'] = true;
}
if (!isset($_SESSION['token']))
{$token = md5(uniqid(Rand(), TRUE));
$_SESSION['token'] = $token;

}

HTML

 <form>
//...
<input type="hidden" name="token" value="<?php echo $token; ?>" />
//...
</form>

助言がありますか?

74
Ken

セキュリティコードについては、次のようにトークンを生成しないでください:$token = md5(uniqid(Rand(), TRUE));

これを試してください:

CSRFトークンの生成

PHP 7

_session_start();
if (empty($_SESSION['token'])) {
    $_SESSION['token'] = bin2hex(random_bytes(32));
}
$token = $_SESSION['token'];
_

サイドノート: 私の雇用主のオープンソースプロジェクト の1つは、random_bytes()およびrandom_int()をPHP 5プロジェクトにバックポートするイニシアチブです。 MIT Githubでライセンスされており、利用可能です。Composer as paragonie/random_compat

PHP 5.3以降(またはext-mcryptを使用)

_session_start();
if (empty($_SESSION['token'])) {
    if (function_exists('mcrypt_create_iv')) {
        $_SESSION['token'] = bin2hex(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM));
    } else {
        $_SESSION['token'] = bin2hex(openssl_random_pseudo_bytes(32));
    }
}
$token = $_SESSION['token'];
_

CSRFトークンの検証

_==_または_===_を使用するだけでなく、 hash_equals() を使用してください(PHP 5.6+のみですが、 hash-compat library)。

_if (!empty($_POST['token'])) {
    if (hash_equals($_SESSION['token'], $_POST['token'])) {
         // Proceed to process the form data
    } else {
         // Log this as a warning and keep an eye on these attempts
    }
}
_

フォームごとのトークンでさらに進む

hash_hmac() を使用して、特定のフォームでのみ使用できるようにトークンをさらに制限できます。 HMACは、より弱いハッシュ関数(例:MD5)でも安全に使用できる特定のキー付きハッシュ関数です。ただし、代わりにハッシュ関数のSHA-2ファミリを使用することをお勧めします。

最初に、HMACキーとして使用する2番目のトークンを生成してから、次のようなロジックを使用してレンダリングします。

_<input type="hidden" name="token" value="<?php
    echo hash_hmac('sha256', '/my_form.php', $_SESSION['second_token']);
?>" />
_

そして、トークンを検証するときに一致する操作を使用します。

_$calc = hash_hmac('sha256', '/my_form.php', $_SESSION['second_token']);
if (hash_equals($calc, $_POST['token'])) {
    // Continue...
}
_

_$_SESSION['second_token']_を知らないと、1つのフォーム用に生成されたトークンを別のコンテキストで再利用できません。 ページにドロップしたばかりのトークンとは別のトークンをHMACキーとして使用することが重要です。

ボーナス:ハイブリッドアプローチ+ Twig Integration

Twigテンプレートエンジン を使用する人は誰でも、このフィルターをTwig環境に追加することにより、単純化された二重戦略の恩恵を受けることができます。

_$twigEnv->addFunction(
    new \Twig_SimpleFunction(
        'form_token',
        function($lock_to = null) {
            if (empty($_SESSION['token'])) {
                $_SESSION['token'] = bin2hex(random_bytes(32));
            }
            if (empty($_SESSION['token2'])) {
                $_SESSION['token2'] = random_bytes(32);
            }
            if (empty($lock_to)) {
                return $_SESSION['token'];
            }
            return hash_hmac('sha256', $lock_to, $_SESSION['token2']);
        }
    )
);
_

このTwig関数では、両方の汎用トークンを次のように使用できます。

_<input type="hidden" name="token" value="{{ form_token() }}" />
_

または、ロックダウンされたバリアント:

_<input type="hidden" name="token" value="{{ form_token('/my_form.php') }}" />
_

Twigはテンプレートのレンダリングのみに関係しています。トークンを適切に検証する必要があります。私の意見では、Twig戦略は最大限のセキュリティの可能性を維持しながら、より高い柔軟性とシンプルさを提供します。


使い捨てCSRFトークン

各CSRFトークンを1回だけ使用できるというセキュリティ要件がある場合、検証が成功するたびに最も単純な戦略でトークンが再生成されます。ただし、そうすると、以前のすべてのトークンが無効になり、複数のタブを一度に閲覧するユーザーとうまく混合できません。

Paragon Initiative Enterprisesは、これらのコーナーケースに対して Anti-CSRFライブラリ を維持しています。これは、1回限りのフォームごとのトークンでのみ機能します。十分なトークンがセッションデータに保存されると(デフォルト構成:65535)、最も古い未使用のトークンが最初にサイクルアウトされます。

242

セキュリティ警告md5(uniqid(Rand(), TRUE))は、乱数を生成する安全な方法ではありません。詳細については、 この回答 をご覧ください。

Ifでelseが必要なようです。

if (!isset($_SESSION['token'])) {
    $token = md5(uniqid(Rand(), TRUE));
    $_SESSION['token'] = $token;
    $_SESSION['token_time'] = time();
}
else
{
    $token = $_SESSION['token'];
}
23
datasage

変数$tokenは、セッション中にセッションから取得されていません

1
Dani