CSRF保护导致"无效或意外令牌"错误

时间:2016-11-09 17:41:37

标签: javascript php csrf-protection

我正在尝试将CS​​RF保护整合到我的表单上,并且我已经开始使用我的注册表单,该表单在添加CSRF令牌之前开始工作,但现在只生成一个"无效或意外的令牌"错误。这是我目前的表格:

    <form method="post" name="registration_form" action="<?php echo esc_url($_SERVER['PHP_SELF']); ?>">
                <input type="hidden" name="<?= $token_id; ?>" value="<?= $token_value; ?>" />
                First Name: <input type="text" name='<?=$form_names['firstname'];?>' id='firstname' /><br>
                Last Name: <input type="text" name='<?=$form_names['lastname'];?>' id='lastname' /><br>
                Phone: <input type="tel" name='<?=$form_names['phone'];?>' id='phone' /><br>            
                Email: <input type="email" name="<?=$form_names['email'];?>" id="email" /><br>
                Username: <input type="text" name='<?=$form_names['username'];?>' id='username' /><br>
                Password: <input type="password"
                                 name="<?=$form_names['password'];?>" 
                                 id="password"/><br>
                Confirm password: <input type="password" 
                                         name="<?=$form_names['passwordconf'];?>" 
                                         id="confirmpwd" /><br>
                <input type="button" 
                       value="Register" 
                       onclick="return regformhash(this.form,
                                       this.form.<?=$form_names['firstname'];?>,
                                       this.form.<?=$form_names['lastname'];?>,
                                       this.form.<?=$form_names['phone'];?>,
                                       this.form.<?=$form_names['username'];?>,
                                       this.form.<?=$form_names['email'];?>,
                                       this.form.<?=$form_names['password'];?>,
                                       this.form.<?=$form_names['passwordconf'];?>);" /> 
            </form>
</body>

我已经包含了一个带有名称/值对标记的隐藏字段,以及每个名称字段的随机标记。令牌都按预期工作,因此问题不在于生成它们。还有一个Javascript文件验证表单输入,我不知道它是否相关,但这里是js验证:

function regformhash(form, firstname, lastname, phone, username, email, password, confirmpwd) {
    // Check each field has a value
    if (firstname.value == '' || lastname.value == '' || phone.value == '' || email.value == '' || password.value == '' || confirmpwd.value == '') {
        alert('You must provide all the requested details. Please try again');
        return false;
    }

       // Check the First Name
    re = /^[A-Za-z\s]+$/; 
    if(!re.test(form.firstname.value)) { 
        alert("First Name must contain only upper and lower case letters. Please try again"); 
        form.firstname.focus();
        return false; 
    }

      // Check the Last Name
    re = /^[A-Za-z\s]+$/; 
    if(!re.test(form.lastname.value)) { 
        alert("Last Name must contain only upper and lower case letters. Please try again"); 
        form.lastname.focus();
        return false; 
    }

      // Check the Phone Number
    re = /\d{3}[\-]\d{3}[\-]\d{4}/; 
    if(!re.test(form.phone.value)) { 
        alert("Phone Number must be formatted as follows, xxx-xxx-xxxx or (xxx) xxx-xxxx. Please try again"); 
        form.phone.focus();
        return false; 
    }


    // Check the username
    re = /^\w+$/; 
    if(!re.test(form.username.value)) { 
        alert("Username must contain only letters, numbers and underscores. Please try again"); 
        form.username.focus();
        return false; 
    }

    // Check that the password is sufficiently long (min 6 chars)
    // The check is duplicated below, but this is included to give more
    // specific guidance to the user
    if (password.value.length < 6) {
        alert('Passwords must be at least 6 characters long.  Please try again');
        form.password.focus();
        return false;
    }

    // At least one number, one lowercase and one uppercase letter 
    // At least six characters 
    var re = /(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,}/; 
    if (!re.test(password.value)) {

       alert('Passwords must contain at least one number, one lowercase and one uppercase letter.  Please try again');
        return false;
    }

    // Check password and confirmation are the same
    if (password.value != confirmpwd.value) {
        alert('Your password and confirmation do not match. Please try again');
        form.password.focus();
        return false;
    }

    // Create a new element input, this will be our hashed password field. 
    var p = document.createElement("input");

    // Add the new element to our form. 
    form.appendChild(p);
    p.name = "p";
    p.type = "hidden";
    p.value = hex_sha512(password.value);

    // Make sure the plaintext password doesn't get sent. 
    password.value = "";
    confirmpwd.value = "";

    // Finally submit the form. 
    form.submit();
    return true;
}

