Обеспеченный регистрации сценарий


Я создал код для моего регистрационную форму на моей социальной сети. Я хочу знать, если это достаточно обеспеченные, чтобы выпустить на веб-сайте в социальной сети. Кроме того, весь код работает. Я просто хочу убедиться, что это все безопасно.

registration.php:

ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

//check if form is submitted
if ( $_SERVER['REQUEST_METHOD'] != 'POST' || ! isset($_POST['Register'])) 
{
// looks like a hack, send to index.php
header('Location: index.php');
die();
}

include("connect.php");

            if(isset($_POST['Register'])) {

               if (empty($_POST["username"])) {
                echo"Fill in username to sign up";
                } else {

                if (empty($_POST["pw"])) {
                 echo"Fill in password to sign up";
                } else {

                if (empty($_POST["pw2"])) {
                echo"Confirm password to sign up";
                 } else {

                if (empty($_POST["email"])) {
                     echo"Fill in email to sign up";
                 } else {

                if ($_POST['pw'] == $_POST['pw2']) {
                 $username = mysqli_real_escape_string($conn, $_POST["username"]);
                 $pw= mysqli_real_escape_string($conn, $_POST["pw"]);
                 $pw2= mysqli_real_escape_string($conn, $_POST["pw2"]);
                 $email = mysqli_real_escape_string($conn, $_POST["email"]);

                 $result = mysqli_query($conn ,"SELECT * FROM users WHERE username='" . $username . "'");

                    if(mysqli_num_rows($result) > 0)
                    {
                    echo "Username exists . <a href= index.php>Try again</a><br /> ";
                    } else {

                       $result2 = mysqli_query($conn ,"SELECT * FROM users WHERE email='" . $email. "'");

                       if(mysqli_num_rows($result2) > 0)
                       {
                       echo "Email exist.  <a href= index.php>Try again</a><br /> ";
                       } else {

                       $pw = password_hash($pw, PASSWORD_BCRYPT, array('cost' => 14));          

                       $stmt = $conn->prepare("INSERT INTO users (username, pw, email) VALUES(?, ?, ?)");
                       $stmt->bind_param("sss", $username, $pw, $email);

                      $username = $username;
                      $pw = $pw;
                      $email = $email;
                      $stmt->execute();

                      echo"Registration successful <a href= index.php>Login here</a><br />";


                    } } } else{
            echo "The passwords do not match.";  // and send them back to registration page
            }
}
}}}}

login.php:

error_reporting(E_ALL);
ini_set('display_errors', 1);

include("connect.php");

$_SESSION['username'] = $_POST['username'];

//check if form is submitted
if ( $_SERVER['REQUEST_METHOD'] != 'POST' || ! isset($_POST['signin'])) 
{
// looks like a hack, send to index.php
header('Location: index.php');
die();
}

if (empty($_POST["username"])) {
echo 'Fill in username to sign in. <a href= index.php>Try again</a><br />';
die();
}

if (empty($_POST["pw"])) {
echo 'Fill in password to sign in. <a href= index.php>Try again</a><br />';
die();
}

$sql = "SELECT pw FROM users WHERE username = ?";
$stmt = mysqli_prepare($conn, $sql);
if ( !$stmt ) {
echo mysqli_error($conn);
die();
}

$stmt->bind_param('s', $_POST['username']);
if ( !$stmt->execute() ) {
echo mysqli_error($conn);
die();
}
// we found a row with that username, 
// now we need to check the password is correct

// get the password from the row
$stmt->bind_result($hashed_pwd);
$stmt->fetch();

if ( password_verify($_POST['pw'], $hashed_pwd) ) {
// password verified
$_SESSION["username"] = $_POST['username'];
header('Location: profile.php');
} else {
echo 'Incorrect username or Password. <a href= index.php>Try again</a><br />';
}


669
11
задан 3 апреля 2018 в 03:04 Источник Поделиться
Комментарии
3 ответа

Правильность


