若转载教程,请注明出自SW-X框架官方文档

6、什么是Token令牌

之前我们想到了一个方法生成一段随机字符串放在每个POST、GET请求中,这样可以有效的防止CSRF攻击,而这段随机字符串就是ToKen令牌。

7、手把手教学之四,实现Token令牌防御表单重复多次提交函数封装

Token令牌即使上除了可以防止CSRF攻击之外,还可以防止用户通过F5刷新页面达到重复提交相同的内容到数据库中。
Token令牌的生成流程如下:

  1. 1、生成一段32位的随机字符串
  2. 2、将这段字符串保存到一个名为csrf_tokensession

Token令牌的验证流程如下:

  1. 1、获取POSTGET请求中,是否有一个叫csrf_token的节点
  2. 2session中的csrf_token为空则直接验证失败
  3. 3、获得请求中的csrf_token节点 session中的csrf_token做对比,成功则通过
  4. 4、不管对比成功或失败,都要清空session中的csrf_token

按正常情况下,我们应该一个函数就完成上面的两种生成和验证的功能,朋友们可以自己先思考完成,然后再对比下面的案例代码:
本次案例主要有3个文件:

  1. 1index.php文件,用于创建测试的form表单,发起GET请求
  2. 2demo.php文件,用于接收form表单提交的数据,同时检查表单中的csrf_token是否合法
  3. 3token.php文件,存放csrf_token生成跟检查的函数

index.php文件代码如下:

  1. <?php
  2. # 引用csrf_token令牌函数
  3. require_once 'token.php';
  4. ?>
  5. <html>
  6. <head>
  7. <meta charset="utf-8">
  8. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  9. </head>
  10. <body>
  11. <h2>提交之后,按住F5,看能不能刷新重复提交</h2>
  12. <hr/>
  13. <form method="GET" action="demo.php">
  14. <?php echo csrf_token(1);?>
  15. 随便输入:<input type="type" name="content">
  16. <br/>
  17. <br/>
  18. <input type="submit" value="提交校验">
  19. </form>
  20. </body>
  21. </html>

demo.php文件代码如下:

  1. <?php
  2. # 引用csrf_token令牌函数
  3. require_once 'token.php';
  4. # 验证csrf_token令牌
  5. $vif = csrf_token(false);
  6. if (!$vif) {
  7. die('csrf_token攻击验证未通过');
  8. }
  9. $content = $_GET['content'];
  10. echo "你提交的内容:$content";

token.php文件代码如下:

  1. <?php
  2. /**
  3. * 生成与验证csrf_token令牌
  4. * @param int|bool $type 当为false时,则是验证Token,为1时返回表单结构,为2时返回csrf_token随机字符串
  5. * @return string|bool Token结构 或 验证结果
  6. */
  7. function csrf_token($type){
  8. # 先开启SESSION
  9. isset($_SESSION) || session_start();
  10. # 验证码Token
  11. if ($type === false) {
  12. # 现在检测有没有创建过csrf_token
  13. if (empty($_SESSION['csrf_token'])) {
  14. return false;
  15. }
  16. # 收集表单中关于csrf_token的信息
  17. if (!empty($_POST['csrf_token'])) {
  18. $csrf_token = $_POST['csrf_token'];
  19. } else if (!empty($_GET['csrf_token'])) {
  20. $csrf_token = $_GET['csrf_token'];
  21. } else {
  22. return false;
  23. }
  24. # 对比csrf_token是否正确
  25. if ($_SESSION['csrf_token'] != $csrf_token) {
  26. # 清除csrf_token
  27. $_SESSION['csrf_token'] = null;
  28. return false;
  29. }
  30. $_SESSION['csrf_token'] = null;
  31. return true;
  32. }
  33. # 生成Token随机令牌
  34. $str = 'ABCDEFGHIJKLMNPQRSTUVWXYZabcdefghijklmnpqrstuvwxyz123456789';
  35. $length = mb_strlen($str, 'utf-8');
  36. $Token = '';
  37. for ($i=1; $i<=32; $i++) {
  38. $rand = rand(1, ($length-1));
  39. $Token .= mb_substr($str, $rand, 1, 'utf-8');
  40. }
  41. # 更新csrf_token
  42. $_SESSION['csrf_token'] = $Token;
  43. # 生成Token,模式1
  44. if ($type == 1) {
  45. return "<input type='hidden' name='csrf_token' value='$Token'>";
  46. }
  47. # 生成Token,模式2
  48. return $Token;
  49. }