【问题标题】:password_reset validation php密码重置验证php
【发布时间】:2011-12-10 01:52:13
【问题描述】:

我很想知道:通过电子邮件发送 pw_reset 令牌后,是否可以获取推荐人帐户的电子邮件地址进行验证,或者最好根据 IP 进行验证?我将令牌设置为仅 1 小时过期,即使是动态 IP 也不会以该速率更改,对吧?

我正在使用的一些代码(非常多的 WIP),欢迎改进/建议/批评。

提前致谢。

 /**
 * Reset Password Form
 * 
 * $route['auth/reset-password'] = 'reset_password_form';
 * 
 */
public function reset_password_form()
{
    $this->load->view('templates/public', array(
        'content'   =>  'auth/reset_password_form',
        'title' =>  'Password reset',
        'description'   =>  '',
        'canonical' =>  'auth'
    ));
}
/**
 * Reset Password Authentication
 * 
 * $route['auth/reset_password/validate'] = 'auth/reset_password';
 * 
 */
public function reset_password()
{
    //setup validation rules
    $config = array(
        array('field'=>'email_address', 'label' =>'Email address', 'rules'=>'trim|required|max_lenght[128]|valid_email|valid_user_email')
    );
    $this->form_validation->CI =& $this; 
    $this->form_validation->set_rules($config); 

    //run validator and confirm OR
    if($this->form_validation->run($this))
    {

        //create the token data
        $tokenhash = Auth::create_token();
        $password_token = json_encode(array(
            'token'      => $tokenhash,
            'expires'    => date('h:i:s A', strtotime($this->config->item('pw_token_expires'))), // default 1 hours
            'ip_address' =>  $this->input->ip_address()
        )); // output {"token":"3513f5ee34ED3","expires":"01:14:06 AM","ip_address":"127.0.0.1"}


        //grab a userid to use via php-activerecord custom USER model
        $user = User::get_by_email_address($this->input->post('email_address'));

        //update the user pw_token field
        try{
            if($user->update_attribute('pw_token', $password_token))
            {
                throw new \ActiveRecord\ActiveRecordException($user->errors);
            }
        }catch(\ActiveRecord\ActiveRecordException $e){
            log_message('error', $e->getMessage());
        }

        //setup email data
        //TODO : move this to USER activeRecord\Model (pre_)
        $email_data = array(
            'email_to'  =>  $user->email_address,
            'token'     =>  anchor(site_url('auth/reset_password/confirm/'.$tokenhash.'/'.$user->id.''), 'click here to reset your password'),
            'site_link' =>  anchor(site_url(), site_url())
        );

        try{
            if(Mail::send($email_data, 'reset_password.tpl.html'))
            {
                throw new Exception(Mail::errors());
            }
        }catch(Exception $e){
            log_message('error', $e->getMessage());
        }

    }
    else
    {
        $this->reset_password_form();
    }
}
/**
 * Reset Password Confirmation
 * 
 * $route['auth/reset_password/confirm/(:any)/(:num)'] = 'auth/confirm_password_reset';
 */
public function confirm_password_reset($token='', $userid='')
{
    //check for null values
    if(!$token || !$id)
    {
        redirect('/');
    }

    //ugly
    $attempts = $this->session->set_userdata('reset_pw_attempt', (int)0);
    $this->session->set_userdata('reset_pw_attempts', $attempts++);



    if(!$this->user->id != $userid && $this->session->userdata('logged_in') === (int)0)
    {
        //not great but cant validate the email, so we check to see if they have a logged in session.
        //this is not a "forgot_password request" so we should be good as long as they are logged in
        show_404();
    }
    else
    {

        //looking good so far, now lets see do they have the correct permissions
        if(in_array(PERM_UPDATE, Auth::permissions($this->user->permissions)))
        {
            if($attempts == (int)3)
            {
                $this->session->set_flashdata('', $this->lang->line('pw_reset_attempt_over'));
                redirect('/');
            }
            else
            {
                $tokn[] = json_decode($this->user->pw_token);

                if(date('h:i:s A', time()) > $tokn['expires'] && $token===$tokn['token'])
                {
                    $this->load->view('do-pw_reset_form');
                }
            }
        }
        else
        {
            $this->session->set_flashdata('info', $this->lang->line('update_permission_denied'));
            redirect('/');
        }
    }
}

【问题讨论】:

    标签: php codeigniter authentication reset-password


    【解决方案1】:

    通过查询字符串参数检查电子邮件地址或利用 IP 限制都是完全多余的。如果密码令牌是随机的且足够长,则不可能对其进行暴力破解,尤其是与令牌过期和令牌猜测的速率限制结合使用时。

    IP 地址限制也可能是一个可用性问题。例如,用户可能会在下班前请求重置密码,然后在家中或在回家途中完成重置过程——也就是说,用户的 IP 地址在重置请求和重置确认之间发生变化。

    【讨论】:

      【解决方案2】:

      为什么要检查推荐人?使用closed loop verification 是确认用户电子邮件地址的标准做法。您无需检查 IP 或推荐人,只需创建自定义哈希并跟踪电子邮件地址,然后将包含此信息的电子邮件发送给用户。

      当用户点击嵌入链接时,您会确认您创建的哈希值,如果哈希值对齐,则用户已在您的系统中确认了他们的电子邮件帐户。

      //Example Link
      https://mydomain.com/verify?hash=<some_hash>
      

      为了增加安全性,您还可以跟踪系统发送电子邮件的时间,并在 24 小时后使哈希无效。因此,如果用户在 25 小时后点击,您会告知他们哈希无效,并询问他们是否要发送另一封电子邮件,如果是,请重复上述过程。

      【讨论】:

      • 谢谢 digitalp,我想我正试图在不需要的地方使用某种身份验证。我的想法是在向他们展示实际的重置表单之前添加一个额外的子句。
      • 我也只是使用令牌并在单独的表中记录重置密码请求。当一个好的令牌可以满足您的所有需求时,我发现没有必要在链接中发送电子邮件。
      • 在 URL 中传递电子邮件地址等个人身份信息并不是一个好主意。 URL 不仅存储在本地浏览器历史记录中,还可以由 ISP 记录、通过代理缓存等。假设查询字符串中的任何信息(包括密码重置令牌)都可以被预期接收者以外的各方查看。然而,虽然重置令牌可能会过期,但像 http://horsepronxxx.com?token=abc123&amp;email=importanperson@senate.gov 这样的关联可以无限期地保持。
      • @powers1:关于将电子邮件排除在 url 之外的好建议,更新帖子以反映。
      • @dp 我同意,但这意味着用户需要登录会话
      【解决方案3】:

      没有理由检查引荐来源网址,如果客户的计算机遭到入侵,您将无法避免劫持。您可以做的是注意 CSRF 漏洞。

      此外,在阅读后,它看起来确实检查了“ip_address”=> $this->input->ip_address()。

      【讨论】:

      • 嗨 fabianhjr,感谢您的回复。您提到的那一行只是将输入 ip 分配给 json 对象。这或多或少是我的问题,是否可以在令牌到期前验证 ip 作为它的短窗口(1 小时)。
      • 老实说,我没有看到任何好处。无论哪种方式都可以随意验证。
      猜你喜欢
      • 1970-01-01
      • 2019-07-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多