Security in login system

Asked

Viewed 1,580 times

8

Hello, I made a torrent file download and upload system, and on this site has a login system with a database, and I would like you to explain a little bit about security in PHP and mysql and what I should do to maintain the security of the site.

If you can avoid sending me a material/topic in English or send me to Google I would appreciate it.

System to register

    include("connection.php");

$login = $_POST['login_cadastro'];
$senha = $_POST['senha_cadastro'];
$confirmarsenha = $_POST['confirmarsenha_cadastro'];
$email = $_POST['email_cadastro'];

if($senha != $confirmarsenha)
{   
    echo "<meta http-equiv='refresh' content='0; url=index.php'>
          <script type='text/javascript'>alert('As senhas estão diferentes')</script>";
}
else
{
    $sqlpegar = mysqli_query($coneccao, "SELECT * FROM usuarios");

    while($linha = mysqli_fetch_array($sqlpegar))
    {   
        $login_db = $linha['login'];
        $email_db = $linha['email'];
    }

    if($login_db == $login)
    {
        echo "  <meta http-equiv='refresh' content='0'>
                <script type='text/javascript'>alert('Esse usuario já existe')</script>";
    }
    if($email_db == $email)
    {
        echo "  <meta http-equiv='refresh' content='0'>
                <script type='text/javascript'>alert('Esse email já esta sendo usado')</script>";
    }
    else
    {

        $sql = mysqli_query($coneccao, "INSERT INTO usuarios(login, senha, email) VALUES ('$login', '$senha', '$email')");  
        header("location: index.php");  
    }
}

mysqli_close($coneccao);

System to log in

    include "connection.php";

$login = $_POST['login_entrar'];
$senha = $_POST['senha_entrar'];

$sql = mysqli_query($coneccao, "SELECT * FROM usuarios WHERE login = '$login'");    


while($linha = mysqli_fetch_array($sql))
{
    $senha_db = $linha['senha'];
    $login_db = $linha['login'];
}

$cont = mysqli_num_rows($sql);

if($login_db != $login || $login == "")
{       
    echo "<meta http-equiv='refresh' content='0; url=index.php'>
    <script type='text/javascript'>alert('Este usuario não existe')</script>";      
}
else
{
    if($senha_db != $senha )
    {
        echo "<meta http-equiv='refresh' content='0; url=index.php'>
        <script type='text/javascript'>alert('Senha incorreta')</script>";  
    }
    else
    {
        session_start();

        $_SESSION['login_usuario'] = $login;

        header("location: index.php");  
    }
}

mysqli_close($coneccao);

Connection.php

$coneccao = mysqli_connect("localhost", "root", "");
mysqli_select_db($coneccao, "luppbox");

2 answers

4


Hello, this is a "doodle" of a tutorial login system that I recently created, it is not the most complex, but it will help you understand the basics about security currently.

Login & registration form

<?php
// index.php
require_once("database.php");
// [Nao usar estes modelos em aplicacoes reais]
// Estes são apenas para demonstração e teste, e não são nada seguros
if(isset($_POST["submit"]) && isset($_POST["tipo"]) && $_POST["tipo"] === "novo"){
    $usuario = $_POST["usuario"];
    $email = filter_var($_POST['email'], FILTER_SANITIZE_EMAIL);
    $password = $_POST["password"];
    // Limpar caracteres invalidos
    // Incompleto [Nao usar estes modelos em aplicacoes reais]
    // Validar Letras para o usuarios e remover espaços brancos com o trim()
    $usuario = preg_replace("/[^A-Za-z0-9]/","", $usuario) ? trim($usuario) : NULL;
    // Validar Conjunto alfa-numérico para o password e remover espaços brancos com o trim()
    $password = preg_match("/[A-Za-z0-9]/", $password) ? trim($password) : NULL;
    if($usuario !== NULL && $password !== NULL){
        $sim = registar($usuario, $email, $password);
        if($sim){
            header("Location: index.php");
            exit;
        } else {
            //Erro para o caso de o usuario ja existir, ou nao ser armazenado
            echo "<span style=\"color:red;\">erro: cadastro falhou, tente novamente.</span>";
            exit;
        }
    } else {
        //Erro para o caso de a senha, usuario, email estar(em) vazio(s)
        echo "<span style=\"color:red;\">erro: usuario/senha vazio(s)</span>";
        exit;
    }
}

