Защита CSRF вызывает ошибку "Неверный или неожиданный токен"

Я пытаюсь интегрировать защиту CSRF в мои формы, и я начал с моей регистрационной формы, которая начала работать до того, как были добавлены токены 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;
}

Я не знаю, должны ли имена параметров совпадать с именами форм, они не все раньше, и это работало.

Наконец, ошибка "Неверный или неожиданный токен" указывала на закрытие </body>, если это также помогает.

Обновить:

Я собираюсь более подробно рассказать о том, как именно этот CSRF работает для этой конкретной формы. Форма имеет включение в другой php-файл с именем register.inc.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 ответов

Другие вопросы по тегам