我正在登录,以私有化我正在做的 Web 应用程序。我的知识不是那么基本,但不是最好的,我需要帮助。
没有注册方法。注册将由我亲自从数据库中完成。我需要的是建议或方法来使此登录和以后的会话控制,更安全,更可靠,根目录中 .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(user)、pass(password)和enter(submit)
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; }
我不是安全专家,但根据您共享的代码,我发现了一些可以改进的问题/要点:
登录状态更改后会话ID未更改
它确实并没有显示出您如何对待会话,但乍一看似乎您在用户登录时打开它(我不知道它之前是否打开过)并保存用户名,而似乎没有做任何其他事情。
每次用户的权限级别发生变化时(例如,登录或注销时)重新生成会话 ID 是OWASP 推荐的做法,用于防止多种攻击,例如会话劫持、或会话覆盖(以其首字母缩写而闻名)英文 CSRF 或 XSRF)。
在 PHP 中,您可以使用 重新生成会话 id
session_regenerate_id()
,应用于您的代码将如下所示:会话似乎没有过期
同样,您并没有真正了解如何处理会话,但是当您登录时,它似乎只保存了用户名,没有别的。
您最好保存登录日期,然后在每次执行操作或加载页面时检查日期并验证它是否在适当的范围内以执行适当的操作(刷新或注销)。
例如,如果超过 10 分钟没有操作,则终止会话(使用前一点中所述的内容重新生成 id)并让用户登录。如果不超过这 10 分钟,则再次更新计数器以再给它 10 分钟。
如会话到期的 OWASP 页面所述:
当会话在客户端和服务器端都过期时,必须做好准备关闭会话(尽管我们在这里只看到 PHP 部分)。
你不会惩罚错误
当用户输入错误的密码时,您只需向他们显示错误消息即可。如果用户没有犯错,而是攻击者试图查看是否有运气并猜测您的密码,会发生什么?
通过不惩罚登录失败,代码很容易受到暴力攻击。攻击者(可能是程序/机器人)只需发送多次密码尝试,直到其中一个成功并获得对特权帐户的访问权限。
暴力登录攻击的解决方案是在多次尝试后添加某种锁定。例如:X 次失败后,锁定账号 M 分钟。如果在该时间过去后它再次失败,请按照某种严重性模式增加 M 的值。
另一种可能的解决方案是在 X 次尝试失败并且每次再次失败后都需要验证码。
数据库连接信息在代码本身
正如这个 OWASP 页面和另一个页面(我的翻译)所示:
此外,如果由于任何原因服务器停止正确处理 PHP 代码并将其呈现为纯文本(很少见,但可能发生),任何人都可以看到连接数据。
理想情况下,数据库连接信息将位于网站根目录之外的单独文件中。这样就无法从服务器外部访问它,并且可能的窥探者(外部或内部)也无法使用它。
密钥没有散列
如果有人设法闯入您的系统并访问数据库,这将是一个问题:登录系统的设计必须考虑到在某些时候它们可能会失败并且数据最终会受到损害的可能性(如 OWASP 中所述)。
从这个意义上说,保护用户帐户至关重要,并且在数据库中以纯文本形式保存密码是一个坏主意。您不应存储密码本身,而应存储密码的表示形式。
为此,您必须通过将密码保存在数据库中来对密码进行哈希处理,然后使用哈希验证表单中传递的密码以进行验证(在 PHP 中,您可以使用
password_verify
)。使用散列而不是加密算法的优势在于,通过加密密码,可能有办法对其进行解密;另一方面,使用散列,没有回头路。如果数据库被破坏,至少用户的密码不会丢失(我知道这对你来说可能不是什么安慰,但对用户来说,它可能考虑到许多人使用来自一个站点的相同密码到另一个)。
除此之外,可以对数据库连接的处理方式(每个请求的连接和断开连接)进行一些评论,尽管这比安全性更重要。
还需要评估您在共享代码中看不到的其他一些内容。例如:您使用 HTTPS 登录吗?数据如何存储在数据库中?
您可以在OWASP 页面上找到更多安全建议(尤其是会话)。