【问题标题】:Internal server error after submitting a form with CSRF protection enabled in codeigniter提交在 codeigniter 中启用 CSRF 保护的表单后出现内部服务器错误
【发布时间】:2017-03-14 17:57:26
【问题描述】:

我正在使用 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);
}

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

【问题讨论】:

  • 显示你的函数get_cookie
  • 添加函数 get_cookie

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


【解决方案1】:

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

具有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) 错误 当我第二次点击登录按钮时,共 500 个

来自the docs

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

$config['csrf_regenerate'] = TRUE;

将其设置为 FALSE。

【讨论】:

  • 我知道 form_open() 会自动添加隐藏的输入字段。我已经试过了。序列化的表单数据应该已经包含 csrf 令牌。为什么还要将它附加到数据变量中?无论如何,使用该方法,一旦我第二次单击提交按钮,无论表单是否有效,我都会开始收到 500 错误
  • @Vaeianor:第三个是您的代码的更正版本,序列化时将不包含 csrf 令牌(without using form_open or hidden input in view
  • 其实我只是注意到我第二次点击登录按钮时收到的是 403(Fordidden) 错误而不是 500
  • @Vaeianor:是的,当 csrf 检查失败时,codeigniter 会警告禁止
  • 好的,我可以通过在 codeigniter 配置文件中将 $config['csrf_regenerate'] 设置为 FALSE 来修复 403 错误。但是,如果表单通过验证,我仍然会收到 500 错误
猜你喜欢
  • 2015-04-26
  • 1970-01-01
  • 2012-05-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-31
相关资源
最近更新 更多