if(isset($_POST["submit"]) && isset($_POST["tipo"]) && $_POST["tipo"] === "entrar"){
    $usuario = $_POST["usuario"];
    // $email = $_POST["email"];
    $password = $_POST["password"];
    // Limpar caracteres invalidos
    $usuario = preg_replace("/[^A-Za-z0-9]/","", $usuario) ? trim($usuario) : NULL;
    $password = preg_match("/[A-Za-z0-9]/", $password) ? trim($password) : NULL;
    if($usuario !== NULL && $password !== NULL){
        $sim = login($usuario, $password);
        if($sim){
            header("Location: privado.php");
            exit;
        } else {
            //Erro para o caso de o usuario nao ser encontrado ou para o caso de os dados nao corresponderem
            echo "<span style=\"color:red;\">erro: usuario/senha nao encontrados</span>";
            exit;
        }
    } else {
        //Erro para o caso de a senha e usuario estar(em) vazio(s)
        echo "<span style=\"color:red;\">erro: usuario/senha vazio(s)</span>";
        exit;
    }
}
?>
<!-- HTML !-->
<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Login/Cadastro [Seguro]</title>
    </head>
    <body>
        <?php 
            if(isset($_GET["opcao"]) && $_GET["opcao"] === "entrar"){
            ?>
            <!-- LOGIN !-->
            <h1>Login</h1>
            <form method="POST" action="index.php">
                <input type="hidden" name="tipo" value="entrar"/>
                Usuario:<br/>
                <input type="text" name="usuario" value="" size="40"/><br/>
                Password:<br/>
                <input type="password" name="password" value="" size="40"/><br/>
                <input type="submit" name="submit" value="Entrar"/>
            </form>
            <a href="index.php?opcao=novo">Cadastrar</a><br/><br/>
            <?php
            } elseif(isset($_GET["opcao"]) && $_GET["opcao"] === "novo"){
            ?>
            <!-- CADASTRO !-->
            <h1>Cadastrar</h1>
            <form method="POST" action="index.php">
                <input type="hidden" name="tipo" value="novo"/>
                Usuario:<br/>
                <input type="text" name="usuario" value="" size="40"/><br/>
                Email:<br/>
                <input type="email" name="email" value="" size="40"/><br/>
                Password:<br/>
                <input type="password" name="password" value="" size="40"/><br/>
                <input type="submit" name="submit" value="Entrar"/>
            </form>
            <a href="index.php?opcao=entrar">Login</a><br/><br/>
            <?php
            } else {
            ?>
            <!-- LOGIN !-->
            <h1>Login</h1>
            <form method="POST" action="index.php">
                <input type="hidden" name="tipo" value="entrar"/>
                Usuario:<br/>
                <input type="text" name="usuario" value="" size="40"/><br/>
                Password:<br/>
                <input type="password" name="password" value="" size="40"/><br/>
                <input type="submit" name="submit" value="Entrar"/>
            </form>
            <a href="index.php?opcao=novo">Cadastrar</a><br/><br/>
            <?php
            }
            ?>
    </body>
</html>

The Login and Registration Form are in a single file along with the validation for both. In a login and registration script, it is always crucial sanear, validar information from the forms to verify that they are of the right size, or that they are not problematic. Pay attention to the notes in the script, and notice that I haven’t even used a decent validation.

Database

<?php
// database.php
session_start();
// Em fase de correcção não vamos querer essa função no ativo
// Se esta linha for descomentada, os erros estarao visiveis apenas no log.log
// ini_set("error_reporting", "E_ALL");
require_once("blowfish.php");

// Host, normalmente é o local
DEFINE("HOST", "localhost");
// O port, na maior parte das vezes é dispensavel
// Ainda assim o POST inicia conexoes seguras/nao seguras dependendo do PORT usado
// DEFINE("PORT", "80");
// Usuario do banco de dados
DEFINE("USR", "root");
// Senha do usuario do banco de dados
DEFINE("PWD", "");
// Banco de dados
DEFINE("BD", "_banco_de_dados_em_uso_");

// Ficheiro de log, caso não exista, crie um manualmente;
// Ou crie uma função que o faça de forma segura e autonoma
$error_log = "log.log";

$db = new mysqli(HOST, USR, PWD, BD);

if(mysqli_connect_errno()){
    error_logi("Conexao falhou", mysqli_connect_error());
}

function error_logi($error,$msg=""){
    global $error_log;
    
    $log_msg = $error . " : " . $msg . PHP_EOL;
    
    return file_put_contents($error_log, $log_msg, FILE_APPEND | LOCK_EX);
    exit;
}

// Função para encontrar usuario por nome;
function encontrar_usuario($usuario){
    global $db;
    ($stmt = $db->prepare("SELECT username,senha FROM usuarios WHERE username=?")) || error_logi("STMT Encontrar Usuario", $db->error);
    
    $stmt->bind_param('s', $usuario) || error_logi("STMT Bind Param", $db->error);
    
    $stmt->execute() || error_logi("STMT Execute", $db->error);
    
    $stmt->bind_result($username,$senha) || error_logi("STMT Bind Result", $db->error);
    
    $stmt->fetch();
    
    $result = ["username"=>$username,"senha"=>$senha];
    
    return $result;
}
// Função para efectuar o registo;
function registar($usuario, $email, $password){
    global $db;
    $password = hash_password($password);
    ($stmt = $db->prepare("INSERT INTO usuarios (username, email, senha) VALUES (?, ?, ?)"))
    || error_logi("SQL Prepared Statment",$db->error);
    ($stmt->bind_param('sss', $usuario, $email, $password)) || error_logi("SQL BindParam",$db->error);
    $exec = $stmt->execute() ? true : error_logi("SQL Execute",$db->error);
    return $exec;
    $stmt->close();
    $db->close();
}
// Tentar fazer o login
function login($usuario, $password){
    $usuario = encontrar_usuario($usuario);
    if($usuario){
        // usuario encontrado
        // Verificar a hash para a password
        if(verifica_hash($password, $usuario["senha"])){
            $_SESSION["usuario"] = $usuario["username"];
            return true;
        } else {
            // hash não encontrada
            return false;
        }
    } else {
        // usuario não encontrado
        return false;
    }
}

