提交在codeigniter中启用CSRF保护的表单后出现内部服务器错误

时间:2017-03-14 17:57:27

标签: jquery ajax codeigniter-3 internal-server-error csrf-protection

我使用Codeigniter 3.1.3并在配置文件中启用了CSRF保护。不知何故,如果表单通过验证,我总是从ajax $ .post获得500(内部服务器错误)。如果验证失败,我没有收到该错误。有什么想法吗?

以下是codeigniter配置文件中的csrf设置:

$config['csrf_protection'] = TRUE;
$config['csrf_token_name'] = 'csrf_token_name';
$config['csrf_cookie_name'] = 'csrf_cookie_name';
$config['csrf_expire'] = 7200;
$config['csrf_regenerate'] = TRUE;

以下是我在login.php视图中的登录表单:

<form id="login_form" class="col-12 col-md-8 mx-auto" method="post">

    <div class="alert" id="login-alert" role="alert"></div>


    <div class="form-group input-group">
        <span class="input-group-addon" id="basic-addon1">Username</span>
        <input type="text" class="form-control" id="username" name="username" />
    </div>
    <div class="form-group input-group">
        <span class="input-group-addon" id="basic-addon2">Password</span>
        <input type="password" class="form-control" id="pwd" name="pwd" />
    </div>
    <div class="text-center">
        <button type="button" id="login_btn" class="btn btn-primary">LOGIN</button>
    </div>

</form>

我使用AJAX $ .post提交表单

$("#login_btn").click(function(){
            var data = $("#login_form").serialize();
            var csrf_name = "<?=$this->security->get_csrf_token_name()?>";
            //get_cookie is a function that I defined to retrieve the cookie 
            var csrf_cookie = get_cookie("csrf_cookie_name");


            $.post('index.php/sentinel/verify_user',data+"&"+csrf_name+"="+csrf_cookie,function(data){
                if(data && data !== '')
                {
                    data = data.replace(/(<p>)/g,'').replace(/(<\/p>)/g,'<br>');
                    $("#login-alert").prop('class','alert alert-danger').html(data);
                }
                else
                {
                    //success
                    //redirect to the main page

                }


            });            
        });

function get_cookie( check_name ) {
    var a_all_cookies = document.cookie.split( ';' );
    var a_temp_cookie = '';
    var cookie_name = '';
    var cookie_value = '';
    var b_cookie_found = false; // set boolean t/f default f

    for ( i = 0; i < a_all_cookies.length; i++ )
    {
         // now we'll split apart each name=value pair
        a_temp_cookie = a_all_cookies[i].split( '=' );
        // and trim left/right whitespace while we're at it
        cookie_name = a_temp_cookie[0].replace(/^\s+|\s+$/g, '');

        // if the extracted name matches passed check_name
       if ( cookie_name == check_name )
       {
            b_cookie_found = true;
           // we need to handle case where cookie has no value but exists (no = sign, that is):
            if ( a_temp_cookie.length > 1 )
            {
                cookie_value = unescape( a_temp_cookie[1].replace(/^\s+|\s+$/g, '') );
            }
           // note that in cases where cookie is initialized but no value, null is returned
           return cookie_value;
           break;
        }
        a_temp_cookie = null;
        cookie_name = '';
    }
    if ( !b_cookie_found )
    {
        return null;
    }
}

这是我的控制器:

public function verify_user(){


    $this->form_validation->set_rules('username', 'Username', 'required|alpha');
    $this->form_validation->set_rules('pwd', 'Password', 'required|callback_alpha_numeric_dots');
    $this->form_validation->set_message('alpha_numeric_dots','Invalid Password.');
    if ($this->form_validation->run() == FALSE)
    {
        echo validation_errors();
    }
    else
    {
        $this->form_validation->set_rules('pwd','Password','callback_login_check');
        if ($this->form_validation->run() == FALSE)
        {
            echo validation_errors();
        }
        else
        {
            echo '';
        }

    }

}

public function login_check(){
    $data = $this->security->xss_clean($this->input->post());
    $rep_info= $this->sentinel_model->user_verify($data);
    if($rep_info === FALSE)
    {
        $this->form_validation->set_message('login_check', 'Incorrect Username or Password');
        return FALSE;

    }
    else
    {
        //set session data here
        $newdata = array(
            'id' =>$this->encryption->encrypt($rep_info['user_id']),
            'name'  => $rep_info['user_name'],
            'email' => $rep_info['user_email'],
            'logged_in' => TRUE
        );

        $this->session->set_userdata($newdata);
        return TRUE;

    }
}
public function alpha_numeric_dots($str)
{
    return (bool) preg_match('/^[A-Z0-9.]+$/i', $str);
}

我还想在用户成功登录后将用户重定向到另一个视图而不更改网址。最好的方法是什么?

1 个答案:

答案 0 :(得分:0)

当使用CSRF函数及其快速修复时,form_open()标记作为隐藏输入添加到表单中。

具有CSRF标记值的cookie由Security类创建,并在必要时为每个请求重新生成。

如果存在$_POST数据,则Cookie会自动由输入类验证。如果发布的令牌与Cookie的值不匹配,CI将显示错误,无法处理$_POST数据。

所以基本上,它都是自动的 - 您只需在$config['csrf_protection']中启用它,然后使用表单的form_open()功能。

所以客户端你只需要发布

$.post('index.php/sentinel/verify_user',$("#login_form").serialize(),function(data){

});

在您的控制器中

$this->load->helper('form');

并在您查看文件

<?php echo form_open('sentinel/verify_user', 'id="login_form" class="col-12 col-md-8 mx-auto"'); ?>
    <div class="alert" id="login-alert" role="alert"></div>


    <div class="form-group input-group">
        <span class="input-group-addon" id="basic-addon1">Username</span>
        <input type="text" class="form-control" id="username" name="username" />
    </div>
    <div class="form-group input-group">
        <span class="input-group-addon" id="basic-addon2">Password</span>
        <input type="password" class="form-control" id="pwd" name="pwd" />
    </div>
    <div class="text-center">
        <button type="button" id="login_btn" class="btn btn-primary">LOGIN</button>
    </div>

</form>

在下面的表单中创建隐藏输入

<input type="hidden" name="<?php echo $this->security->get_csrf_token_name(); ?>" value="<?php echo $this->security->get_csrf_hash();?>" />

不修改视图文件,(已发布的更正版本)

var data = $("#login_form").serialize();
$.post(
        'index.php/sentinel/verify_user',
        data+"&" + '<?php echo $this->security->get_csrf_token_name(); ?>' +"="+ '<?php echo $this->security->get_csrf_hash(); ?>',
        function(data){

 });

发表评论

  

实际上我只是注意到我得到了403(Fordidden)错误   我第二次点击登录按钮

来自the docs

  

标记可以在每次提交时重新生成(默认),也可以在CSRF cookie的整个生命周期内保持相同。标记的默认重新生成提供更严格的安全性,但可能导致可用性问题,因为其他标记变得无效(后退/前进导航,多个选项卡/窗口,异步操作等)。您可以通过编辑以下配置参数

来更改此行为
$config['csrf_regenerate'] = TRUE;

将其设置为FALSE。