HTTP Basic Authentication with PHP
There many ways of performing authentication over the web. You can use a token and pass it as a special header. This is commonly done with API tokens. You can also use a cookie to store a session token. This is common for webservers that have a database session in the backend.
One simple method is to use HTTP Basic Access Authentication. This involves adding a header that contains your username and password. The proper format for the header is:
Authorization: Basic XXXXXX
Where XXXXXX is your credentials in the form of username:password with base64 encoding.
PHP automatically decodes and splits the username and password into special named constants:
- PHP_AUTH_USER with the username as a plain-text string
- PHP_AUTH_PW with the password as a plain-text string
We will look at how to restrict a page using HTTP basic authentication in PHP.
Example of HTTP Basic Auth
Access denied. You did not enter a password.'; exit; // Be safe and ensure no other content is returned. > // If we get here, username was provided. Check password. if ($_SERVER['PHP_AUTH_PW'] == '$ecret') < echo 'Access granted. You know the password!
'; > else < echo 'Access denied! You do not know the password.
'; >
Hashing passwords
Really, you should never be storing passwords in plain-text. If you are storing user account information in a file or a database, the password should be hashed with a salt and each user should have a unique salt. The salt will be useful if the database is ever compromised by making it harder to crack the passwords by reducing the effectiveness of rainbow tables. It will also reduce the amount of identical hashes caused by people using the same password.
PHP’s password_hash() can take care of the hashing and the salt generation. Here is a quick example, but you can read more about Safe Password Hashing.
You use password_hash() to generate the hash that you want to store in your database or password file. This will include the salt.
When a user attempts to authenticate and they provide a password, you use crypt() and pass it the user-supplied password along with your stored hash and then compare that to the stored hash. See the example below.
Testing with curl
If you want to test, an easy way to send an HTTP request with a properly formatted header is with curl . It has a convenient —user option you can set like this:
curl --user my_username:my_password http://localhost:8000/
Conclusion
After reading this, you should understand how to restrict a page using simple HTTP basic authentication in PHP.
References
Простая аутентификация на PHP
Многие новички до сих пор попадают в тупик при написании простейшей аутентификации в PHP. На Тостере с завидной регулярностью попадаются вопросы о том, как сравнить сохраненный пароль с паролем полученным из формы логина. Здесь будет краткая статья-туториал на эту тему.
Disclaimer: статья рассчитана на совершенных новичков. Умудрённые опытом разработчики ничего нового здесь не найдут, но могут указать на возможные недочёты =).
Для написания системы аутентификации будем использовать базу данных MySQL/MariaDB, PHP, PDO, функции для работы с паролями, для построения интерфейса возьмём bootstrap.
Для начала создадим базу. Пусть она называется php-auth-demo. В новой базе создадим таблицу пользователей users :
CREATE TABLE `users` ( `id` int unsigned NOT NULL AUTO_INCREMENT, `username` varchar(255) COLLATE utf8mb4_general_ci NOT NULL, `password` varchar(255) COLLATE utf8mb4_general_ci NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `username` (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
Создадим конфиг с данными для подключения к базе.
Имейте ввиду, что в реальном проекте к конфигам не должно быть доступа из браузера, и они не должны быть включены в систему контроля версий, во избежание компрометации учетных данных.
'php-auth-demo', 'db_host' => '127.0.0.1', 'db_user' => 'mysql', 'db_pass' => 'mysql', ];
И сделаем «загрузочный» файл, который будем подключать вначале всех остальных файлов.
В реальных проектах обычно используется автозагрузка необходимых файлов. Но этот момент выходит за рамки статьи, и в демо-примере мы обойдёмся простым подключением.
В «загрузочном» файле мы будем инициализировать сессию и объявим некоторые функции-помощники.
setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); > return $pdo; >
Функция pdo() даст нам доступ к объекту PDO в любом месте нашего кода.
Далее нам нужна форма регистрации. Разместим её прямо в файле index.php .
Здесь всё просто: два поля, кнопка и форма, отправляющая запрос на файл do_register.php методом POST. Процесс регистрации пользователя опишем в файле do_register.php .
prepare("SELECT * FROM `users` WHERE `username` = :username"); $stmt->execute(['username' => $_POST['username']]); if ($stmt->rowCount() > 0) < flash('Это имя пользователя уже занято.'); header('Location: /'); // Возврат на форму регистрации die; // Остановка выполнения скрипта >// Добавим пользователя в базу $stmt = pdo()->prepare("INSERT INTO `users` (`username`, `password`) VALUES (:username, :password)"); $stmt->execute([ 'username' => $_POST['username'], 'password' => password_hash($_POST['password'], PASSWORD_DEFAULT), ]); header('Location: login.php');
В самом начале подключим наш «загрузчик».
Потом проверим, не занято ли имя пользователя. Для этого сделаем выборку из таблицы указав в условии полученное из формы имя пользователя. Обратите внимание, для запросов здесь и далее мы будем использовать подготовленные запросы, что обезопасит нас от SQL-инъекций. Для этого в тексте SQL-запроса мы указываем специальные плейсхолдеры, а при выполнении ассоциируем с ними ненадёжные данные (ненадёжными данными следует считать всё, что приходит из вне – $_GET, $_POST, $_REQUEST, $_COOKIE). После выполнения запроса мы просто проверим количество возвращённых строк. Если их больше нуля, то имя пользователя уже занято. В этом случае мы выведем сообщение и вернём пользователя на форму регистрации.
Я написал «больше нуля», но по факту, из-за того, что поле username в таблице уникальное, rowCount() может нам вернуть лишь два возможных значения: 0 и 1 .
В приведённом выше коде мы использовали функцию flash() . Данная функция предназначена для «одноразовых» сообщений. Если вызвать её со строковым параметром, то она сохранит эту строку в сессии, а если вызвать без параметров, то выведет из сессии сохранённое сообщение и затем удалит его в сессии. Добавим эту функцию в файл boot.php .
function flash(?string $message = null) < if ($message) < $_SESSION['flash'] = $message; >else < if (!empty($_SESSION['flash'])) < ?> unset($_SESSION['flash']); > >
А также вызовем её нa форме регистрации, для вывода возможных сообщений.
На данном этапе простейший функционал регистрации нового пользователя готов.
Если мы посмотрим код регистрации выше, то увидим, что в случае успешной регистрации, мы перенаправляем пользователя на страницу логина. Самое время ее написать.
Login