Кроме того, весь код работает.

Нет, это не так, поэтому давайте начнем с этого.


                 $username = mysqli_real_escape_string($conn, $_POST["username"]);


...

                 $result = mysqli_query($conn ,"SELECT * FROM users WHERE username='" . $username . "'");

if(mysqli_num_rows($result) > 0)
{
echo "Username exists . <a href= index.php>Try again</a><br /> ";
} else {



...

                       $stmt = $conn->prepare("INSERT INTO users (username, pw, email) VALUES(?, ?, ?)");
$stmt->bind_param("sss", $username, $pw, $email);

$username = $username;
$pw = $pw;
$email = $email;
$stmt->execute();


Если указанное имя пользователя такова, что mysqli_real_escape_string не нет, регистрации для существующего пользователя и ввода пользователя использовать различные имена. Это важная ошибка. Совсем как важно, зависит от того, что вы не показали нам, в частности на схему базы данных.

Учитывая нынешнее сочетание методов доступа к базе данных, вы должны бросить один, который требует от вас, чтобы защитить себя вещи (опасные, потому что это легко забыть, когда вы измените код) и использовать подготовленные операторы для все.


Я тоже борюсь, чтобы увидеть, как это может работать:


//check if form is submitted
if ( $_SERVER['REQUEST_METHOD'] != 'POST' || ! isset($_POST['Register']))
{
// looks like a hack, send to index.php
header('Location: index.php');
die();
}


...

                    } } } else{
echo "The passwords do not match."; // and send them back to registration page

Если registration.php это страница регистрации, то (а) она, кажется, обделена по содержанию; и (б) там, кажется, "Уловка-22" с предоставлением его в первый раз для того, чтобы представить данные.


Ремонтопригодность

Ремонтопригодность-это само по себе важно, но и особое отношение к безопасности, поскольку unmaintainable код имеет больше шансов обзавестись ошибок с течением времени.

Исправить вмятины. В настоящее время она представляется случайным, и это абсолютно непроницаемым.


Избегайте массивных цепей if заявления. Различных проверок здесь было бы лучше переработать так, чтобы функция, которая может воспользоваться преимуществами раннего возвращения.


Избегайте бессмысленного кода, таких как


                      $username = $username;
$pw = $pw;
$email = $email;


Безопасности

Этот код утечек слишком много информации для вредоносных абонента.


ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

это совершенно не то, что вы хотите делать на рабочей системе, если вы также используете set_error_handler для входа в какое-нибудь уединенное место и убедиться, что конечный пользователь не видит сообщения об ошибках.

Аналогично


echo mysqli_error($conn);


Форма регистрации запрашивает адрес электронной почты. Почему? Если вы не используете его, не просите его. Если вы используете его, ваша модель безопасности не требуют, чтобы это было правильно? Так не должно быть по электронной почте этапа проверки в процессе регистрации?

11
ответ дан 3 апреля 2018 в 08:04 Источник Поделиться

Ваш код является безопасным, но трудно читать, неясным и противоречивым. Бедный Scarlett O'Hara будут сохранены в базе данных как Scarlett O\'Hara который не сделает ее счастливее. Есть также незначительные проблемы безопасности.

Трудно читать

Вы должны всегда использовать отступы, отступы подчиненные строки кода, но ваш login.php не хватает отступов вообще.

В то же время, не стоит злоупотреблять также вдавливания,


  • сделав случайный отступы (как вы делали после include("connect.php");)

  • путем добавления ненужных условия (как if(isset($_POST['Register'])))

  • путем введения ненужных уровней отступа со всеми этими вложенными условиями.

Вместо того, чтобы делать много вложенных условий, запустите их в порядок, и собирать ошибки в массиве, мне.е

$errors = [];
if (empty($_POST["username"])) {
$errors[] = "Fill in username to sign up";
}
...
if ($_POST['pw'] !== $_POST['pw2']) {
$errors[] = "The passwords do not match";
}
...

и, наконец, проверить переменную $ошибок, прежде чем вставить, выполнив запрос к он пустой или повторяя все ошибки в цикле иначе.

Разнобой

Вы используете двух противоположных подходов в коде, добавление строк, напрямую в SQL и добавить их в отчетности. Вы должны придерживаться последней. Который делает все mysqli_real_escape_string вещи устаревшими и вредными. Конвертировать все ваши старому стилю запросы на заранее подготовленные заявления и избавиться от mysqli_real_escape_string звонки.

Кроме того, такой код

                  $username = $username;
$pw = $pw;
$email = $email;

нет смысла. вы можете удалить его так же.

Безопасности

echo mysqli_error($conn);

это худший способ сообщать об ошибках никогда. На живом сайте это позволит выявить некоторые важные детали, чтобы потенциальный хакер. У меня есть специальная статья, которая объясняет, что PHP отчетов об ошибках , которые вы можете прочитать. Короче, просто добавьте следующую строку перед mysqli_connect

mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);

