title: How secure is my PHP login system?
body: <p>I'm new to PHP and this is also my first log in system so it would be great if you guys could look over my code and see if you can spot any security holes:</p>
  
  <p>note: I am sanitizing all user input although it's not shown here.</p>
  
  <h2>Sign Up:</h2>
  
  <p><strong>Step 1:</strong> I take the password the user chose and run it through this function:</p>
  
  <pre><code>encrypt($user_chosen_password, $salt);
  
  function encrypt($plain_text, $salt) {
      if(!$salt) {
          $salt = uniqid(rand(0, 1000000));
      }
      return array(
          'hash' =&gt; $salt.hash('sha512', $salt.$plain_text),
          'salt' =&gt; $salt
      );
  }
  </code></pre>
  
  <p><strong>Step 2:</strong> I then store the hash and the salt (<code>$password['hash']</code> and <code>$password['salt']</code>) in the users table in the database:</p>
  
  <pre><code>id | username | password  | salt       | unrelated info...
  -----------------------------------------------------------
  1  | bobby    | 809a28377 | 809a28377f | ...
                  fd131e5934
                  180dc24e15
                  bbe5f8be77
                  371623ce36
                  4d5b851e46
  </code></pre>
  
  <h2>Log In:</h2>
  
  <p><strong>Step 1:</strong> I take the username the user entered and do a look up on the database to see if any rows are returned. On my site no 2 users can share the same
  username so the username field always has a unique value. If I get 1 row returned I grab the salt for that user.</p>
  
  <p><strong>Step 2:</strong> Then I run the user entered password through the encrypt function (as previously posted above) but this time I also supply the salt retrieved from the database:</p>
  
  <pre><code>encrypt($user_entered_password, $salt);
  </code></pre>
  
  <p><strong>Step 3:</strong> I now have the proper password to match against in this variable: <code>$password['hash']</code>. So I so a second lookup on the database to see if the
  username entered and the hashed password together return a single row. If so then the user's credentials are correct.</p>
  
  <p><strong>Step 4:</strong> In order to log the user in after their credentials passed I generate a random unique string and hash it:</p>
  
  <pre><code>$random_string = uniqid(rand(0, 1000000));
  $session_key = hash('sha512', $random_string);
  </code></pre>
  
  <p>I then insert the <code>$session_key</code> into the <code>active_sessions</code> table in the database:</p>
  
  <pre><code>user_id | key
  ------------------------------------------------------------
  1       | 431b5f80879068b304db1880d8b1fa7805c63dde5d3dd05a5b
  </code></pre>
  
  <p><strong>Step 5:</strong></p>
  
  <p>I take the unencrypted unique string generated in the last step (<code>$random_string</code>) and set that as the value of a cookie which I call <code>active_session</code>:</p>
  
  <pre><code>setcookie('active_session', $random_string, time()+3600*48, '/');
  </code></pre>
  
  <p><strong>Step 6:</strong></p>
  
  <p>At the top of my <code>header.php</code> include there is this check:</p>
  
  <pre><code>if(isset($_COOKIE['active_session']) &amp;&amp; !isset($_SESSION['userinfo'])) {
     get_userinfo();
  }
  </code></pre>
  
  <p>The <code>get_userinfo()</code> function does a lookup on the <code>users</code> table in the database and returns an associative array which is stored in a session called <code>userinfo</code>:</p>
  
  <p>// first this function takes the value of the active_session cookie and hashes it to get the session_key:</p>
  
  <pre><code>hash('sha512', $random_string);
  </code></pre>
  
  <p>// then it does a lookup on the <code>active_sessions</code> table to see if a record by this <code>key</code> exists, if so it will grab the <code>user_id</code> associated with that record and use this to do a second lookup on the <code>users</code> table to get the <code>userinfo</code>:</p>
  
  <pre><code>    $_SESSION['userinfo'] = array(
          'user_id'           =&gt; $row-&gt;user_id,
          'username'          =&gt; $row-&gt;username,
          'dob'               =&gt; $row-&gt;dob,
          'country'           =&gt; $row-&gt;country,
          'city'              =&gt; $row-&gt;city,
          'zip'               =&gt; $row-&gt;zip,
          'email'             =&gt; $row-&gt;email,
          'avatar'            =&gt; $row-&gt;avatar,
          'account_status'    =&gt; $row-&gt;account_status,
          'timestamp'         =&gt; $row-&gt;timestamp,
      ); 
  </code></pre>
  
  <p>If the <code>userinfo</code> session exists I know the user is authenticated. If it doesn't exist but the <code>active_session</code> cookie exists then that check
  at the top of the <code>header.php</code> file will create that session.</p>
  
  <p>The reason why I am using a cookie and not sessions alone is to persist the login. So if the user closes the browser the session may be gone but the
  cookie will still exist. And since there is that check at the top of <code>header.php</code>, the session will be recreated and the user can function as a logged
  in user as normal.</p>
  
  <h2>Log Out:</h2>
  
  <p><strong>Step 1:</strong> Both the <code>userinfo</code> session and the <code>active_session</code> cookie are unset.</p>
  
  <p><strong>Step 2:</strong> The associated record from the <code>active_sessions</code> table in the database is removed.</p>
  
  <hr>
  
  <p>Notes: The only issue I can see (and perhaps there are many others), is if the user fakes that <code>active_session</code> cookie by creating it themselves in their browser. Of course they must set as that cookie's value a string which after it is encrypted must match a record in the <code>active_sessions</code> table from where I will retrieve the <code>user_id</code> to create that session. I am not sure what the chances of this is realistically, for a user (perhaps using an automated program) to guess a string correctly which they don't know will then be sha512 encrypted and matched against the string in the <code>active_sessions</code> table in the database to get the user id to build that session.</p>
  
  <p>Sorry for the big essay but since this is such a critical part of my site and due to my inexperience I just wanted to run it by more experienced developers to make sure it's actually safe. </p>
  
  <p>So do you see any security holes in this route and how can it be improved?</p>