首先,我想确保我获得了正确的CSRF令牌工作流程。 服务器在我的机器上,在站点的域上设置cookie。浏览器阻止从其他域访问此cookie。发出POST请求时,我将CSRF令牌发送到服务器,然后将其与我的cookie进行比较。它们不一样,返回403 Forbidden页面。
现在,如果我手动更改cookie中令牌的值并在POST请求中发送该新值,服务器是否应返回403?服务器是否需要再次验证存储在服务器或cookie上的值?
我在Django 1.3(https://docs.djangoproject.com/en/1.3/ref/contrib/csrf/)上使用CSRF保护的默认实现,并且它仅针对令牌验证请求中发送的令牌。
答案 0 :(得分:2)
你如何发送令牌?
通常,令牌应该是cookie的一些功能(使用密钥 - 只有服务器知道,例如MAC)!不是饼干。
比流程如下: 1.客户端使用cookie发送服务器请求。 2.服务器返回具有CSRF令牌的网页用于不同目的(例如,表单或仅通过URL的简单获取请求)。 3.客户端执行一些操作(通过POST或GET)并使用令牌(在请求正文或URL中)和cookie发送请求。 4.服务器是无状态的,但它可以通过计算cookie(或部分)上的函数(使用服务器知道的密钥)验证请求是由同一客户端发送的,并将输出与令牌。
对于CSRF,cookie会自动附加到浏览器的请求中,但攻击者(可能甚至不知道cookie)无法添加相应的令牌。
我相信你应该这样做。
现在,如果我手动更改cookie中令牌的值,那么 如果服务器返回a,则在POST请求中发送新值 403还是没有?服务器是否需要再次验证令牌值 存储在服务器上还是cookie上?
服务器应该是无状态的(通常)。您不希望针对数据库中的某些值或类似值验证每个请求的令牌。最好根据cookie进行验证。 在这种情况下,如果您更改令牌,那么它可能与cookie不匹配,您应该发送403。
答案 1 :(得分:1)
TL; DR:是的,您或您正在使用的框架需要具有服务器端逻辑来验证CSRF令牌。它不能是cookie,它必须是要求用户在您的页面上的东西,而不是点击攻击者提供的链接。
你的工作流程非常正确。第一步是生成无法由攻击者预测的加密随机字符串。每种编程语言都有自己的构造来执行此操作,但是一个24 - 32字符的字符串应该可以很好地满足此目的。
在我们进入下一步之前,让我们确定我们知道我们正在处理的威胁 - 我们不希望攻击者代表用户提出请求,因此应该有一些可以访问的内容需要用户执行操作以发送令牌的浏览器,但是,如果用户点击了攻击者已设置的内容,则不应发送令牌。
鉴于此,不应该做的一种方法是使用cookie。浏览器会在每次向cookie设置的域发出请求时自动发送cookie,因此这会自动阻止我们的辩护。
那就是说,让我们进入下一步,即以可由服务器端验证的方式设置此令牌,但攻击者无法访问。有多种方法可以做到这一点:
1) CSRF标头:这是在许多node.js / Express安装中完成的 - CSRF令牌作为标头发送,具体而言,是X-CSRF-Token标头。生成此令牌后,服务器会将其存储在该特定cookie的会话存储中。在前端,令牌存储为JavaScript变量,这意味着只有在该特定页面上生成的请求才能具有标头。每当发出请求时,都会话cookie(在node.js的情况下,连接。 sid)并且所有POST / PUT / DELETE请求都需要X-CSRF-Token。如果发送了错误的令牌,服务器将发送401 Unauthorized,并重新生成令牌,请求用户登录。
<script type="text/javascript">
window.NODE_ENV = {};
window.NODE_ENV.csrf = "q8t4gLkMFSxFupWO7vqkXXqD";
window.NODE_ENV.isDevelopment = "true";
</script>
2)隐藏的表单值:许多PHP安装使用它作为CSRF防御机制。根据配置,特定于会话或特定于请求(后者是过度杀伤,除非应用程序需要它)令牌嵌入在隐藏表单字段中。这样,每次提交表单时都会发送它。验证方法各不相同 - 可以通过对数据库进行验证,也可以是服务器特定的会话存储。
3) Double Submit Cookies :这是OWASP建议的机制,除了通过标题发送会话Cookie之外,您还将其包含在提交的表单中。这样,一旦验证会话有效,您就可以验证表单是否也包含会话变量。如果使用此机制,确保在验证CSRF之前验证用户的会话至关重要;否则,它会引入缺陷。
在构建/测试此机制时,重要的是要注意虽然许多实现将其限制为POST / DELETE / PUT事务,但这是因为自动假设所有敏感事务都通过此动词发生。如果您的应用程序使用GET执行敏感事务(例如激活),那么您还需要GET / HEAD的这种机制。