Як створити безпечний логін скрипт в PHP і MySQL

Фото - Як створити безпечний логін скрипт в PHP і MySQL

Спостерігаючи все більше і більше новин про атаки хакерів, розробники шукають кращі способи для захисту їх вебсайтів. Якщо у вашого вебсайту є логін система, існує ризик злому і втрати даних користувача. З цієї статті ви дізнаєтеся про один із способів створення захищеного логіна, використовуючи PHP. Ви можете написати відмінний код, але, в теж час, упустити деякі важливі моменти безпеки. Основні поняття про комп`ютерну безпеку постійно змінюються. Можливо, ми забули згадати про якісь деталі, якщо це так, то, будь ласка, дайте нам про це знати, і ми додамо це до існуючої інформаціі.Следуя цьому керівництву, ви зможете захистити персональні дані ваших користувачів від багатьох типів атак. Різновиди атак, від яких ми вам допоможемо захиститися:

  • SQL Injections
  • Session Hijacking
  • Network Eavesdropping
  • Cross Site Scripting
  • Brute Force Attacks

Підхід полягає в тому, щоб використовувати фільтри інформації, шифрування та інші методи, які ускладнюють життя хакерів.

Ми постійно працюємо над поліпшенням скрипта. Сама остання версія скрипта доступна на github. Ви можете помітити невелику різницю між кодом, який ви завантажили, і кодом в цій статті. Ми не намагалися прикрасити висновок сторінок HTML

Ви можете помітити, що ми не закриваємо PHP таги у файлах з PHP кодом.

Ми рекомендуємо створювати всі необхідні файли в різних папках, в кореневій папці програми. Найпростіший спосіб правильно створити архітектуру розташування файлів - це завантажити одну з останніх версій коду, по посиланнях представленим вище.

Будь ласка, використовуйте цей додаток для ваших власних проектів. Ми не рекомендуємо використовувати його, як доброго прикладу для виробничих збірок.

Що вам знадобиться

Так як для доступу в MySQL ми будемо використовувати PHP класи сету mysqli_ *, вам знадобляться дані версії PHP і MySQL.

  • PHP версія 5.3 або вище
  • MySQL версія 4.1.3 або вище

Вам, природно, знадобиться веб-сервер, налаштований під використання PHP. Швидше за все, веб сервер вашого хостингу, якщо тільки ви не самі хостів ваш вебсайт.

Щоб перевірити версію PHP і MySQL на вашому сервері, використовуйте функцію phpinfo () ;




Частина 1 з 8: Налаштуйте ваш сервер

  1. 1

    [b]Встановіть веб сервер, PHP і MySQL.

    Більшість хостингів має встановлені PHP і MySQL. Вам залишиться перевірити версію таких, щоб ця стаття виявилася для вас корисною. Якщо встановлені версії продуктів нижче, ніж PHP5.3 і MySQL5 - у ваших інтересах буде дізнатися про безпеку цього хостингу. Підтримка оновлень ваших продуктів - це частина заходів щодо забезпечення безпеки.

    Якщо ви самі хостів веб сервер - в домашніх умовах, то встановіть останні продукти, потрібні для роботи нашого застосування. В цілому, якщо ви не використовуєте вашу збірку для виробничих цілей і працюєте під Windows або OS / X, встановіть XAMPP. Завантажте потрібну версію для вашої операційної системи тут:

    https://apachefriends.org/en/xampp.html

    Але врахуйте, не використовуйте XAMPP для виробничої конфігурації.

    Під Linux використовуйте пакет менеджер, щоб завантажити потрібні пакети. Деякі збірки, наприклад, Ubuntu, збирає всі необхідні додаток в один пакет. Використовуйте вікно терміналу для даних команд:

    sudo apt-get install lamp-server ^ phpmyadmin



    Після установки потрібних елементів встановіть безпечний пароль рута в MySQL.

Частина 2 з 8: Налаштуйте базу даних MySQL

  1. 1

    [b]Створіть базу даних MySQL.

    Зайдіть в базу даних "secure_login".

    Подивіться, як Create-a-Database-in-Phpmyadmin.

    Ви можете використовувати даний код або зробити те ж саме в phpMyAdmin / вашому улюбленому клієнті GUI MySQL:

    CREATE DATABASE `Secure_login`-

    Зауважте: деякі хостинги не дозволяють створювати бази даних через phpMyAdmin, Дізнайтеся, як це зробити в cPanel.

  2. 2

    [b]Створіть користувача з привілеями SELECT, UPDATE і INSERT.

    Створення користувача з такими привілеями є заходом безпеки, якщо в певний момент в нашому скрипті знайдеться лазівка, і хакер зламає даного користувача - він не зможе видалити або прибрати що-небудь з бази даних. Використання даних привілеїв достатньо для всіх функцій нашого застосування. Якщо ви хочете ускладнити, створіть користувача під кожну функцію.

    Природно, щоб створити даного користувача, ви повинні знаходиться в MySQL з певними привілеями. Швидше за все, під root.

    Інформація про користувача, якого ми створюємо:
    • [b]User:"Sec_user"
    • [b]Password:"EKcGZr59zAa2BEWU"

    Зауважте: бажано використовувати пароль, що відрізняється від представленого тут. При цьому, не забудьте змінити код, представлений нижче, і код з`єднання бази даних PHP, який ми створюємо.

    Пам`ятайте, вам не потрібно запам`ятовувати цей пароль, зробіть його якомога складніше. Ви можете використовувати Генератор паролів

    Тут представлений SQL код для створення користувача бази даних і призначення певних привілеїв. Або ж ви можете використовувати клієнт GUI для бази даних, наприклад, phpmyadmin, якщо бажаєте:

    CREATE USER `Sec_user`@`Localhost` IDENTIFIED BY `EKcGZr59zAa2BEWU`-GRANT SELECT, INSERT, UPDATE ON `Secure_login`.* TO `Sec_user`@`Localhost`-



    Якщо ви плануєте видаляти записи в ваших таблицях, ви можете додати DELETE до списку привілеїв, або ж створіть іншого користувача для даної функції. Вам не потрібно додавати привілей DELETE для цього скрипта.

  3. 3

    [b]Створіть MySQL таблицю з назвою "members".

    Цей код створює таблицю з 5 полями (id, username, email, password, salt). Ми використовуємо CHAR datatype для полів з відомою довгою і для полів "password" і "salt", довжина завжди буде 128 символів. Використовуючи CHAR, ми спрощуємо процес:

    CREATE TABLE `Secure_login`.`Members` (`Id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,`Username` VARCHAR(30) NOT NULL,`Email` VARCHAR(50) NOT NULL,`Password` CHAR(128) NOT NULL,`Salt` CHAR(128) NOT NULL) ENGINE = InnoDB-


    Ви можете зробити це в будь-якому клієнті.

  4. 4

    [b]Створіть таблицю для спроб логінів.

    Ми будемо використовувати цю таблицю, щоб записувати спроби логінів. Це один з способів ускладнити брут форс атаки:

    CREATE TABLE `Secure_login`.`Login_attempts` (`User_id` INT(11) NOT NULL,`Time` VARCHAR(30) NOT NULL) ENGINE=InnoDB


  5. 5

    [b]Створіть тестову колону в "members".

    Це важливо для тестування скрипта логіна, знизу представлений скрипт для створення користувача з певними деталями:
    Код, який вам потрібен для логіна під цим користувачем:

    INSERT INTO `Secure_login`.`Members` VALUES(1, `Test_user`, `[email protected]`,`00807432eae173f652f2064bdca1b61b290b52d40e429a7d295d76a71084aa96c0233b82f1feac45529e0726559645acaed6f3ae58a286b9f075916ebf66cacc`,`f9aab579fc1b41ed0c44fe4ecdbfcdb4cb99b9023abb241a6db833288f4eea3c02f76e0d35204a8695077dcf81932aa59006423976224be0390395bae152d4ef`)-

Частина 3 з 8: Додати сторінку з`єднання в базі даних

  1. 1

    [b]Додати сторінку глобальних налаштувань

    Створіть папку з назвою "includes" в кореневій папці програми та потім створіть новий PHP файл у цій же папці. З назвою psl-config.php. У виробничій збірці, бажано, помістити цей файл (та інші) - поза кореневої папки програми. Якщо ви це зробите, і ми вам рекомендуємо це зробити, поміняйте налаштування програми, щоб воно змогло знайти ваші файли.

    Розташування ваших файлів поза кореневої папки означає, що ваші файли не зможуть бути знайдені, використовуючи URL.

    Файл містить глобальні налаштування параметрів. Наприклад, хто може зареєструватися, або використання безпечного (HTTPS) з`єднань та інші важливі деталі ...

    <?php/ *** Інформація про користувачів бази даних * /define("HOST", "Localhost"); // The host you want to connect to.define("USER", "Sec_user");// The database username. define("PASSWORD", "4Fa98xkHVd2XmnfK");// The database password. define("DATABASE", "Secure_login");// The database name.define("CAN_REGISTER", "Any");define("DEFAULT_ROLE", "Member");define("SECURE", FALSE);// FOR DEVELOPMENT ONLY !!!!

  2. 2

    [b]Додати сторінку підключення бази даних

    Це PHP код, який ми будемо використовувати для підключення до нашої бази даних mySQLcode. Створіть новий файл PHP з назвою db_connect.php в теці програми includes і додайте код, представлений нижче. Після цього ви можете помістити цей файл на будь-яку сторінку, при підключенні до бази даних.

    <?phpinclude_once `Psl-config.php`; // As functions.php is not included$ Mysqli = new mysqli(HOST, USER, PASSWORD, DATABASE);

Частина 4 з 8: Створіть PHP функції

Ці функції будуть відстежувати всі деталі логін скрипта. Додайте всі функції на сторінку functions.php, в папку програми includes.



  1. 1

    [b]Відкрийте безпечну PHP сесію.
    PHP сесії відомі через свою незахищеність, дуже важливо почати, не просто використовуючи "session_start () -" зверху кожної сторінки, де ви плануєте використання PHP сесії. Ми створимо функцію з назвою "sec_session_start ()", таким чином, сесія відкриється в безпечному режимі. Якщо ви стурбовані безпекою ваших cookies, подивіться: Create-a-Secure-Session-Managment-System-in-Php-and-Mysql.

    Ця функція робить ваш логін скрипт набагато безпечніше. Це зупиняє доступ хакерів до session id cookie через javascript (наприклад, XSS атака). А також функцію "session_regenerate_id ()", яка генерує session id на кожному оновленні сторінки, допомагає від крадіжки сесій. Примітка: якщо ви використовуєте HTTPS в ваших логін додатках, поставте змінну "$ secure" на true. У виробничій збірці - використовуйте HTTPS.

    Створіть новий файл з назвою functions.php в теці програми includes і додайте код:

    <?phpinclude_once `Psl-config.php`;function sec_session_start() {$ Session_name = `Sec_session_id`; // Set a custom session name$ Secure = SECURE;// This stops javascript being able to access the session id.$ Httponly = true;// Forces sessions to only use cookies.if (ini_set(`Session.use_only_cookies`, 1) === FALSE) {header("Location: ../error.php?err=Could not initiate a safe session (ini_set)");exit();}// Gets current cookies params.$ CookieParams = session_get_cookie_params();session_set_cookie_params($ CookieParams["Lifetime"],$ CookieParams["Path"],$ CookieParams["Domain"],$ Secure,$ Httponly);// Sets the session name to the one set above.session_name($ Session_name);session_start();// Start the PHP session session_regenerate_id();// Regenerated the session, delete the old one. }

  2. 2

    [b]Створіть функцію логіна.
    Ця функція звіряє email і password з базою даних. Якщо відповідь буде true, то дані збігаються. Додайте цю функцію в файл functions.php:

    function login($ Email, $ Password, $ Mysqli) {// Using prepared statements means that SQL injection is not possible. if ($ Stmt = $ Mysqli->prepare("SELECT id, username, password, saltFROM membersWHERE email =? LIMIT 1")) {$ Stmt->bind_param(`S`, $ Email);// Bind "$ email" to parameter.$ Stmt->execute();// Execute the prepared query.$ Stmt->store_result();// Get variables from result.$ Stmt->bind_result($ User_id, $ Username, $ Db_password, $ Salt);$ Stmt->fetch();// Hash the password with the unique salt.$ Password = hash(`Sha512`, $ Password . $ Salt);if ($ Stmt->num_rows == 1) {// If the user exists we check if the account is locked// From too many login attempts if (checkbrute($ User_id, $ Mysqli) == true) {// Account is locked // Send an email to user saying their account is lockedreturn false;} else {// Check if the password in the database matches// The password the user submitted.if ($ Db_password == $ Password) {// Password is correct!// Get the user-agent string of the user.$ User_browser = $ _SERVER[`HTTP_USER_AGENT`];// XSS protection as we might print this value$ User_id = preg_replace("/ [^ 0-9] + /", "", $ User_id);$ _SESSION[`User_id`] = $ User_id;// XSS protection as we might print this value$ Username = preg_replace("/ [^ A-zA-Z0-9 _ -] + /","",$ Username);$ _SESSION[`Username`] = $ Username;$ _SESSION[`Login_string`] = hash(`Sha512`,$ Password . $ User_browser);// Login successful.return true;} else {// Password is not correct// We record this attempt in the database$ Now = time();$ Mysqli->query("INSERT INTO login_attempts (user_id, time) VALUES (`$ User_id`,`$ Now`) ");return false;}}} else {// No user exists.return false;}}}


  3. 3

    [b]Функція брут форсу.
    Атаки брут форс - це коли хакер пробує тисячі різних паролів під один аккаунт, або ж випадково згенеровані, або паролі зі словника. У нашому скрипті, якщо користувач використовує п`ять невдалих спроб залогінитися, то аккаунт блокується.

    Атаки брут форс дуже складно запобігти. Кілька варіантів захисту від таких атак - це використання CAPTCHA тесту, блокування аккаунта користувача і додавання затримки між логінами так, щоб користувач не зміг залогінитися протягом 30 секунд.

    Ми дуже рекомендуємо використання CAPTCHA. На даний момент ми не застосували цей метод в прикладах нашого коду, але сподіваємося зробити це в найближчому майбутньому, використовуючи SecureImage. Ви можете використовувати що-небудь більш серйозне, як reCAPTCHA від Google.

    Яку б ви не використали схему, ми рекомендуємо використання картинки CAPTCHA після двох неправильних спроб логіна, щоб не заподіювати незручності користувачам.

    Коли справа доходить до брут форс атак, більшість розробників блокує IP-адреса атакуючого. Але, існує безліч утиліт, щоб обійти таку долю, наприклад, хакери можуть використовувати проксі сервера або ж міняти IP-адресу автоматично при кожній атаці. У нашому коді ми запишемо невдалі спроби логіна і заблокуємо аккаунт користувача. Це спровокує відправку email на адресу користувача, з проханням скидання пароля, на даний момент ми не застосували це в нашому коді. Додайте цей код в functions.php:

    function checkbrute($ User_id, $ Mysqli) {// Get timestamp of current time $ Now = time();// All login attempts are counted from the past 2 hours. $ Valid_attempts = $ Now ; (2 * 60 * 60);if ($ Stmt = $ Mysqli->prepare("SELECT timeFROM login_attempts 
    WHERE user_id =? AND time> `$ Valid_attempts`")) {$ Stmt->bind_param(`I`, $ User_id);// Execute the prepared query. $ Stmt->execute();$ Stmt->store_result();// If there have been more than 5 failed logins if ($ Stmt->num_rows  5) {return true;} else {return false;}}}

  4. 4

    [b]Перевірте інформацію про браузер.
    Ми робимо це, перевіряючи "user_id" і "login_string" змінної SESSION. Змінна "login_string" SESSION має інформацію браузера користувача і пароль. Ми використовуємо інформацію браузера, так як, малоймовірно, що користувач змінить браузер на середині сесії. Допомагає від крадіжки сесій. Додайте цю функцію в файл functions.php в теці програми includes:

    function login_check($ Mysqli) {// Check if all session variables are set if (isset($ _SESSION[`User_id`],$ _SESSION[`Username`],$ _SESSION[`Login_string`])) {$ User_id = $ _SESSION[`User_id`];$ Login_string = $ _SESSION[`Login_string`];$ Username = $ _SESSION[`Username`];// Get the user-agent string of the user.$ User_browser = $ _SERVER[`HTTP_USER_AGENT`];if ($ Stmt = $ Mysqli->prepare("SELECT passwordFROM membersWHERE id =? LIMIT 1")) {// Bind "$ user_id" to parameter. $ Stmt->bind_param(`I`, $ User_id);$ Stmt->execute(); // Execute the prepared query.$ Stmt->store_result();if ($ Stmt->num_rows == 1) {// If the user exists get variables from result.$ Stmt->bind_result($ Password);$ Stmt->fetch();$ Login_check = hash(`Sha512`, $ Password . $ User_browser);if ($ Login_check == $ Login_string) {// Logged In !!!! return true;} else {// Not logged in return false;}} else {// Not logged in return false;}} else {// Not logged in return false;}} else {// Not logged in return false;}}

  5. 5

    [b]Очистіть URL від PHP_SELF
    Наступна функція очищає вихід від серверної змінної PHP_SELF. Це модифікація функції, з ім`ям, яке використовується WordPress Content Management System:

    function esc_url($ Url) {if (`` == $ Url) {return $ Url;}$ Url = preg_replace(`| [^ A-z0-9- ~ + _.? # =! -, /:% @ $ | * ` () X80 - xff] | i `, ``, $ Url);$ Strip = array(`% 0d`, `% 0a`, `% 0D`, `% 0A`);$ Url = (string) $ Url;$ Count = 1;while ($ Count) {$ Url = str_replace($ Strip, ``, $ Url, $ Count);}$ Url = str_replace(`- //`, `: //`, $ Url);$ Url = htmlentities($ Url);$ Url = str_replace(`Amp-`, `# 038-`, $ Url);$ Url = str_replace("`", `` `, $ Url);if ($ Url[0] !== `/`) {// We`re only interested in relative links from $ _SERVER [`PHP_SELF`]return ``;} else {return $ Url;}}



    Проблема застосування не фільтрованої змінної сервера - це можливе використання її в крос сайт скрипт атаках. Більшість джерел свідчать про використанні htmlentities (), однак, це варіант не повністю прибирає ризик.

    Інші радять залишати атрибут дії порожнім або поставити нульову рядок. Така дія залишає форму відкритої для атаки по крадіжці iframe.

Частина 5 з 8: Створіть сторінки обробки

  1. 1

    [b]Додати сторінку обробки логінів (process_login.php)

    Створіть файл для обробки логінів з назвою process_login.php в теці програми includes.

    Ми будемо використовувати mysqli_ * функцій PHP, так як, це є одним з найбільш новий MySQL розширень.

    <?phpinclude_once `Db_connect.php`;include_once `Functions.php`;sec_session_start(); // Our custom secure way of starting a PHP session.if (isset($ _POST[`Email`], $ _POST[`P`])) {$ Email = $ _POST[`Email`];$ Password = $ _POST[`P`]; // The hashed password.if (login($ Email, $ Password, $ Mysqli) == true) {// Login success header(`Location: ../protected_page.php`);} else {// Login failed header(`Location: ../index.php?error=1`);}} else {// The correct POST variables were not sent to this page. echo `Invalid Request`;}

  2. 2

    [b]Створіть скрипт виходу.

    Ваш скрипт виходу повинен починати сесію, видаляти її і після цього перенаправляти в інше місце. Примітка: хорошою ідеєю буде - додати CSRF захист, якщо хто-небудь посилає посилання, заховану в сторінці. Щоб дізнатися побільше інформації про CSRF, ви можете відвідати Кошмари програмування.

    Код для користувацького виходу, які ви можете додати в файл, називається logout.php в теці програми includes, такий:

    <?phpinclude_once `Includes / functions.php`;sec_session_start();// Unset all session values $ _SESSION = array();// Get session parameters $ Params = session_get_cookie_params();// Delete the actual cookie. setcookie(session_name(),``, time() ; 42000,$ Params["Path"],$ Params["Domain"],$ Params["Secure"],$ Params["Httponly"]);// Destroy session session_destroy();header(`Location: ../ index.php`);

  3. 3

    [b]Сторінка реєстрації.

    Код для реєстрації включений в двох нових файлах, під назвою register.php в кореневій папці програми та register.inc.php в папці includes. Що він робить:
    • Приймає і перевіряє ім`я користувача при реєстрації
    • Приймає і перевіряє користувальницький email
    • Приймає і перевіряє пароль при реєстрації
    • Забирає пароль і передає його назад на сторінку реєстрації

    Більшість перевірок відбувається в javascript, на стороні клієнта. Це тому, що користувачеві не потрібно обходити ці перевірки. Який користувач захоче створити менш захищений аккаунт? Ми обговоримо javascript в наступній секції

    Зараз створіть файл register.php і додайте цей код:

    <?phpinclude_once `Includes / register.inc.php`;include_once `Includes / functions.php`;?>Secure Login: Registration Form

    Register with us

    <?phpif (!empty($ Error_msg)) {echo $ Error_msg;}?>
    • Usernames may contain only digits, upper and lower case letters and underscores
    • Emails must have a valid email format
    • Passwords must be at least 6 characters long
    • Passwords must contain
      • At least one upper case letter (A..Z)
      • At least one lower case letter (a..z)
      • At least one number (0..9)
    • Your password and confirmation must match exactly
    <?php echo esc_url($ _SERVER[`PHP_SELF`]); ?>"Method =" post "name =" registration_form "> Username:
    Email:
    Password:
    Confirm password:

    Return to the login page.



    Файл register.inc.php в папці includes, повинен містити:

    <?phpinclude_once `Db_connect.php`;include_once `Psl-config.php`;$ Error_msg = "";if (isset($ _POST[`Username`], $ _POST[`Email`], $ _POST[`P`])) {// Sanitize and validate the data passed in$ Username = filter_input(INPUT_POST, `Username`, FILTER_SANITIZE_STRING);$ Email = filter_input(INPUT_POST, `Email`, FILTER_SANITIZE_EMAIL);$ Email = filter_var($ Email, FILTER_VALIDATE_EMAIL);if (!filter_var($ Email, FILTER_VALIDATE_EMAIL)) {// Not a valid email$ Error_msg .= `

    The email address you entered is not valid

    `
    ;}$ Password = filter_input(INPUT_POST, `P`, FILTER_SANITIZE_STRING);if (strlen($ Password) != 128) {// The hashed pwd should be 128 characters long.// If it`s not, something really odd has happened$ Error_msg .= `

    Invalid password configuration.

    `
    ;}// Username validity and password validity have been checked client side.// This should should be adequate as nobody gains any advantage from// Breaking these rules.//$ Prep_stmt = "SELECT id FROM members WHERE email =? LIMIT 1";$ Stmt = $ Mysqli->prepare($ Prep_stmt);if ($ Stmt) {$ Stmt->bind_param(`S`, $ Email);$ Stmt->execute();$ Stmt->store_result();if ($ Stmt->num_rows == 1) {// A user with this email address already exists$ Error_msg .= `