我一直在学习如何使身份验证系统更安全。我的代码的一个问题是它容易受到使用时间检查错误的影响。这是我的代码:
$stmt = $connection->prepare("SELECT username, password, email FROM users WHERE username=:username");
$stmt->execute(array(':username' => $username));
$rows = $stmt->fetch(PDO::FETCH_ASSOC);
if ($rows != null) {
$_SESSION["message"] = "name already exists";
} else {
$stmt = $connection->prepare("INSERT INTO users(company, username, password, first_name, last_name, address, address2, city, state, zip, phone, email) VALUES (:company, :username, :password, :first_name, :last_name, :address, :address2, :city, :state, :zip, :phone, :email)");
$result = $stmt->execute(array(':company' => $company, ':username' => $username, ':password' => $hashed_password, ':first_name' => $firstName, ':last_name' => $lastName, ':address' => $address1, ':address2' => $address2, ':city' => $city, ':state' => $states, ':zip' => $zip, ':phone' => $phone, ':email' => $email));
if ($result) {
// Success
$_SESSION["message"] = "You've successfully registered.";
redirect_to("login.php");
} else {
// Failure
$_SESSION["message"] = "Registration failed.";
}
}
}
} else {
// This is probably a GET request
}
因此,它首先看到用户是否存在,如果不存在,则继续将用户的信息插入数据库。但这会给TOCTTOU带来漏洞。为了解决这个问题,我将“用户名”列设为UNIQUE。这是我唯一需要做的事情吗?有没有办法将这两个语句结合起来,以免它受到这种类型的攻击?
我一直在阅读这方面的资源,似乎使列UNIQUE或PRIMARY是最好的选择。但是,我不想依赖数据库。有什么建议?谢谢!
这是我的固定代码:
在文件顶部:
ini_set('display_errors', 'On'); error_reporting(E_ALL);
然后:
$connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$connection->beginTransaction();
try {
$stmt = $connection->prepare('SELECT 1 FROM users WHERE username = :username LIMIT 1');
$stmt->execute([':username' => $username]);
$check = $stmt->fetchColumn();
if ($check) {
$_SESSION["message"] = "name already exists";
} else {
$stmt = $connection->prepare('INSERT INTO users(company, username, password, first_name, last_name, address, address2, city, state, zip, phone, email) VALUES (:company, :username, :password, :first_name, :last_name, :address, :address2, :city, :state, :zip, :phone, :email)');
$stmt->execute(array(':company' => $company, ':username' => $username, ':password' => $hashed_password, ':first_name' => $firstName, ':last_name' => $lastName, ':address' => $address1, ':address2' => $address2, ':city' => $city, ':state' => $states, ':zip' => $zip, ':phone' => $phone, ':email' => $email)); // this should throw an exception if it fails, no need to check the return value
$_SESSION["message"] = "You've successfully registered.";
}
$connection->commit();
redirect_to("login.php");
} catch (PDOException $e) {
$connection->rollBack();
throw $e;
}
答案 0 :(得分:1)
我会在你的桌面上保持唯一约束,因为从数据的角度来看这是有意义的。
对于您的PHP代码,只需使用事务。例如......
// make sure you have $connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION)
$connection->beginTransaction();
try {
$stmt = $connection->prepare('SELECT 1 FROM users WHERE username = :username LIMIT 1');
$stmt->execute([':username' => $username]);
$check = $stmt->fetchColumn();
if ($check) {
$_SESSION["message"] = "name already exists";
} else {
$stmt = $connection->prepare('INSERT INTO ...');
$stmt->execute([...]); // this should throw an exception if it fails, no need to check the return value
$_SESSION["message"] = "You've successfully registered.";
}
$connection->commit();
redirect_to("login.php");
} catch (PDOException $e) {
$connection->rollBack();
throw $e;
}