Formularz logowania i rejestracji w PHP – szyfrowanie haseł

Zdjęcie laptopa w nocy

Ten wpis przedstawia w jaki sposób można stworzyć formularze logowania i rejestracji oraz w jaki sposób powinno się zapisywać hasła w bazie danych.

Na pewno większość czytelników jest świadoma tego, że zapisywanie haseł w bazie danych czystym tekstem (plain text) jest bardzo złym pomysłem. Osoba, która włamałaby się nam do bazy, widzi od razu wszystkie hasła naszych użytkowników oraz ich loginy/adresy email. Na pewno dużo naszych użytkowników używa takich samych lub podobnych haseł w różnych serwisach, więc włamywacz bez problemu mógłby użyć tych danych przechwytując konta użytkowników w innych serwisach.

Funkcja password_hash()

Do szyfrowania haseł skorzystamy z funkcji PHP o nazwie password_hash()

Tak wygląda jej składnia:

string password_hash ( string $password , integer $algo [, array $options ] )

Parametry:
string $password – hasło zdefiniowane przez użytkownika
integer $algo – stała opisująca algorytm hasła. Obecnie dostępne wartości to PASSWORD_DEFAULT oraz PASSWORD_BCRYPT

Na chwilę obecną PASSWORD_DEFAULT używa algorytmu BCrypt to tworzenia haszu hasła, ale w przyszłości może się to zmienić. Ustawiając stałą PASSWORD_BCRYPT narzucamy haszowanie haseł metodą BCrypt – wtedy funkcja zwraca nam 60-znakowy string lub wartość false. Zalecane jest ustawienie wartości PASSWORD_DEFAULT.

array $options – tablica asocjacyjna która może zawierać 2 indeksy: cost oraz salt. Cost oznacza ile razy algorytm ma się wykonać w celu uzyskania silnego skrótu. Czym większa ilość, tym czas operacji znacznie się wydłuża. Salt to sól służąca do szyfrowania haseł. W chwili obecnej nie zaleca się podawania tego parametru. Gdy jest pusty, sól zostaje automatycznie wygenerowana.

Przygotowanie

W tym przykładzie skorzystamy z bazy danych, w której będzie następująca tabela:

CREATE TABLE `user` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`email` VARCHAR(255) NOT NULL,
`password` VARCHAR(255) NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8 COLLATE=utf8_polish_ci ENGINE=InnoDB AUTO_INCREMENT=1

Mimo, że obecnie funkcja password_hash() zwraca 60 znaków, to jednak zalecane jest ustawienie wartości 255 dlatego, że jest ona ciągle aktualizowana.

Tworzymy plik config.php o następującej zawartości:

<?php
try{
  $db = new PDO('mysql:host=localhost;dbname=nazwa_bazy', 'nazwa_uzytkownika', 'haslo_do_bazy');
}
catch (PDOException $e){
  die ("Error connecting to database!");
}

Oczywiście uzupełniamy prawidłowe wartości dla hostu, nazwy bazy, nazwy użytkownika i hasło do bazy.

Formularz rejestracyjny

Tworzymy prosty formularz rejestracyjny w pliku registration.php

<?php
require_once("config.php");
if(isset($_POST['register'])){
  $email = $_POST['email'];
  $password = $_POST['password'];
  $hashPassword = password_hash($password,PASSWORD_BCRYPT);

  $sth = $db->prepare('INSERT INTO user (email,password) VALUE (:email,:password)');
  $sth->bindValue(':email', $email, PDO::PARAM_STR);
  $sth->bindValue(':password', $hashPassword, PDO::PARAM_STR);
  $sth->execute();

  die('Rejestracja pomyslna!');
}
?>
<h1>Formularz rejestracyjny</h1>
<form method="post">
  <input type="text" name="email" placeholder="Email">
  <input type="password" name="password" placeholder="Password">
  <button type="submit" name="register">Zarejestruj</button>
</form>

Jak widzimy na podstawie hasła przekazanego przez użytkownika tworzymy jego haszowaną wersję w zmiennej $hashPassword i tylko ją zapisujemy do bazy danych.

Formularz logowania

Tworzymy plik login.php z następującą zawartością:

<?php
require_once("config.php");
if(isset($_POST['login'])){
  $email = trim($_POST['email']);
  $password = trim($_POST['password']);

  $sth = $db->prepare('SELECT * FROM user WHERE email=:email limit 1');
  $sth->bindValue(':email', $email, PDO::PARAM_STR);
  $sth->execute();
  $user = $sth->fetch(PDO::FETCH_ASSOC);
  if($user){
    if(password_verify($password,$user['password'])){
      die("<h3>Uzytkownik zalogowany pomyslnie</h3>");
    }else{
      echo "<h3>Nieprawidlowe haslo</h3>";
    }
  }else{
    echo "<h3>Nie znaleziono uzytkownika</h3>";
  }
}
?>
<h1>Login</h1>
<form method="post">
  <input type="text" name="email" placeholder="Email">
  <input type="password" name="password" placeholder="Password">
  <button type="submit" name="login">Zaloguj</button>
</form>

Jak możemy zauważyć, najpierw z bazy danych pobieramy użytkownika na podstawie samego adresu email. Niestety SQL nie ma funkcji, która na poziomie zapytania do bazy danych mogłaby sprawdzić hash hasła. Jeśli email jest prawidłowy, sprawdzamy czy hash hasła się zgadza funkcją password_verify(par1,par2) która przyjmuje 2 parametry: jeden z nich to hasło wpisane przez użytkownika, drugi to hasło z bazy danych. Istotne jest, aby nie sprawdzać haseł po prostu przez porównanie stringów: $password ==$user[’password’] – ta metoda jest nieprawidłowa, dlatego że bcrypt za każdym razem generuje inną sól do hasła.

Podsumowanie

Mam nadzieję, że ten wpis pomoże Wam w bezpiecznym przechowywaniu haseł użytkowników Waszych serwisów napisanych w PHP.

Kod przykładu dostępny jest na GitHub: https://github.com/kamilwyremski/password-hash

Ta strona używa ciasteczek (cookies), dzięki którym nasz serwis może działać lepiej. Więcej informacji

The cookie settings on this website are set to "allow cookies" to give you the best browsing experience possible. If you continue to use this website without changing your cookie settings or you click "Accept" below then you are consenting to this.

Close