【问题标题】:csrf hash token behaviour when website is open in multiple tabs在多个选项卡中打开网站时的 csrf 哈希令牌行为
【发布时间】:2012-01-11 02:47:30
【问题描述】:

我正在尝试集成一种方法来为我的应用程序中的每个表单添加一个哈希令牌。我想以此实现目标:

  1. 防止 csrf 攻击
  2. 防止用户在提交表单后重新加载页面时重新提交表单

现在,我认为这样做的概念应该很简单:

  • 我生成一个唯一的哈希并将其保存到 cookie 中
  • 我使用生成的哈希在表单中创建了一个隐藏字段
  • 在处理表单中的 $_POST 数据之前,我会验证表单中的哈希值是否与 cookie 中的值匹配。

到目前为止一切顺利,现在我真正陷入困境的是以下场景:

如果用户使用我的应用程序打开另一个选项卡会怎样。每次加载页面时,都会重新生成哈希值。从而使第一个选项卡中表单的哈希值无效。

【问题讨论】:

  • 为每个用户存储一个当前有效令牌的列表怎么样?

标签: forms csrf


【解决方案1】:

我会独立实现这些功能:

CSRF 预防

使用 CSRF 令牌来防止成功的 CSRF 攻击:为每个会话或每个 URL/表单生成一个 随机 令牌并将其存储在会话中。最初请求表单时,放置(关联的)CSRF 令牌并在稍后处理提交的表单数据时对其进行验证。

由于 CSRF 令牌是由服务器生成并存储在服务器上的,因此它不能被攻击站点伪造,并且仅对该会话有效。同样,验证也是微不足道的。唯一剩下的风险是攻击站点可以通过跨站点脚本获得已发行的 CSRF 令牌(但这会使 CSRF 在某种程度上过时)。

防止多次提交表单

使用一次有效的令牌来防止多次提交:为每个初始表单请求生成一个唯一令牌并将其放入表单中。在提交表单时,检查该令牌是否已被使用并记住它是使用过的。

要仅允许您的服务器发布的令牌,您可以使用某种message authentication algorithm。为了减少要记住的令牌数量,您可以让令牌在某个时间过期,这样您只需要记住特定时间范围内的令牌即可。

多个选项卡或表单请求没有问题:CSRF 令牌在整个会话期间或对于特定 URL/表单的任何表单提交都有效。而另一个令牌只对单个表单提交有效。

【讨论】:

    【解决方案2】:

    不要将令牌存储为 cookie。

    在每次页面展示时生成一个唯一令牌。在 POST 后,验证您是否向该用户颁发了令牌,并验证它之前没有被使用过。

    首先,我们生成一个令牌,我们可以在其中验证我们是否为给定用户颁发了它:

    token = hash(session_id + secret)

    这样,使用他们的会话 ID 和我们的秘密,我们始终可以验证我们是否发布了该令牌,因为没有人知道秘密。

    现在我们需要确保令牌只能使用一次。

    rnd = rand()
    token = rnd + hash(session_id + secret + rnd)
    

    令牌现在有一个随机数。当 POST 发生时,我们可以将此随机数存储为“之前已使用过”,并拒绝任何重复使用相同随机数的令牌。

    但我们不想永远存储使用过的令牌的随机数。所以我们限制了令牌的生命周期。

    rnd = rand()
    now = time()
    token = rnd + time + hash(session_id + secret + rnd + time)
    

    在 POST 上,当我们得到一个令牌时,我们现在检查我们是否“最近”发出了它。我们只需要为相同的时间跨度存储使用过的随机数。根据定义,所有旧令牌都是无效的。

    您可以将使用的随机数与会话 ID 一起保护,并在您驱逐会话 ID 或它们变得无效时删除它们(以先发生者为准)。

    【讨论】:

    • 这是一个很好的、彻底的答案。然而;其中大部分不是针对实际问题,即如何使代码对多标签浏览友好。您几乎顺便提到它,并且主要描述(很好,正确,imo)如何创建代码......这不是OP所要求的。
    • 非常感谢您非常详细的回答。我已经尝试过您的方法,但我遇到了一个问题:如果我为每个请求保存一个哈希 - > 时间戳对,并让它们在假设 60 分钟后过期,那么用户会话中可能有数百个这样的哈希值。我不确定我可以在会话中保存多少数据,但这样我会弄乱会话。
    • 对于随机值,您需要 - 比如说 - 4 个字节。对于时间戳,另外 4 个字节。每个 POST 请求总共 8 个字节。数百个这些值仍然不是很多数据。如果您不习惯将其存储在会话中,请将其放在其他地方。 Memcached 可能是一种选择。顺便说一句,您应该知道会话数据的存储位置。查找并学习。
    • Andrew:他正在寻找 CSRF 保护。他最初的方法对多标签浏览并不友好,因为它依赖于 cookie。我展示了一种他不需要 cookie 的方法,因此“对多标签浏览很友好”。我只是描述了这种方法是如何工作的以及它为什么工作,为了清楚起见使用伪代码。我不明白这有什么问题。
    • 我将会话保存为 mysql 数据库中的 TEXT 字段。目前我使用一个 32 字符长的哈希键,你说的时间戳是 4 个字节。所以一个哈希 => 时间戳对将是 36 个字节。 TEXT 类型的 mysql 字段最多允许 65535 个字节。因此,如果我们假设只使用会话作为哈希键,我可以在空间不足之前保存大约 1820 个哈希键。那不是很多。另一种方法是将散列键的数量限制为 20,如果达到该数量,则删除第一个键。
    猜你喜欢
    • 1970-01-01
    • 2021-02-01
    • 2021-02-07
    • 1970-01-01
    • 1970-01-01
    • 2017-11-28
    • 1970-01-01
    • 2017-05-19
    • 2018-05-23
    相关资源
    最近更新 更多