и забыть о результатах тестирования отдельных запросов.

Рефакторинг регистрации

В конце концов ваш регистрационный код может быть такой

$errors = [];
if (empty($_POST["username"])) {
$errors[] = "Fill in username to sign up";
}
// and so on...
if ($_POST['pw'] !== $_POST['pw2']) {
$errors[] = "The passwords do not match.";
}
if (!empty($_POST["username"]) || !empty($_POST["pw"])) {
$stmt = $conn->prepare("SELECT * FROM users WHERE username=? or email=?");
$stmt->bind_param("ss", $_POST['username'], $_POST['email']);
$stmt->execute();
$row = $stmt->get_result()->fetch_assoc();

if ($row && $row['username'] == $_POST['username']) {
$errors[] = "Username exists";
}
if ($row && $row['email'] == $_POST['email']) {
$errors[] = "Email exists";
}
}
if (!$errors) {
$pw = password_hash($_POST['pw'], PASSWORD_BCRYPT, array('cost' => 14));

$stmt = $conn->prepare("INSERT INTO users (username, pw, email) VALUES(?, ?, ?)");
$stmt->bind_param("sss", $_POST['username'], $pw, $_POST['email']);
$stmt->execute();

echo "Registration successful <a href= index.php>Login here</a><br />";
} else {
foreach ($errors as $error) {
echo "$error <br /> \n";
}
echo '<a href="index.php">Try again</a><br />';
}

Юзабилити

Вы должны думать об удобстве пользователя. Это муторно опять вводить значения в случае ошибки. Рекомендуется размещать на одной странице и уже показывает введенную информацию в поля в случае ошибки.

6
ответ дан 3 апреля 2018 в 08:04 Источник Поделиться

Я полностью согласен с существующим ответов, а есть только два небольших дополнения.

Это выглядит очень странно, и возможно, небезопасным:

// login.php:
$_SESSION['username'] = $_POST['username'];

[...]

if ( password_verify($_POST['pw'], $hashed_pwd) ) {
// password verified
$_SESSION["username"] = $_POST['username'];

Вы назначаете имя пользователя в сессии, если пользователь прошел проверку и если они не заверены.

Мое предположение заключается в том, что profile.php есть проверить, например if (isset($_SESSION['username']) { $user = $_SESSION['username']; handleRequestForUser($user); } else { header('Location: login.php'); }?

Вы пробовали войти с правильным именем пользователя и неверным паролем, а затем просто посетив profile.php? Я думаю, что тогда вы могли бы войти в систему.

Даже если нет, то задание кажется сбивчивым, неуверенным, и просто попрошайничество для будущих ошибок.

Другой вопрос у меня заключается в том, что вы используете подготовленные заявления (хороший!), но не всегда. Почему падают обратно на менее безопасной mysqli_real_escape_string? Просто использовать подготовленные операторы везде.

2
ответ дан 4 апреля 2018 в 02:04 Источник Поделиться