{"id":728,"date":"2022-08-30T15:06:11","date_gmt":"2022-08-30T15:06:11","guid":{"rendered":"https:\/\/unknownerror.org\/index.php\/2013\/11\/09\/codeigniter-2-1-0-jquery-login-security-questions-collection-of-common-programming-errors\/"},"modified":"2022-08-30T15:06:11","modified_gmt":"2022-08-30T15:06:11","slug":"codeigniter-2-1-0-jquery-login-security-questions-collection-of-common-programming-errors","status":"publish","type":"post","link":"https:\/\/unknownerror.org\/index.php\/2022\/08\/30\/codeigniter-2-1-0-jquery-login-security-questions-collection-of-common-programming-errors\/","title":{"rendered":"CodeIgniter 2.1.0, jQuery: Login Security questions-Collection of common programming errors"},"content":{"rendered":"<p>I have a website where I do the following in order to let people log in:<\/p>\n<p><strong>jQuery:<\/strong> &#8211; using the $.md5() plugin and the $.cookie() plugin (for CodeIgniter CSRF protection cookie for post data)<\/p>\n<pre><code>$('#login').on('submit', function(e){\n  e.preventDefault();\n  $.get('\/salt\/', function(salt){\n  \/\/ get \/ set salt and salt flashdata\n    $.post('\/login\/', {\n      'username' : $('#l_username').val(),\n      'password' : $.md5('' + salt['m'] + $.md5($('#l_password').val()) + salt['s']),\n      \/\/set to string (in case MD5 hash of password is an integer)hash password, add salt, hash salted password. \n      '_token' : $.cookie('_cookie')\n    }, function(r){\n      \/\/ *r* == 0: unknown user; 1: wrong password; 2: logged in; 3: database error; 4: session error;\n      \/\/ perform action depending on *r* value\n    })\n  })\n});\n<\/code><\/pre>\n<p><strong>PHP (CodeIgniter):<\/strong> &#8211; rout file forwards <code>\/salt\/<\/code> and <code>\/login\/<\/code> to <code>\/process\/salt\/<\/code> and <code>\/process\/login\/<\/code> &#8211; &#8216;session&#8217; class is autoloaded<\/p>\n<pre><code>class Process extends CI_Controller {\n\n  public function __construct() {\n    parent::__construct();\n    $this-&gt;load-&gt;model('login'); \/\/ loads model with login functions ()\n  }\n\n  public function login() {\n    $data = array();\n    $data['username'] = $this-&gt;input-&gt;post('username');\n    $data['password'] = $this-&gt;input-&gt;post('password');\n    if ($data['username'] &amp;&amp; $data['password']) {\n    \/\/if user and password are set\n      $user = $this-&gt;login-&gt;getUser($data['username']);\n      if ($user-&gt;num_rows() === 1) {\n      \/\/ if the username is in the database\n        $user = $user-&gt;row();\n        $salt = $this-&gt;session-&gt;flashdata('s');\n        \/\/flashdata set by ajax call to '\/salt\/'\n        $pass = $salt-&gt;m . $user-&gt;password . $salt-&gt;s;\n        \/\/password is already hashed in database\n        if ($data['password'] === $pass){\n          \/\/ update logged_in database table then set login session\n          echo 2; \/\/ logged in; if database \/ session error, echo 3 \/ 4\n        } else {\n          echo 1; \/\/ wrong password\n        }\n      } else {\n        echo 0; \/\/unknown user\n          }\n    }\n  }\n\n  public function salt() {\n    \/\/ set salt values based in minute and second time\n    $salt = (object) array('m' =&gt; intval(date('i')), 's' =&gt; intval(date('s')));\n    \/\/intval() removes leading 0\n    $this-&gt;session-&gt;set_flashdata('s', $salt);\n    \/\/ flashdata only valid for the next server request (which is the login);\n    header('Content-type: application\/json');\n    \/\/return value as json string\n    echo json_encode($salt);\n  }\n}\n<\/code><\/pre>\n<p>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.<\/p>\n<ol>\n<li>\n<p>If I&#8217;m guessing correctly, you are storing a md5-hashed version of the password in your database?<\/p>\n<p>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&#8217;t have a hash in your database which is pretty easy to decifer if it&#8217;s an easy password.<\/p>\n<p>Even better would be to use a personal salt for each user, which you can get at the same time as the other salts.<\/p>\n<pre><code>'password' : $.md5('' + salt['m'] + $.md5($('#l_password').val() + salt['personal']) + salt['s']),\n<\/code><\/pre>\n<p>Another advice would be to use a stronger hashing function than md5 since that isn&#8217;t so secure anymore.<\/p>\n<p>This is all however in case your database gets leaked. The transfer of the password and username looks pretty secure and obfuscated though.<\/p>\n<p>I think you still have a bug, since you don&#8217;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:<\/p>\n<pre><code>\/\/flashdata set by ajax call to '\/salt\/'\n$pass = md5($salt-&gt;m . $user-&gt;password . $salt-&gt;s);\n<\/code><\/pre>\n<p>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.<\/p>\n<\/li>\n<\/ol>\n<p id=\"rop\"><small>Originally posted 2013-11-09 21:44:12. <\/small><\/p>","protected":false},"excerpt":{"rendered":"<p>I have a website where I do the following in order to let people log in: jQuery: &#8211; using the $.md5() plugin and the $.cookie() plugin (for CodeIgniter CSRF protection cookie for post data) $(&#8216;#login&#8217;).on(&#8216;submit&#8217;, function(e){ e.preventDefault(); $.get(&#8216;\/salt\/&#8217;, function(salt){ \/\/ get \/ set salt and salt flashdata $.post(&#8216;\/login\/&#8217;, { &#8216;username&#8217; : $(&#8216;#l_username&#8217;).val(), &#8216;password&#8217; : $.md5(&#8221; [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-728","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/posts\/728","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/comments?post=728"}],"version-history":[{"count":0,"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/posts\/728\/revisions"}],"wp:attachment":[{"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/media?parent=728"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/categories?post=728"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/unknownerror.org\/index.php\/wp-json\/wp\/v2\/tags?post=728"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}