duandeng1824 2014-06-13 20:08
浏览 50

reCaptcha - 强力保护(php / codeIgniter)

I am adding a reCaptcha field in my login form when failed attempt on the same username is > 5. I'm having problem doing this without cookies. If someone has an idea, it is welcome.


Here is my pseudo code :

User get on page, counter is set to failed_login=0 When trying to login, if user guess a good username but a bad password, the db field for this username is increased When the counter is > 5, show a captcha on the login page

The problem I have is that even when the counter is > 5, the captcha field is not verified by my controller. And also, A user may just leave my site and come back to have it's counter back to 0. Is this possible to do without using cookie? I would like to have a simple way. If I use cookie, maybe check if user has cleared his cookie not too long ago, and display a captcha, any idea welcome https://www.dropbox.com/s/30mf4ha1rm7w407/still_login.png

I'll post my full code below

Controller :

public function index()
{
    $data['publickey'] = "6LczHPd2USAAAAAbffN2Po1HaNqPyfdfdfdfdsRfaDPO9E-"; // for reCaptcha

    $this->form_validation->set_error_delimiters('<div class="alert alert-danger">', '</div>');

    $this->form_validation->set_rules('email', 'lang:label_email', 'required');
    $this->form_validation->set_rules('password', 'lang:label_password', 'required');


    global $nb_failed_attempt;
    $data['show_captcha'] = FALSE;


    if ($this->form_validation->run() === FALSE)
    {
        $this->load->view('templates/header', $data);
        $this->load->view('templates/topMenu', $data);
        $this->load->view('pages/login', $data);

    }
    else
    {
        #check login
        $success = $this->User_model->checkLogin($this->input->post('email'), $this->input->post('password'));

        #sucess redirect to home page
        if ($success) {
            redirect('/');
        }
        # bad login, reload page with error message
        else {

            echo $nb_failed_attempt;  ## variable not defined even when it is global inside user_model.php?

            if ($nb_failed_attempt > 5) {
                $data['show_captcha'] = TRUE;
                $this->form_validation->set_rules('recaptcha_challenge_field', 'Challenge', 'trim|required|callback_captcha_check');
                # this doesnt get triggered even when >5, no captcha filled and with good pw you can still login..
            }
            else {
                $data['show_captcha'] = FALSE;
            }

            $this->load->view('templates/header', $data);
            $this->load->view('templates/topMenu', $data);
            $this->load->view('pages/login', $data);
        }
    }
}


public function captcha_check($string) {

    $privatekey = "6LczHPUSAAAAAKQ58YZoV8S8ZCZ6A4ZiuqxSbbK5";
    $resp = recaptcha_check_answer ($privatekey,
            $_SERVER["REMOTE_ADDR"],
            $_POST["recaptcha_challenge_field"],
            $_POST["recaptcha_response_field"]);

    if (!$resp->is_valid) {
        $this->form_validation->set_message('captcha_check', 'The reCAPTCHA wasn\'t entered correctly. Go back and try it again.');
        return FALSE;
    } else {
        return TRUE;
    }
}

