我想知道这是否是设置令牌的安全方式,除非实际存在令牌,我生成一个令牌,并在整个应用程序和那些表单中使用它。每个会话一个令牌?
if (!isset($_SESSION['token'])) {
$data['token'] = uniqid(rand(), true);
session_regenerate_id();
$_SESSION['token'] = $data['token'];
}
是否有必要在提交的表单上清除令牌?或者只是坚持下去,即使我提交了一份表格?
答案 0 :(得分:11)
答案 1 :(得分:10)
我个人会为我想要显示的每个表单生成一个新标记。如果你这样做,只要会话保持活动状态,有人只需要一个会话cookie来读取你的令牌并使用它。
在我的应用程序中,我为每个表单显示生成一个令牌,如下所示:
<?php
$token = uniqid(rand(), true);
$_SESSION['csrf_tokens'][$token] = true;
<强> HTML 强>
<form>
<input type="hidden" name="token" value="<?php echo $token ?>" />
</form>
在表单验证时,我会检查这个令牌:
if (isset($_SESSION['csrf_tokens'][$token]) && $_SESSION['csrf_tokens'][$token] === true) {
unset($_SESSION['csrf_tokens'][$token]);
// additional code here
}
答案 2 :(得分:2)
除了使用per-session token
我更希望per-form/url token
获得额外的安全性,有些人可能会认为per-request token
最安全,但会影响可用性。
我还认为最好将会话存储与令牌存储分开并使用Memcache
之类的内容。当您需要使用多个应用程序服务器等速度时,这会更好。我也更喜欢它,因为我可以添加自定义expiration to the token
而不必影响整个session
这是一个典型的例子
<强> HTML 强>
<form method="POST" action="#">
IP:<input type="text" name="IP" /> <input type="hidden" name="token"
value="<?php echo Token::_instance()->generate(); ?>" /> <input
type="Submit" value="Login" />
</form>
处理
$id = "id44499900";
Token::_instance()->initialise($id); // initialise with session ID , user ID or IP
try {
Token::_instance()->authenticate();
// Process your form
} catch ( TokenException $e ) {
http_response_code(401); // send HTTP Error 401 Unauthorized
die(sprintf("<h1>%s</h1><i>Thief Thief Thief</i>", $e->getMessage()));
}
使用的课程
class Token {
private $db;
private $id;
private static $_instance;
function __construct() {
$this->db = new Memcache();
$this->db->connect("localhost");
}
public static function _instance() {
self::$_instance === null and self::$_instance = new Token();
return self::$_instance;
}
public function initialise($id) {
$this->id = $id;
}
public function authenticate(array $source = null, $key = "token") {
$source = $source !== null ? $source : $_POST;
if (empty($this->id)) {
throw new TokenException("Token not Initialised");
}
if (! empty($source)) {
if (! isset($source[$key]))
throw new TokenException("Missing Token");
if (! $this->get($this->id . $source[$key])) {
throw new TokenException("Invalid Token");
}
}
}
public function get($key) {
return $this->db->get($key);
}
public function remove($key) {
return $this->db->delete($key);
}
public function generate($time = 120) {
$key = hash("sha512", mt_rand(0, mt_getrandmax()));
$this->db->set($this->id . $key, 1, 0, $time);
return $key;
}
}
class TokenException extends InvalidArgumentException {
}
注意:请注意,该示例可能会影响“后退”按钮或刷新,因为令牌会在
120
秒后自动删除,这可能会影响用户友好功能
答案 3 :(得分:2)
我想知道这是否是设置令牌的安全方式
这取决于您的网络应用程序需要多么安全。此行不具有加密安全性(正如在uniqid()和rand()的PHP文档中所警告的那样:
uniqid(rand(), true);
如果已知/确定了令牌生成的时间并且已知/确定了rand()种子,则攻击者可以确定/强制执行此操作。但是,出于您的目的,它可能会很好,因为它仍会阻止攻击者不知道令牌值的CSRF攻击。
每个会话一个令牌?
每个会话使用一个令牌可能适用于您的目的。但请注意:
是否有必要在提交的表单上清除令牌?或者只是留下来,即使我提交了一份表格?
这取决于您的应用程序针对攻击者的目标值以及攻击导致您的中断程度。您现有的措施使得执行CSRF攻击变得困难,但如果它具有很高的价值并且您拥有非常坚定的攻击者,那么您可能希望通过以下方式降低CSRF的风险:
答案 4 :(得分:1)
您可以参考以下网站,这可能会有一些想法。
1。)https://docs.djangoproject.com/en/dev/ref/contrib/csrf/
2。)http://blog.whitehatsec.com/tag/session-token/
感谢您的回复。
答案 5 :(得分:1)
我已经在另一个论坛上回答了类似的问题:here。希望这很有帮助。它解释了CSRF预防的基本过程,并链接到CSRF框架的一些代码。
如果您想要更高的安全性,请在每次会话的每次请求后更改令牌。如果您想要更好的可用性,请在每个会话中保留一个令牌。