我不知道参数名称是否需要与表单名称匹配,他们之前并没有完成任何操作。

最后,&#34;无效或意外的令牌&#34;错误指向结束</body>,如果这也有帮助。

更新

我将更深入地了解这个特定CSRF如何为这种特定形式工作。该表单包含另一个名为register.inc.php的php文件,它在向数据库添加数据时执行一系列清理,但我还决定将其用于CSRF检查。这是与CSRF相关的基本代码(注意,我还没有在if语句中添加清理功能,我尝试在没有它之前让表单工作,然后再添加它。我有一个评论,它最终会去):

include 'csrf.class.php';
require 'Sessions/session.class.php';
$session = new session();
// Set to true if using https
$session->start_session('_s', false);

$csrf = new csrf();


// Generate Token Id and Valid
$token_id = $csrf->get_token_id();
$token_value = $csrf->get_token($token_id);

// Generate Random Form Names
$form_names = $csrf->form_names(array('firstname','lastname','phone','email', 'username', 'password','passwordconf'), false);


if(isset($_POST[$form_names['email']], $_POST[$form_names['password']])) {
        // Check if token id and token value are valid.
        if($csrf->check_valid('post')) {
                // Get the Form Variables.


                // Add Sanitization function here
        }
        // Regenerate a new random value for the form.
        $form_names = $csrf->form_names(array('email', 'password'), true);
}

以下是此处引用的csrf.class.php:

<?php

class csrf{

    public function get_token_id() {
        if(isset($_SESSION['token_id'])) { 
                return $_SESSION['token_id'];
        } else {
                $token_id = $this->random(10);
                $_SESSION['token_id'] = $token_id;
                return $token_id;
        }
}
public function get_token() {
        if(isset($_SESSION['token_value'])) {
                return $_SESSION['token_value']; 
        } else {
                $token = hash('sha512', $this->random(500));
                $_SESSION['token_value'] = $token;
                return $token;
        }

}
public function check_valid($method) {
        if($method == 'post' || $method == 'get') {
                $post = $_POST;
                $get = $_GET;
                if(isset(${$method}[$this->get_token_id()]) && (${$method}[$this->get_token_id()] == $this->get_token())) {
                        return true;
                } else {
                        return false;   
                }
        } else {
                return false;   
        }
}
public function form_names($names, $regenerate) {

        $values = array();
        foreach ($names as $n) {
                if($regenerate == true) {
                        unset($_SESSION[$n]);
                }
                $s = isset($_SESSION[$n]) ? $_SESSION[$n] : $this->random(10);
                $_SESSION[$n] = $s;
                $values[$n] = $s;       
        }
        return $values;
}
private function random($len) {
        if (function_exists('openssl_random_pseudo_bytes')) {
                $byteLen = intval(($len / 2) + 1);
                $return = substr(bin2hex(openssl_random_pseudo_bytes($byteLen)), 0, $len);
        } elseif (@is_readable('/dev/urandom')) {
                $f=fopen('/dev/urandom', 'r');
                $urandom=fread($f, $len);
                fclose($f);
                $return = '';
        }

        if (empty($return)) {
                for ($i=0;$i<$len;++$i) {
                        if (!isset($urandom)) {
                                if ($i%2==0) {
                                             mt_srand(time()%2147 * 1000000 + (double)microtime() * 1000000);
                                }
                                $rand=48+mt_rand()%64;
                        } else {
                                $rand=48+ord($urandom[$i])%64;
                        }

                        if ($rand>57)
                                $rand+=7;
                        if ($rand>90)
                                $rand+=6;

                        if ($rand==123) $rand=52;
                        if ($rand==124) $rand=53;
                        $return.=chr($rand);
                }
        }

        return $return;
}
}

提交表单时,它会从会话中的表单中保存CSRF令牌,并将其与Post值中的令牌进行比较。如果两者匹配,则继续代码。这是我用来创建CSRF保护的网站Prevent CSRF

0 个答案:

没有答案