若转载教程,请注明出自SW-X框架官方文档
6、什么是Token令牌
之前我们想到了一个方法生成一段随机字符串放在每个POST、GET请求中,这样可以有效的防止CSRF攻击,而这段随机字符串就是ToKen令牌。
7、手把手教学之四,实现Token令牌防御表单重复多次提交函数封装
Token令牌即使上除了可以防止CSRF攻击之外,还可以防止用户通过F5刷新页面达到重复提交相同的内容到数据库中。
Token令牌的生成流程如下:
1、生成一段32位的随机字符串
2、将这段字符串保存到一个名为csrf_token的session中
Token令牌的验证流程如下:
1、获取POST或GET请求中,是否有一个叫csrf_token的节点
2、session中的csrf_token为空则直接验证失败
3、获得请求中的csrf_token节点 与 session中的csrf_token做对比,成功则通过
4、不管对比成功或失败,都要清空session中的csrf_token
按正常情况下,我们应该一个函数就完成上面的两种生成和验证的功能,朋友们可以自己先思考完成,然后再对比下面的案例代码:
本次案例主要有3个文件:
1、index.php文件,用于创建测试的form表单,发起GET请求
2、demo.php文件,用于接收form表单提交的数据,同时检查表单中的csrf_token是否合法
3、token.php文件,存放csrf_token生成跟检查的函数
index.php文件代码如下:
<?php
# 引用csrf_token令牌函数
require_once 'token.php';
?>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<h2>提交之后,按住F5,看能不能刷新重复提交</h2>
<hr/>
<form method="GET" action="demo.php">
<?php echo csrf_token(1);?>
随便输入:<input type="type" name="content">
<br/>
<br/>
<input type="submit" value="提交校验">
</form>
</body>
</html>
demo.php文件代码如下:
<?php
# 引用csrf_token令牌函数
require_once 'token.php';
# 验证csrf_token令牌
$vif = csrf_token(false);
if (!$vif) {
die('csrf_token攻击验证未通过');
}
$content = $_GET['content'];
echo "你提交的内容:$content";
token.php文件代码如下:
<?php
/**
* 生成与验证csrf_token令牌
* @param int|bool $type 当为false时,则是验证Token,为1时返回表单结构,为2时返回csrf_token随机字符串
* @return string|bool Token结构 或 验证结果
*/
function csrf_token($type){
# 先开启SESSION
isset($_SESSION) || session_start();
# 验证码Token
if ($type === false) {
# 现在检测有没有创建过csrf_token
if (empty($_SESSION['csrf_token'])) {
return false;
}
# 收集表单中关于csrf_token的信息
if (!empty($_POST['csrf_token'])) {
$csrf_token = $_POST['csrf_token'];
} else if (!empty($_GET['csrf_token'])) {
$csrf_token = $_GET['csrf_token'];
} else {
return false;
}
# 对比csrf_token是否正确
if ($_SESSION['csrf_token'] != $csrf_token) {
# 清除csrf_token
$_SESSION['csrf_token'] = null;
return false;
}
$_SESSION['csrf_token'] = null;
return true;
}
# 生成Token随机令牌
$str = 'ABCDEFGHIJKLMNPQRSTUVWXYZabcdefghijklmnpqrstuvwxyz123456789';
$length = mb_strlen($str, 'utf-8');
$Token = '';
for ($i=1; $i<=32; $i++) {
$rand = rand(1, ($length-1));
$Token .= mb_substr($str, $rand, 1, 'utf-8');
}
# 更新csrf_token
$_SESSION['csrf_token'] = $Token;
# 生成Token,模式1
if ($type == 1) {
return "<input type='hidden' name='csrf_token' value='$Token'>";
}
# 生成Token,模式2
return $Token;
}