若转载教程,请注明出自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){# 先开启SESSIONisset($_SESSION) || session_start();# 验证码Tokenif ($type === false) {# 现在检测有没有创建过csrf_tokenif (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,模式1if ($type == 1) {return "<input type='hidden' name='csrf_token' value='$Token'>";}# 生成Token,模式2return $Token;}
