title: Secure signin with unique token
body: <p>My user authorization process looks like that.</p>
  <p>During signin process function fires following code.</p>
  <pre><code> $token = sha1(microtime(true) . mt_rand(10000, 90000));
   setcookie('auth', $token, $timeout);
  </code></pre>
  <p>Then adds generated <code>token</code> into database table right after user id column. BTW, in both <code>$_SESSION</code> and <code>$_COOKIE</code> storing only <code>$token</code> value.</p>
  <p>There is another function named <code>protect</code> that comes at the top of every page and checks if cookies exist:</p>
  <ol>
  <li>at first checks db table for <code>$token</code>: if there is no user with this
  token, signs out</li>
  <li>Then checks <code>$_SERVER['HTTP_USER_AGENT']</code>, <code>$_SERVER['REMOTE_ADDR']</code> with ones that stored in db tables.</li>
  </ol>
  <h1>Question</h1>
  <p>I feel that this is not secure way, because if attacker uses same PC and same browser, can get access simply by &quot;copying-pasting&quot; cookies. Also $_SERVER['REMOTE_ADDR'] doesn't always work.</p>
  <p>How can I make this login more secure?</p>
  <h1>Detailed</h1>
  <p>Here is function protect</p>
  <pre><code>public function protect() {
      if (!isset($_SESSION)) {
          session_start();
      }
      $data = array();
  
      if (isset($_SESSION['auth'])) {
          $stmt = $this-&gt;db-&gt;prepare(&quot;SELECT l.browser, l.ip, u.ban from log AS l, users AS u WHERE l.token =? AND u.id=l.user_id LIMIT 1&quot;) or die($this-&gt;db-&gt;error);
          $stmt-&gt;bind_param(&quot;i&quot;, $_SESSION['auth']) or die($stmt-&gt;error);
          $stmt-&gt;execute() or die($stmt-&gt;error);
          $stmt-&gt;store_result();
          if ($stmt-&gt;num_rows == 0) {
              $this-&gt;signout();
          }
          $stmt-&gt;bind_result($data['browser'], $data['ip'], $data['ban']);
          $stmt-&gt;fetch() or die($stmt-&gt;error);
          $stmt-&gt;close() or die($stmt-&gt;error);
          $this-&gt;validation-&gt;check(&quot;protection&quot;, $data);
      } else {
          if (!isset($_COOKIE['auth'])) {
              header('Location:' . wsurl);
          }
          $stmt = $this-&gt;db-&gt;prepare(&quot;SELECT l.browser, l.timeout, l.ip, u.ban from log AS l, users AS u where l.token =? AND u.id=l.user_id LIMIT 1&quot;) or die($this-&gt;db-&gt;error);
          $stmt-&gt;bind_param(&quot;s&quot;, $_COOKIE['auth']) or die($stmt-&gt;error);
          $stmt-&gt;execute() or die($stmt-&gt;error);
          $stmt-&gt;store_result();
          if ($stmt-&gt;num_rows == 0) {
              $this-&gt;signout();
          }
          $stmt-&gt;bind_result($data['browser'], $data['timeout'], $data['ip'], $data['ban']) or die($stmt-&gt;error);
          $stmt-&gt;fetch() or die($stmt-&gt;error);
          $this-&gt;validation-&gt;check(&quot;protection&quot;, $data);
          session_regenerate_id();
          $_SESSION['auth'] = $_COOKIE['auth'];
          $stmt-&gt;close() or die($stmt-&gt;error);
      }
  }
  </code></pre>
  <p>And Validation checks this</p>
  <pre><code>if ($data['browser'] != md5($_SERVER['HTTP_USER_AGENT'])) {
              $this-&gt;registration-&gt;signout();
          }
  
          if ($data['ban'] == 1) {
              $this-&gt;registration-&gt;signout(false);
              header('Location:' . wsurl . &quot;?page=msg&amp;id=34&quot;);
          }
          if ($data['ip'] != $this-&gt;common-&gt;getIP(&quot;long&quot;)) {
              $this-&gt;registration-&gt;signout();
          }
  
          if (isset($data['timeout']) &amp;&amp; !empty($data['timeout'])) {
              if (($data['timeout'] - $this-&gt;common-&gt;getTime()) &lt; 0) {
                  $this-&gt;registration-&gt;signout();
              }
          }
  </code></pre>