web-dev-qa-db-ja.com

ログインフォームにform_tokenを追加する方法

セキュリティ上の理由から、form_tokenをログインフォームに追加する必要があります。ただし、(hook_form_alterで)同じコードを使用する場合Drupal他のフォームのform.incで使用)、検証中に計算されたトークンが最初に計算されたものと異なるため、検証は失敗します。

私が使用しているコードは次のとおりです。

if (!isset($form['#token'])) {
  $form['#token'] = $form_id;
  $form['form_token'] = array(
    '#id' => drupal_html_id('edit-' . $form_id . '-form-token'),
    '#type' => 'token',
    '#default_value' => drupal_get_token($form['#token']),
    // Form processing and validation requires this value, so ensure the
    // submitted form value appears literally, regardless of custom #tree
    // and #parents being set elsewhere.
    '#parents' => array('form_token'),
  );
}

Drupal_render(drupal_get_form( 'user_login'))でフォームを呼び出しています。 Drupalは頻繁にキャッシュされるため、ログインページでform_tokenを使用していませんが、hook_initでそのページのキャッシュを無効にしています:

$GLOBALS['conf']['cache'] = FALSE 

ログインフォームにform_tokenを追加して検証するにはどうすればよいですか? Drupal 7.を使用しています。

なぜこれが必要なのかに関する追加情報。 CSRFに対してログインフォームを保護するリクエストがセキュリティスキャン中に出されました。リクエストの説明には同意しませんが(そのような場合はログインを自動ブロックするにもかかわらず、ブルートフォース攻撃をセットアップする方が簡単です)、これがなぜ良いアイデアであるのかについてのstackoverflow: ログインフォームにはCSRF攻撃に対するトークンが必要ですか?

4
Whiskey

私は最終的に(推奨されているとおり) hook_form_alter を使用して、すべての保護されていないフォームにトークンを追加しました。

これには drupal_get_token() を使用するのが効率的と思われますが、(匿名の)ユーザーがログインすると、セッションID(drupal_get_token()がトークンを生成するために使用)が変更されるため、機能しませんでした。

したがって、最初のセッションID(ログインページを表示するとき)を$ _SESSIONに保存して、検証時に取得できるようにしました。しかし、$ _ SESSIONに何かを入れると、Drupalがsession_idを再生成するのを防ぐことができます。そして、ログイン中にセッションIDが保持されるため、 drupal_valid_token() も期待どおりに機能するため、特別なトークン検証関数を書く必要はありませんでした。

これは私が終わったコードです:

function MY_MODULE_form_alter(&$form, $form_state, $form_id) {
    // add a custom csrf token for forms that do not have one
    if ($!isset($form['#token'])) {
        $form['anon_token'] = array(
            '#type' => 'token',
            '#default_value' => drupal_get_token()
        );

        $form['#validate'][] = 'MY_MODULE_validate_anon_token';

        // store current session id
        // touching $_SESSION alone seems to preserve the session id after login
        $sess_id = session_id();
        if (isset($_SESSION))
            $_SESSION['anon_session_id'] = $sess_id;
        else
            $_SESSION = array('anon_session_id' => $sess_id);
    }
}

function MY_MODULE_validate_anon_token($form, &$form_state) {
    $token = '';
    if (isset($form_state['values']['anon_token']))
        $token = $form_state['values']['anon_token'];

    if (!drupal_valid_token($token)) {
        // not a valid token!
        $path = current_path();
        $query = drupal_get_query_parameters();
        $url = url($path, array('query' => $query));

        // Setting this error will cause the form to fail validation.
        form_set_error('form_token', t('The form has become outdated. Copy any unsaved work in the form below and then <a href="@link">reload this page</a>.', array('@link' => $url)));
    }
}
4
Whiskey

以前のプロジェクトでも同じユースケースが出てきましたが、匿名ユーザーがフォームからフォームトークンを添付して、サイトからの正当な送信であることを確認する必要がありました。

次のhook_form_alterコードを使用してトークンを追加しました。

  if(!isset($form['#token'])) {
    $form['#token'] = $form_id;
    $form['form_token'] = array(
      '#id' => drupal_html_id('edit-' . $form_id . '-form-token'),
      '#type' => 'token',
      '#default_value' => mymodule_generate_token($form['#token']),
    );
  }

次に、このカスタム関数を使用してトークンを生成しました。

function mymodule_generate_token($form_id){
  $secret = 'my-unique-string'; //could be todays date, whatever you want to make it.
  return drupal_hmac_base64($form_id, $secret. drupal_get_private_key() . drupal_get_hash_salt());
}

次に、匿名フォームからのトークンが有効かどうかを確認するユーティリティ関数も作成しました。

function mymodule_check_valid_token($form_token, $form_id){
  return ($form_token == mymodule_generate_token($form_id));
}

お役に立てれば

2
Alex Kirsten

通常、フォームは認証されたユーザー専用のトークンで作成されます。トークンはセッションにバインドされ、匿名ユーザーに表示されるフォームはcache_formテーブルにキャッシュされ、トークンを割り当てることができません。別の目的で別のトークンが必要な場合は、#tokenキーを使用せずに自分で追加できます。 $form['my_special_token'] = some_tokenを使用してみてください。次に、検証関数を追加して、特別なトークンが有効かどうかを確認します。

いずれの場合も、トークンを追加するための「セキュリティ上の理由」に関する詳細情報が必要です。おそらくそれから、私たちは何をすべきかについてより良い考えを持つことができます。

2
awm