CodeIgniter 2.1.0, jQuery: Login Security questions-Collection of common programming errors

I have a website where I do the following in order to let people log in:

jQuery: – using the $.md5() plugin and the $.cookie() plugin (for CodeIgniter CSRF protection cookie for post data)

$('#login').on('submit', function(e){
  e.preventDefault();
  $.get('/salt/', function(salt){
  // get / set salt and salt flashdata
    $.post('/login/', {
      'username' : $('#l_username').val(),
      'password' : $.md5('' + salt['m'] + $.md5($('#l_password').val()) + salt['s']),
      //set to string (in case MD5 hash of password is an integer)hash password, add salt, hash salted password. 
      '_token' : $.cookie('_cookie')
    }, function(r){
      // *r* == 0: unknown user; 1: wrong password; 2: logged in; 3: database error; 4: session error;
      // perform action depending on *r* value
    })
  })
});

PHP (CodeIgniter): – rout file forwards /salt/ and /login/ to /process/salt/ and /process/login/ – ‘session’ class is autoloaded

class Process extends CI_Controller {

  public function __construct() {
    parent::__construct();
    $this->load->model('login'); // loads model with login functions ()
  }

  public function login() {
    $data = array();
    $data['username'] = $this->input->post('username');
    $data['password'] = $this->input->post('password');
    if ($data['username'] && $data['password']) {
    //if user and password are set
      $user = $this->login->getUser($data['username']);
      if ($user->num_rows() === 1) {
      // if the username is in the database
        $user = $user->row();
        $salt = $this->session->flashdata('s');
        //flashdata set by ajax call to '/salt/'
        $pass = $salt->m . $user->password . $salt->s;
        //password is already hashed in database
        if ($data['password'] === $pass){
          // update logged_in database table then set login session
          echo 2; // logged in; if database / session error, echo 3 / 4
        } else {
          echo 1; // wrong password
        }
      } else {
        echo 0; //unknown user
          }
    }
  }

  public function salt() {
    // set salt values based in minute and second time
    $salt = (object) array('m' => intval(date('i')), 's' => intval(date('s')));
    //intval() removes leading 0
    $this->session->set_flashdata('s', $salt);
    // flashdata only valid for the next server request (which is the login);
    header('Content-type: application/json');
    //return value as json string
    echo json_encode($salt);
  }
}

My question is this: exactly how secure is this log in method? Are there any potential loopholes in the script? I want to make sure that the log in process is as secure as possible without https for the moment.

  1. If I’m guessing correctly, you are storing a md5-hashed version of the password in your database?

    Would it not be better to store a hash of the password with a global salt and pass that global salt as well to the javascript? This way you don’t have a hash in your database which is pretty easy to decifer if it’s an easy password.

    Even better would be to use a personal salt for each user, which you can get at the same time as the other salts.

    'password' : $.md5('' + salt['m'] + $.md5($('#l_password').val() + salt['personal']) + salt['s']),
    

    Another advice would be to use a stronger hashing function than md5 since that isn’t so secure anymore.

    This is all however in case your database gets leaked. The transfer of the password and username looks pretty secure and obfuscated though.

    I think you still have a bug, since you don’t make a md5 hash of your salted password on the php-side when comparing it to the received value. I think it should be:

    //flashdata set by ajax call to '/salt/'
    $pass = md5($salt->m . $user->password . $salt->s);
    

    For hashing on the serverside could you use the spark-version of phpass, which everyone advices to use. Or you could just install it as a library.

Originally posted 2013-11-09 21:44:12.