【问题标题】:Preventing form_token from rendering in Drupal "GET" forms防止 form_token 在 Drupal“GET”表单中呈现
【发布时间】:2009-09-30 10:36:47
【问题描述】:

Drupal 在呈现表单时会插入一个 form_token 作为隐藏字段。然后在表单提交时检查 form_token 以防止跨站点请求伪造攻击。提交的表单数据保证来自 Drupal 呈现的原始表单。

但是,使用“GET”方法的表单不需要此标记。它所做的只是延长和丑化生成的 URL。

有没有办法抑制它?

【问题讨论】:

标签: drupal drupal-6


【解决方案1】:

是的,有办法,但有意识地使用它(见下面的警告):

如果你自己创建表单,添加

$form['#token'] = FALSE;

到表单定义数组应该首先防止生成令牌。

如果您正在处理现有表单,则可以通过取消设置 hook_form_alter 上的“#token”元素来绕过令牌验证过程:

// Example for removal of token validation from login (NOTE: BAD IDEA!)
function yourmodule_form_alter(&$form, &$form_state, $form_id) {
  if ($form_id == 'user_login_block') {
    unset($form['#token']);
  }
}

警告:鉴于您的问题,我认为对 GET 和 POST 请求之间的区别(更好的是,没有区别)存在一些误解。

... 在使用“GET”方法的表单上 不应该需要这个令牌。它所做的一切 是加长和丑化结果 网址。

这是错误的! GET 和 POST 只是两种不同的,但大部分是等效的方法,用于将数据从客户端传输到服务器。由于 POST 更适合传输大量数据(或难以格式化的数据),因此它是提交表单的既定标准,但它绝不比 GET 请求更安全/不安全或更安全/更不安全。这两种类型的请求都可以被恶意用户以相同的方式篡改,因此两种类型都应该使用相同的保护机制

对于 GET 请求,令牌的作用与 POST 请求完全相同 - 它向服务器证明提交的数据与他构建的请求来自同一台机器上的同一浏览器表格!因此,只有在确定不会通过 XSRF 滥用该请求时才应将其删除。

【讨论】:

  • GET 和 POST 之间的主要区别在于,根据 HTTP 规范,GET 应该是“安全的”。这意味着 XSRF 攻击不是什么大问题 - 欺骗用户的浏览器发出 GET 请求无论如何都不应该导致服务器上的任何状态更改。这就是为什么我认为 form_token 是多余的。
  • 没错——理论上 :) 这里的重要词是“应该是”和“不应该”。不同之处只是一个建议/最佳实践,但没有什么能阻止您或其他开发人员使用 GET 请求来获取“不安全”的东西,而且它经常出于方便而进行,因为一个简单的链接比表单实现得更快、更轻量级.但是可以肯定的是,如果您知道 GET 所做的事情是无害的,请删除令牌 - 我只是想强调 GET 不是“固有”安全的,并且删除令牌处理应该是一个有意识的决定,而不是自动发生。
  • 也就是说,鉴于您的问题,我认为我将这一点延伸了一点 - 我相应地编辑了答案,抱歉有点过火了;)
  • 我并没有真正在问题中说清楚。感谢您的解释和完善您的答案。
【解决方案2】:

这对我有用。我必须取消设置所有表单 api 元素并将 #token 属性设置为 false。注意用于取消设置其他属性的 after_build 函数。

   function mymodule_form(&$form_state){
      $form['name'] = array(
        '#type'   => 'textfield',
        '#title'  => 'name',
        '#value'  => 'name', 
      );
      $form['#method'] = 'get';
      $form['#action'] = url('someurl');
      $form['submit'] = array('#type' => 'submit', '#value' => 'go');
      $form['#token'] = false;
      $form['#after_build'] = array('mymodule_unset_default_form_elements');
      return $form;
    }

    function mymodule_unset_default_form_elements($form){
      unset($form['#build_id'], $form['form_build_id'], $form['form_id']);
      return $form;
    }

【讨论】:

  • 谢谢,对我来说很好用。如果要删除提交时附带的 op=,只需将 '#attributes' => array('name' => '') 添加到提交数组即可。
【解决方案3】:

我工作的站点使用 Drupal 6 表单 API 来自定义搜索表单,因此通过删除令牌和构建 ID,我们能够将结果缓存在内存缓存中。现在我们已经转移到 Acquia 托管,它使用 Varnish 进行缓存。

要从表单中删除 form_token 和 form_build_id 并将其作为 GET 请求提交,请使用以下方法:

<?php

function module_example_form($form_state, $form_id = NULL) {
  // Form root settings.
  $form = array();

  // Set the submission callback for this form. 
  $form['#submit'][] = __FUNCTION__ . '_submit';

  // Set the request method for this form to GET instead of the default
  // of POST.
  $form['#method'] = 'get';

  // Remove unique form token so request can be cached. This is accompanied by
  // code in hook_form_alter to ignore the token and remove the build_id.
  $form['#token'] = FALSE;

  // Submit button.
  $form['go'] = array(
    '#type' => 'submit',
    '#value' => t('Go!'),
  );

  return $form;
}

/**
 * Implements hook_form_alter().
 */
function module_form_alter(&$form, $form_state, $form_id) {
  // Changes to the 'module_example_form' form.
  if ($form_id == 'module_example_form') {
    // Unset the hidden token field and form_build_id field.
    unset($form['#token'], $form['form_build_id'], $form['#build_id']);
  }
}

?>

【讨论】:

    【解决方案4】:

    我发现简单地丢弃 CSRF 令牌不是一种选择。我们使用hook_theme_registry_alter() 覆盖Drupal 核心theme_hidden() 函数解决了这个问题,这样隐藏的表单元素'form_token' 被呈现为&lt;esi /&gt; 标记。该标签将导致 Varnish 调用我们允许通过缓存的 PHP 文件。该文件将为当前用户计算正确的表单标记,然后输出隐藏字段的 HTML 代码。您可以不使用 Drupal 引导程序来计算此令牌,但您需要一个数据库查询来获取您网站的 *drupal_private_key*,它存储在 变量表。

    【讨论】:

      猜你喜欢
      • 2016-01-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多