Ajax and php protection

3

Using ajax in my case, can it bring some security flaw? If so, how do I solve it? javascript:

$(function() {
    if($('#login_submit').length !== 0) {
        $('#login_submit').on('click', function() {
            if(!$('#login_submit').hasClass('disabled')) {

                $.ajax({
                    url: url + 'login/ajaxLogin',
                    type: 'POST',
                    data: $('#login_form').serialize(),
                    success: function(result) {
                        alert(result);
                        $("#login_submit").removeClass("m-progress disabled");
                    }
                });

                return false;
            }
        });
    }
});

Controller:

class Login extends Controller {
    public function index() {
        //if($this->model->isUserLoggedIn()) 
            //header('Location: ' . URL . 'me');

        $news = $this->model->latestNews();

        require APP . 'view/_templates/header.php';
        require APP . 'view/login/index.php';
        require APP . 'view/_templates/footer.php';
    }

    /**
     * AJAX-ACTION: AjaxLogin
     * TODO documentation
     */
    public function ajaxLogin() {
        $errors = $this->model->doLoginWithPostData($_POST['user_email'], $_POST['user_password']);
        echo $errors;
    }
}

Model:

public function doLoginWithPostData($user_email, $user_password) {
    if(empty($user_email) AND empty($user_password)) {
        return MESSAGE_ALL_EMPTY;
    } else if(empty($user_email)) {
        return MESSAGE_USERNAME_EMPTY;
    } else if(empty($user_password)) {
        return MESSAGE_PASSWORD_EMPTY;
    } else {
        $result_row = $this->getUserData(trim($user_email));

        if(!isset($result_row->id)) {
            return MESSAGE_USER_DOES_NOT_EXIST;
        } else if(($result_row->user_failed_logins >= 3) && ($result_row->user_last_failed_login > (time() - 30))) {
            return MESSAGE_PASSWORD_WRONG_3_TIMES;
        } else if(!password_verify($user_password, $result_row->password)) {
            $sth = $this->db->prepare('UPDATE users '
                    . 'SET user_failed_logins = user_failed_logins+1, user_last_failed_login = :user_last_failed_login '
                    . 'WHERE mail = :user_email');
            $sth->execute(array(':user_email' => $user_email, ':user_last_failed_login' => time()));

            return MESSAGE_PASSWORD_WRONG;
        } else {
            // write user data into PHP SESSION [a file on your server]
            $_SESSION['user_id'] = $result_row->id;
            $_SESSION['user_name'] = $result_row->username;
            $_SESSION['user_email'] = $result_row->mail;
            $_SESSION['user_logged_in'] = 1;

            // reset the failed login counter for that user and set last ip and time
            $query = $this->db->prepare('UPDATE users '
                    . 'SET user_failed_logins = 0, user_last_failed_login = NULL, last_online = :time, ip_last = :ip '
                    . 'WHERE id = :user_id AND user_failed_logins != 0');
            $query->execute(array(':time' => time(), ':ip' => $this->getUserIP(), ':user_id' => $result_row->id));

            if(defined('HASH_COST_FACTOR')) {
                if(password_needs_rehash($result_row->password, PASSWORD_DEFAULT, array('cost' => HASH_COST_FACTOR))) {
                    $user_password_hash = password_hash($user_password, PASSWORD_DEFAULT, array('cost' => HASH_COST_FACTOR));

                    $query = $this->db->prepare('UPDATE users SET password = :user_password_hash WHERE id = :user_id');
                    $query->bindValue(':user_password_hash', $user_password_hash, PDO::PARAM_STR);
                    $query->bindValue(':user_id', $result_row->id, PDO::PARAM_INT);
                    $query->execute();
                }
            }
        }
    }
}
    
asked by anonymous 21.07.2015 / 00:43

1 answer

3

The only thing I would recommend is using some kind of nonce .

A nonce is a number that is used only once (for example, a GUID). When the page is created, you also create a nonce with a certain key. When POST is made to the server, it passes the nonce together (either in the Header ) or in the body content). On the server side, make sure that nonce exists in the session, and if so, accept the login.

Why the nonce?

The purpose of nonce in Web applications is to confirm that the POST is being made from a trusted source - in this case, your page, which is the only place you have the logical to create nonce , because it uses the same algorithm that the server uses to verify it. So I can not send a POST to this page and try to log into your site through another.

Beyond That

I see that you are using a number of good practices, including paramaterization of your queries to the database.

    
21.07.2015 / 00:57