User_Model (db request)

   public function checkLogin($email, $password) {


    global $nb_failed_attempt; 
    if (!isset($nb_failed_attempt)) {
        $nb_failed_attempt = 0;
    }

        #retrieve the user salt
        $query1 = $this->db->get_where ( 'user', array ('email' => $email), 1, 0 );


        if ($query1->num_rows () <= 0) {
            $this->messages->add ( sprintf ( lang ( 'error_log_in' ), 'not_exist' ), "error" );
            return false;
        }
        else {
            if ($query1->first_row ()->confirm_code != 'y') {
                $this->messages->add ( sprintf ( lang ( 'error_log_in' ), 'not yet activated' ), "error" );
                return false;
            }
        }

        $salt = $query1->first_row ()->salt;

        // check for hashed password
        $saltedPW = $password . $salt;
        $hashedPW = hash ( 'sha256', $saltedPW );


        $query2 = $this->db->get_where ( 'user', array ('email' => $email, 'password' => $hashedPW), 1, 0 );


        ### FAILED LOGIN ATTEMPT, increase failed_attempt value
        if ($query2->num_rows () <= 0) {
            $this->messages->add ( sprintf ( lang ( 'error_log_in' ), 'bad_pwd' ), "error" );  

            $nb_failed_attempt = $query1->first_row ()->failed_attempt + 1;
            $data = array ('failed_attempt' => $nb_failed_attempt );

            $this->db->where('email', $email);
            $this->db->update('user', $data);
            return false;
        }


        ######## Reset failed login attempt if need be #######
        if ($query2->first_row ()->failed_attempt > 0) {

            $nb_failed_attempt = 0;
            $data = array ('failed_attempt' => $nb_failed_attempt );

            $this->db->where('email', $email);
            $this->db->update('user', $data);
        }


        ######### Save info into cookies #########
        $this->session->set_userdata ( array (
                'id_user' => $query2->first_row ()->id,
                'email' => $query2->first_row ()->email,
                'first_name' => $query2->first_row ()->first_name,
                'last_name' => $query2->first_row ()->last_name,
                'display_name' => $query2->first_row ()->display_name,
                'birthdate' => $query2->first_row ()->birthdate,
                'sex' => $query2->first_row ()->sex,
                'weight' => $query2->first_row ()->weight,
                'height' => $query2->first_row ()->height
        ) );


        return true;
    }

Here is my table "User":

CREATE TABLE IF NOT EXISTS `user` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `email` varchar(150) NOT NULL,
  `password` varchar(255) NOT NULL,
  `failed_attempt` int(3) unsigned DEFAULT 0,
  `salt` varchar(255) NOT NULL,
  `confirm_code` varchar(255),
  `reset_token` varchar(255) NOT NULL,
  `reset_token_expire` datetime DEFAULT NULL,
  `session_id` varchar(255) NOT NULL,
  `first_name` varchar(50) NOT NULL,
  `last_name` varchar(50) NOT NULL,
  `display_name` varchar(50) NOT NULL,
  `birthdate` date NOT NULL,
  `sex` char(1) NOT NULL,
  `last_ip` varchar(50) NOT NULL,
  `last_login` datetime DEFAULT NULL,
  `date_signup` datetime DEFAULT NULL,
  `os` varchar(25) NOT NULL,
  `weight` decimal(5,2) unsigned DEFAULT NULL,
  `height` decimal(5,2) unsigned DEFAULT NULL

  PRIMARY KEY (`id`),
  UNIQUE KEY `email` (`email`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 ;
  • 写回答

1条回答 默认 最新

  • dqm88684 2014-06-13 20:21
    关注

    You may use it without cookies. Some ways are using checking remote IP in table with all IPs and counting, or just set counter for username without checking cookie or IP.

    $nb_failed_attempt for index() function is a global variable? If it's not - that your answer ($nb_failed_attempt not set everytime).

    UPD

    Start of checkLogin() should be like this:

    global $nb_failed_attempt;
    $query1 = $this->db->get_where ( 'user', array ('email' => $email), 1, 0 );
    $nb_failed_attempt = $query1->first_row ()->failed_attempt;
    if(@!$nb_failed_attempt)
        $nb_failed_attempt = 0;
    

    Start of index() should be like this:

    global $nb_failed_attempt;
    

    Check also error messages and please write, if echo $nb_failed_attempt; work.

    UPD2

    I use follow structure of session table in big projects:

    CREATE TABLE IF NOT EXISTS `session` (
        `id` bigint AUTO_INCREMENT,
        `login` char(128),
        `ip` char(128),
        `try` smallint,
        `blocktime` datetime,
        `sitesession` char(128),
        `firsttime` datetime,
        `lasttime` datetime,
        PRIMARY KEY (`id`)
    ) ENGINE=MyISAM AUTO_INCREMENT=1;
    

    It helps save additional information about logins, enters, and help to users's support.

    This table may be not fine, but it work for me.

    评论

报告相同问题?