function check_login($usuario){
    $existe = encontrar_usuario($usuario);
    if($existe){
        return $existe["username"] === $usuario ? true : false;
    } else {
        return false;
    }
}

?>

In the database the password field must be of the type VARCHAR(60) in order to be able to store the hash.

Private page (restricted to people without a session)

<?php
// privado.php
// Esta é a página protegida
require_once("database.php");

if(isset($_SESSION["usuario"])){
    if(check_login($_SESSION["usuario"])){
        echo "Logado";
        // Isto é um sistema para teste, daí usar esta função aqui
        // Significa que a página só pode ser visualizada apenas 1 vez por login
        session_destroy();
    } else {
        header("Location:index.php");
        exit;
    }
} else {
    header("Location:index.php");
    exit;
}
?>

HASH

<?php
// blowfish.php
// Script util apenas para versões do PHP < 5.5.0;
// Função que gera a hash
function hash_password($password){
    $formato = "$2y$10$";
    $salt = salt(22);
    $formato_salt = $formato.$salt;
    $password_hash = crypt($password, $formato_salt);
    return $password_hash;
    // Se algo correr mal a função vai retornar falso;
}
// Função que gera o salt
function salt($tamanho){
    //$random = md5(uniqid(mt_rand(), true));
    // ambas funções geram valores aleatorios
    $random = md5(uniqid(mcrypt_create_iv(22, MCRYPT_DEV_URANDOM), true));
    $base = base64_encode($random);
    $base64 = str_replace('+', '.', $base);
    $salt = substr($base64, 0, $tamanho);
    return $salt;
}
// Função para comparar as duas hash
function verifica_hash($password, $hash_existente){
    $hash = crypt($password, $hash_existente);
    if($hash === $hash_existente){
        return true;
    } else {
        return false;
    }
}


?>

In this script above, the functions are only for the PHP < 5.5.0, for higher versions recommend replacing the functions in the database.php file hash_password, verifica_hash by the new functions officially introduced by PHP >= 5.5.0:

password_hash() password_verify()

And if possible, use these functions instead of using the file blowfish.php.

there is also a API for versions of PHP >= 5.3.7 that initializes these new functions of PHP >= 5.5.0 in lower versions, it is sufficient that the script in use makes a require of this API.

PASSWORD COMPAT API

Other safety tips:

*Avoid users of type root to handle customer applications.

*Avoid saving passwords as texto simples or plain-text for English.

*Requests of the type GET must always be idempotentes, that is, unable to make modifications on the server side (for modifications use POST).

In addition to these cited, there are several points where one should pay attention. However I leave only this summary, and good luck.

Wikihow - How to create a Secure login script in PHP and Mysql

Github - PHP Login Advanced

  • Hello, I don’t understand what private.php is for.

  • Hello, imagine the page privado.php is the page that users can only view if they are authenticated.

3

Storing Passwords in the Database

It is recommended that when storing passwords in the database, you do not do so in order to store them as text-Plain (or plain text). One way to save them would be to use hashs and Salts. There is a very didactic example here.

Avoiding SQL Injection

As for the connection and execution of queries in the database, it is important to use Statements to prevent SQL Injection attacks. This is a type of attack that allows whoever is running it to execute malicious queries when you perform some query with concatenated variables.

Instead of concatenating the attributes in the query string, use wildcards and statements

Change that:

$sql = mysqli_query($coneccao, "SELECT * FROM usuarios WHERE login = '$login'"); 

for:

$mysqli = new mysqli('localhost', 'usuario', 'senha', 'database');
$stmt = $mysqli->prepare("SELECT * FROM usuarios WHERE login = ?");
$stmt->bind_param('s', $login);
$stmt->execute();

This way you indicate to the database that you will send an attribute instead of the wildcard character '?' in the prepare() method. It then informs his type in bind_param, sending’s' to indicate that it will be a string. More information about the types of data that can be sent here (in Parameters > types)

For example purposes follow a code snippet with more than 1 parameter (and with different types):

$stmt = $mysqli->prepare("SELECT * FROM cidades WHERE populacao > ? AND estado = ?")
$stmt->bind_param('is', 42000, "PR"); // 42000 é inteiro (i), "PR" é string (s)
$stmt->execute();

Browser other questions tagged

You are not signed in. Login or sign up in order to post.