我在一些MVC应用程序中看到使用令牌密钥来防止CSRF。它可以在哪里使用的一个典型例子是关于帖子的删除方法。
我看到了同时使用GET和POST方法的实现。
带有令牌的GET请求链接示例:
https://domain.com/posts/G7j/delete/EOwFwC4TIIydMVUHMXZZdkbUR0cluRSkFzecQy3m5pMTYVXRkcFIBWUZYLNUNSNgQKdnpTWu
还有一个带有令牌的POST请求示例:
<form action="/posts/G7j/delete" method="post">
<input type="hidden" name="token" value="EOwFwC4TIIydMVUHMXZZdkbUR0cluRSkFzecQy3m5pMTYVXRkcFIBWUZYLNUNSNgQKdnpTWu" />
<button type="submit">Delete</button>
</form>
我一直在考虑基于以下文档将其实现到我的CakePHP应用程序中:http://book.cakephp.org/2.0/en/core-libraries/components/security-component.html
根据文档,添加Security组件会自动将表单密钥添加到所有使用form Helper的表单中。
例如
public $components = array(
'Security' => array(
'csrfExpires' => '+1 hour'
)
);
但是我有一些问题:
1.)为什么要对一些操作(如删除)使用POST而不是GET?由于控制器中的请求将检查用户是否经过身份验证、是否具有权限以及是否具有正确的表单密钥。
2.)如何在CakePHP中使用GET请求的安全组件?我想我也需要处理路由。
首先,CakePHP使用post链接进行删除,作为一种额外的安全级别,因为例如,您的身份验证系统不是100%安全的,用户可以通过手动键入URL来访问删除方法-如果我进入并键入/users/delete/10,我实际上可以删除您服务器上的用户,这正如您所想象的那样是有风险的。
其次,GET请求可以被缓存或添加书签,因此为这些链接添加书签的用户最终可能会导航到断开的链接,这从来都不是一件好事,而且敏感数据也会在URL中可见,例如,如果有人在登录页面上添加了完整的GET变量书签,这可能会危及安全性。
最后,您可以使用以下代码轻松生成自己的代币:
$string = Security::hash('string', 'sha1 or md5', true);
print $this->Html->link("Link to somewhere",array("controller"=>"users","action"=>"delete",$string));
以上内容将使用核心配置文件中的salt键设置,但您也可以用自定义salt替换true。
对于第一个问题:
原因可能在于HTTP方法的定义。GET
被定义为安全方法之一,这意味着它不能用于更改服务器的状态,而只能用于检索信息。您可以在此链接上阅读有关HTTP方法的更多信息。由于HTML表单无法发送HTTP DELETE
请求,因此"解决方法"是使用一些可用的方法,如果您排除了GET
作为"安全方法"的可能性,则会留下POST
。当然,您可以使用GET
来删除内容,许多人都这样做,但按照惯例,GET
请求不会更改任何内容。
编辑:如果您有兴趣阅读更多关于HTTP方法和浏览器/HTML支持的信息,请查看此SO问题
johhniedoe向我介绍了Croogo 1.3,以了解它们是如何做的。因为1.3是针对2.x之前的CakePHP的,所以代码有点过时,所以我对它进行了如下修改:
首先创建链接(在本例中为删除链接),其中安全组件使用的CSRF令牌作为一个名为token
的参数传递。
<?php echo $this->Html->link('Delete', array('action'=>'delete','token'=>$this->params['_Token']['key'])); ?>
接下来,我创建了一个通配符路由连接来处理令牌(这通常不是必要的,但因为我们没有使用NAMED令牌,我想保持URL更干净):
Router::connect('/:controller/:action/:token',
array('controller' => 'action'),
array(
'pass' => array('token')
)
);
然后最后用你的方法这样处理:
public function delete(){
if (!isset($this->params['token']) || ($this->params['token'] != $this->params['_Token']['key'])) {
$this->Security->blackHoleCallback = '__blackhole';
} else {
// do delete
}
}
这基本上是说,如果令牌不匹配,那么使用黑洞回调函数__blackhole
,我在AppController中定义它来显示错误。
不过,最后需要注意的是,您必须允许令牌使用一次以上,例如持续一个小时,这是因为否则,在发送GET请求后,令牌将被重置,不再匹配。
public $components = array(
'Security' => array(
'csrfExpires' => '+1 hour',
'csrfUseOnce' => false
)
);