简体   繁体   English

一个令牌与多个令牌以防止CSRF攻击

[英]One token vs. multiple tokens to prevent CSRF attacks

I'm using Codeigniter and I want to prevent CSRF attacks attempts that may happen. 我正在使用Codeigniter,我想防止可能发生的CSRF攻击尝试。 And to achieve this I add a hidden input tag with a random token to each form I want to protect, and in the same time I keep this token in a session to compare against when begin handling this form data. 为了实现这一点,我为每个我想要保护的表单添加了一个带有随机令牌的隐藏输入标记,同时我将这个令牌保存在会话中,以便在开始处理此表单数据时进行比较。

  // set a token to prevent CSRF attacks
  $csrf_token = md5(uniqid(rand(), true));
  $this->session->set_userdata("csrf_token", $csrf_token);

And the form will look like this: 表单将如下所示:

<form action="path/to/handler/page" method="post">
    <input type="text" name="title">
    <input type="text" name="date">
    <textarea name="content"></textarea>
    <input type="hidden" name="csrf_token" value="<?php echo $this->session->userdata("csrf_token") ?>"> 
    <input type="submit" name="submit" value="Save"> 
</form>

And in the page where I handle submitted data I check CSRF attacks something like this: 在我处理提交数据的页面中,我检查CSRF攻击是这样的:

  // make sure there is no CSRF attack attempt
  $csrf_token = $this->session->userdata("csrf_token");
  if (empty($csrf_token) || $csrf_token !== $this->input->post("csrf_token")) {
    die("Some message here!!");
  }

And that works pretty good. 这非常有效。 But as you see I generate a random token for each page containing a form and in some cases this causes a problem if for example I opened another tab in the browser to perform some another action. 但正如您所见,我为包含表单的每个页面生成一个随机令牌,在某些情况下,如果我在浏览器中打开另一个选项卡以执行其他操作,则会导致问题。 Consider this scenario: 考虑这种情况:

  1. I opened add.php page to add a new item. 我打开add.php页面添加一个新项目。
  2. I filled in all required data, but I didn't submit the form. 我填写了所有必需的数据,但我没有提交表格。
  3. Now I decided to open edit.php page in another tab in the browser to edit an existing item. 现在我决定在浏览器的另一个选项卡中打开edit.php页面来编辑现有项目。
  4. Then I went back to the add.php page that was filled in and tried to submit the data. 然后我回到填写的add.php页面并尝试提交数据。

At this point I will get an error because value of the token that has been stored in the session when I opened add.php page has been changed and replaced with another token when I open the edit.php page. 此时我将收到一个错误,因为我打开add.php页面时已存储在会话中的令牌值已更改,并在我打开edit.php页面时替换为另一个令牌。 So how can I fix this problem? 那么我该如何解决这个问题呢? Should I generate just one token for each user when he successfully login and then use this token in all pages that he may deal with? 我是否应该在成功登录时为每个用户生成一个令牌,然后在他可能处理的所有页面中使用此令牌? Does this approach has any risks or any cons? 这种方法有任何风险或缺点吗?

I scanned your post and don't see a reason why not to use the base codeigniter CSRF protection? 我扫描了你的帖子,没有看到为什么不使用基本codeigniter CSRF保护的原因? It seems you are reinventing the wheel and creating problems that don't exist within its standard implementation. 您似乎正在重新发明轮子并创建标准实现中不存在的问题。

Not to mention you are violating DRY principles by trying to print your tokens to every form. 更不用说你试图将你的代币打印到每种形式都违反了DRY原则。 Any reason why not to keep it simple? 有什么理由不保持简单?

Codeigniter has build in CSRF protection that can be enabled in /application/config/config.php Codeigniter内置了CSRF保护,可以在/application/config/config.php中启用

$config['csrf_protection'] = TRUE;
$config['csrf_token_name'] = 'csrf_token_name';
$config['csrf_cookie_name'] = 'csrf_cookie_name';
$config['csrf_expire'] = 7200;

To solve this problem, you could create a token string with a unique key and store the pairs of keys/tokens in the session (as userdata in CodeIgniter). 要解决此问题,您可以使用唯一键创建一个令牌字符串,并在会话中存储键/令牌对(作为CodeIgniter中的userdata)。

Considering this scenario, You'll need these steps: 考虑到这种情况,您需要以下步骤:

  1. Creating a unique token key. 创建唯一的令牌密钥。
  2. Creating a token string. 创建令牌字符串。
  3. Storing the key/token pair in the session (userdata). 在会话中存储密钥/令牌对(userdata)。
  4. Creating 2 hidden <input> elements for CSRF key and token. 为CSRF密钥和令牌创建2个隐藏的<input>元素。
  5. Validating the posted key/token by checking the existence of key/token in the session. 通过检查会话中是否存在密钥/令牌来验证发布的密钥/令牌。

Getting to work: 上班:

$csrf_key   = "TOKEN_" . mt_rand(0, mt_getrandmax());
$csrf_token = hash("sha512", mt_rand(0, mt_getrandmax()));
// Store the key/token pair in session
$this->session->set_userdata($csrf_key, $csrf_token);

Adding hidden input s: 添加隐藏input

<form action="path/to/handler/page" method="post">
    <!-- form input elements -->
    <input type="hidden" name="csrf_key" value="<?php echo $csrf_key; ?>">
    <input type="hidden" name="csrf_token" value="<?php echo $this->session->userdata($csrf_key); ?>">
</form>

Validating the posted key/token: 验证发布的密钥/令牌:

if (count($_POST)) {
    if (! isset($_POST['csrf_key']) or ! isset($_POST['csrf_token'])) {
        die('No CSRF token found, invalid request.');     
    }

    $key   = $this->input->post('csrf_key');
    $token = $this->input->post('csrf_token');

    if ($token !== $this->session->userdata($key)) {                
        die('Invalid CSRF token, access denied.');
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM