使用会话令牌保护表单

时间:2016-09-07 18:24:40

标签: php forms token

我写了一个脚本来用会话令牌保护我的表单;但是,如果我在检查令牌之前尝试验证表单字段,则我的脚本不起作用。有人会帮我弄清楚我的剧本有什么问题吗?

<?php
          session_start();
          class TOKEN {
            public static function generate() {
              return $_SESSION['token'] = base64_encode(openssl_random_pseudo_bytes(15));
            }
            public static function check($token) {
              if (isset($_SESSION['token']) && $token === $_SESSION['token']) {
                unset($_SESSION['token']);
                return true;
              }
              return false;
            }
          }
        ?>
        <?php
          $display_form = FALSE;
          if (isset($_POST['submit'])) {
            $username = $_POST['username'];
            $userpass = $_POST['userpass'];

            if (strlen($username) < 4) {
              $error_name = 'required';
              $display_form = true;
              $validation_error = true;
            }
            if (strlen($userpass) < 8) {
              $error_pass = 'required';
              $display_form = true;
              $validation_error = true;
            }
            if (!$validation_error) {
              if (TOKEN::check($_POST['token'])) {
                echo 'process form';
              } else {
                echo 'invalid security token';
              }
            }
          } else {
            $display_form = TRUE;
          }
        ?>
        <!DOCTYPE html>
        <html lang="en">
        <head>
          <meta charset="UTF-8">
          <title>Title</title>
        </head>
        <body>
        <?php
          if ($display_form == true) {
        ?>
        <form method="post" action="<?php echo htmlspecialchars($_SERVER['REQUEST_URI']); ?>">
          <input type="hidden" name="token" value="<?php echo TOKEN::generate(); ?>">
          <input type="text" name="username" id="" placeholder="username">
          <?php echo $error_name; ?>
          <br>
          <input type="password" name="userpass" id="" placeholder="Password">
          <?php echo $error_pass; ?>
          <br>
          <input type="submit" name="submit" value="Sign in">
        </form>
        </body>
        </html>
        <?php
         }
        ?>

2 个答案:

答案 0 :(得分:0)

这段代码很难阅读。我不知道if语句何时开始和结束。也停止使用所有类的类。像大男孩一样使用程序式编程。

您的问题很简单。 $validation_error未在外部范围内初始化。这意味着它没有在if语句之间保存。

要解决此问题,只需在外部范围添加$validation_error = false

...
$display_form = FALSE;
$validation_error = false; // right here
          if (isset($_POST['submit'])) {
            $username = $_POST['username'];
            $userpass = $_POST['userpass'];
            ...

答案 1 :(得分:0)

我想这里的问题如下。

  1. 您在表单中有令牌,在会话中有令牌。他们是平等的。
  2. 当您填写表单时出现错误 - 您的表单会再次加载。但!在会话中,您有前一个令牌,从第1点开始,在表单中您有新令牌。
  3. 您再次提交并检查不同的令牌。
  4. 因此,无论您在表单中有错误或正确的值,解决方案都是unset令牌始终

    <强>更新

    我想它应该是这样的:

    if (!$validation_error) {
      // here token will be removed in `TOKEN::check`
      if (TOKEN::check($_POST['token'])) {
        echo 'process form';
      } else {
        echo 'invalid security token';
      }
    } else {
      // remove token implicitly
      TOKEN::remove();
    }
    

    TOKEN

    public static function check($token) {
      $result = false;
    
      if (isset($_SESSION['token'])) {
        if ($token === $_SESSION['token']) {
          $result = true;
        }
        // if token set - remove it
        self::remove();
      }
    
      return $result;
    }
    
    public static function remove() {
      unset($_SESSION['token']);
    }