Login System using PHP and PDO Prepared Statement vir.php

&lt;?php 

require_once '../db.php';

session_start();

if($_SERVER['REQUEST_METHOD'] == 'POST'){ 

$username = trim($_POST['username']);

try{

  $Query = "SELECT * FROM users WHERE username = :username";
  $statement = $conn-&gt;prepare($Query);
  $statement-&gt;bindValue(':username', $username);
  $statement-&gt;execute();
  $user = $statement-&gt;fetch(PDO::FETCH_ASSOC);    
  $RowCount = $statement-&gt;rowCount();

} catch (PDOerrorInfo $e){

    die('QuerySCD Error '.$e-&gt;getMessage());

  }

  if( $RowCount == 0 ){
   // User doesn't exist
    $_SESSION['message'] = "error!";
    header("location: error-login.php");

  } else{ // User exists

      if( password_verify($_POST['password'], $user['password'])){

        $_SESSION['username']  = $user['username'];
        $_SESSION['active']    = $user['active'];

        $_SESSION['logged_in'] = true;

        header("location: riscar.php");

      } else {

          $_SESSION['message'] = "error!";
          header("location: error-login.php");

        }      
    }  
}

$conn = NULL;

?&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang="pt_BR"&gt;
&lt;head&gt;
    &lt;title&gt;Login&lt;/title&gt;
    &lt;meta charset="UTF-8"&gt;
&lt;/head&gt;

&lt;body&gt;

    &lt;form action="vir.php" method="post" autocomplete="off"&gt;

        &lt;input type="username" required autocomplete="off" name="username"&gt;
        &lt;span data-placeholder="User"&gt;&lt;/span&gt;

        &lt;input type="password" required autocomplete="off" name="password"&gt;
        &lt;span  data-placeholder="Password"&gt;&lt;/span&gt;

        &lt;button name="login"&gt;
            Logar
        &lt;/button&gt;

    &lt;/form&gt;

&lt;/body&gt;
&lt;/html&gt;


ir.php

&lt;?php
/* Log out process, unsets and destroys session variables */
session_start();
session_unset();
session_destroy(); 
?&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang="pt_BR"&gt;
&lt;head&gt;
    &lt;title&gt;Login&lt;/title&gt;
    &lt;meta charset="UTF-8"&gt;
&lt;/head&gt;

&lt;body&gt;

    &lt;p&gt;&lt;?= 'You have been logged out!'; ?&gt;&lt;/p&gt;

&lt;/body&gt;
&lt;/html&gt;


error-login.php

&lt;?php
/* Displays all error messages */
session_start();
?&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang="pt_BR"&gt;
&lt;head&gt;
    &lt;title&gt;Login&lt;/title&gt;
    &lt;meta charset="UTF-8"&gt;
&lt;/head&gt;

&lt;body&gt;

    &lt;h4&gt;
        &lt;?php 
        if( isset($_SESSION['message']) AND !empty($_SESSION['message']) ): 
            echo $_SESSION['message'];    
        else:
            header( "location: vir.php" );
        endif;
        ?&gt;
    &lt;/h4&gt; 

&lt;/body&gt;
&lt;/html&gt;


Only me will use this login sytem and only me will have the link to this login system, i will use this to insert records on my database.

I don't know if someone could find the link to this login, to i used PDO prepared statement, so prevent against SQL Injection.

How safe is my code?
How safe is my code?


I would suggest if you protect the login form from


CSRF (Cross site request forgery) Attacks by generating a unique code for each session and assigning it to a hidden field in the form, and check every post request whether it matches the one from the session or not.




&lt;input type = "hidden" name="token" value="' . $_SESSION['csrf_token'] . '"&gt;




Example: ignore naming convention

   class token {

     public static function generate_csrf_token() {
           if (!isset($_SESSION['csrf_token'])) {
              $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
           }
        }

    public static function check_csrf_token($token) {
        return $token === $_SESSION['csrf_token'];
    }
 }


Then you want to check when the form is submitted and compare both token.


Timing attacks, disable logins requests under a second, mostly bots and robots.



    //Gets the value when the page loads and set it in a session
    $start = microtime(true);

    if (!isset($_SESSION['microtime'])) {
     $_SESSION['microtime'] = $start;
    }


and after the login button is clicked, check and perform the same and then compare the time

if (isset($_POST['submit'])) {
   $finish = microtime(true);
   $seconds_passed = $finish - $_SESSION['microtime'];
   if ($seconds_passed &lt; 1) {

?&gt;



Limit requests from an ip, so for example say, 3 requests maximally allowed per a second, to protect from lots of attack.
Putting captcha and login attempts will be useful.
Session Hijacking multiple things:



Have an identification method that is making each user unique (perhaps hashing user agent and ip) and check every request if they match to see if the current user is the same. And have a session requests tracker that would re-assign a new session id after 25 requests for example. 

 if (!isset($_SESSION['session_clicked_times'])) {
            $_SESSION['session_clicked_times'] = 1;
        } else {
            if (($_SESSION['session_clicked_times']) &gt;= 25) {
                $_SESSION['session_clicked_times'] = 0;
                session_regenerate_id(true);
            }
            $_SESSION['session_clicked_times'] += 1;
        }


Also generate a new id once logged in, and so on.

This code:

 if( $RowCount == 0 ){
   // error   
  } else{ // User exists

      if(password_verify($_POST['password'], $user['password'])){

     //login

      } else {
         //error
        }      
    }  
}


to just this (since they both handle the same thing in your case)

 if( $RowCount == 0 || !password_verify($_POST['password'], $user['password']) ){
       // error   
      } else { 
       //login
    }