Я работаю над логином, чтобы приватизировать веб-приложение, которое я делаю. Мои знания не то чтобы базовые, но и не самые лучшие и мне нужна помощь.
Нет способа регистрации. Регистрация будет производиться мной лично из базы данных. Что мне нужно, так это совет или способы сделать этот вход в систему и более поздние элементы управления сеансом более безопасными, более надежными, лучшую структуру документов .php в корневом каталоге...
На данный момент единственное, что я применяю, это PDO и предложения prepare
, чтобы избежать SQL-инъекций.
Структура документов такова:
login.php (Вот форма и где выполняются функции). Это код PHP перед заголовком для управления входом в систему:
if (isset($_POST['entrar'])) { require "functions.php"; $error = []; $user = ValidUser($error); if(empty($error)){ logearUsuario($user); header("Location: application.php"); } }
Имена переменных, которые должны быть собраны формой: user (пользователь), pass (пароль) и enter (отправить).
conn.php (В этом документе то, что необходимо для подключения к базе данных, в данном случае MySQL, но в будущем это изменится на PostgreSQL. Это функция, включающая в себя все необходимое для подключения)
function connection(){ try{ $dsn = 'mysql:host=localhost; dbname=controlusuarios'; $userBBDD = 'root'; $passBBDD = ''; $conn = new PDO($dsn, $userBBDD, $passBBDD); return $conn; }catch(Exception $e){ die('Error: ' . $e->GetMessage()); } }
functions.php (содержит несколько функций. Одна для проверки пользователя, а другая для входа в систему. Условие входа находится в документе login.php)
function validUser(&$error){ if((!isset($_POST['user'])) || (!isset($_POST['pass']))){ $error[0] = "Usuario y/o contraseña incorrectos."; return null; } $user = $_POST['user']; $pass = $_POST['pass']; if(($user == '') || ($pass == '')){ $error[0] = "Usuario y/o contraseña incorrectos."; return null; }else{ $con = connection(); $sql = "SELECT name FROM usuarios WHERE name = :user AND password = :pass"; $query = $con->prepare($sql); $query-> bindParam(':user', $user); $query-> bindParam(':pass', $pass); $query-> execute(); $contador = $query -> rowCount(); if($contador != 1){ $error[0] = "Usuario y/o contraseña incorrectos."; return null; } $con = null; return $user; } } function logearUsuario($user){ session_start(); $_SESSION['name'] = $user; }
Я не эксперт по безопасности, но на основе кода, которым вы поделились, я вижу некоторые проблемы/моменты, которые можно улучшить:
Идентификатор сеанса не меняется после изменения состояния в логине
На самом деле мало что показывает, как вы относитесь к сеансу, но на первый взгляд кажется, что вы открываете его, когда пользователь входит в систему (я не знаю, открывается он раньше или нет) и сохраняете имя пользователя, не сделать что-нибудь еще.
Повторное создание идентификатора сеанса каждый раз при изменении уровня привилегий пользователя (например, при входе в систему или выходе из нее) является рекомендуемой практикой OWASP , которая служит для предотвращения множественных атак, таких как перехват сеанса , сеанс или переопределение сеанса (известный по аббревиатуре на английском языке CSRF или XSRF).
В PHP вы можете повторно сгенерировать идентификатор сеанса, используя
session_regenerate_id()
, который применительно к вашему коду будет выглядеть примерно так:Сессия, кажется, не истекает
Опять же, вы на самом деле не видите многого из того, как вы относитесь к сеансу, но когда вы входите в систему, кажется, что вы сохраняете только имя пользователя и ничего больше.
Было бы идеально, если бы вы сохранили дату входа в систему, а затем каждый раз, когда выполняется операция или загружается страница, проверяйте дату и убедитесь, что она находится в соответствующем диапазоне для выполнения соответствующего действия (обновления или выхода из системы).
Например, если прошло более 10 минут бездействия, завершите сессию (перегенерировав id с тем, что было указано в предыдущем пункте) и отправьте пользователя на вход. И если прошло не более этих 10 минут, то снова обновить счетчик, чтобы дать ему еще 10 минут.
Как указано на странице OWASP по истечении сеанса :
Необходимо предусмотреть закрытие сеанса по истечении срока его действия как на стороне клиента, так и на стороне сервера (хотя здесь мы видим только часть PHP).
Вы не наказываете ошибки
Когда пользователь неправильно вводит пароль, вы просто показываете ему сообщение об ошибке, и все. Что произойдет, если пользователь не допустил ошибку, а злоумышленник пытается увидеть, есть ли удача, и угадывает ваш пароль?
Код не наказывается за неудачные попытки входа в систему, что делает его уязвимым для атак методом грубой силы . Злоумышленнику (которым может быть программа/бот) достаточно отправить много попыток ввода пароля, пока одна из них не сработает и не получит доступ к привилегированной учетной записи.
Решением для атак методом грубой силы будет добавление какой-либо блокировки после нескольких попыток. Например: после X сбоев заблокировать аккаунт на M минут. И если по прошествии этого времени снова произойдет сбой, увеличьте значение M в соответствии с определенной моделью серьезности.
Другим возможным решением было бы требовать CAPTCHA после X неудачных попыток и каждый раз, когда это не удается снова.
Информация о подключении к базе данных содержится в самом коде.
Как указано на этой странице OWASP, а также на этой другой (мой перевод):
Кроме того, если по какой-либо причине сервер перестанет корректно обрабатывать PHP-код и отобразит его в виде обычного текста (редко, но такое может случиться), данные подключения будут видны всем.
В идеале информация о подключении к базе данных должна находиться в отдельном файле вне корня веб-сайта. Таким образом, он не будет доступен снаружи сервера и не будет доступен для возможных шпионов (внешних или внутренних).
Ключи не хэшируются
Это проблема, если кому-то удастся проникнуть в вашу систему и получить доступ к базе данных: системы входа в систему должны быть разработаны с учетом возможности того, что в какой-то момент они могут выйти из строя, и данные в конечном итоге будут скомпрометированы ( как указано в OWASP ).
В этом смысле очень важно защитить учетную запись пользователя, а хранить пароли в базе данных в виде простого текста — плохая идея. Вы должны хранить не сам пароль, а представление пароля.
Для этого вы должны хэшировать пароль, сохранив его в базе данных, а затем проверить пароль, переданный в форме, с помощью хэша для проверки (в PHP вы можете использовать
password_verify
).Преимущество использования хэшей по сравнению с алгоритмами шифрования заключается в том, что при шифровании пароля могут быть способы его расшифровки; С другой стороны, с хешированием нет пути назад. Если бы база данных была скомпрометирована, по крайней мере, пароли пользователей не были бы потеряны (я знаю, что это может быть не очень утешительно для вас, но для пользователя это может быть связано с тем, что многие используют одни и те же пароли с одного сайта к другому).
Кроме того, можно было бы сделать некоторые комментарии о том, как обрабатываются подключения к базе данных (подключение и отключение при каждом запросе), хотя это было бы больше производительность, чем безопасность.
И некоторые другие вещи, которые вы не видите в общем коде, также должны быть оценены. Например: вы используете HTTPS для входа? Как данные хранятся в базе данных?
Вы можете найти больше рекомендаций по безопасности на странице OWASP (и, в частности, для сессий ).