Harden sign-in and password reset

- OTP attempt limits, constant-time compare, random reference codes
- DB-backed rate limits (429) on sign-in, OTP, reset, register, onboarding
- one generic sign-in failure message; reset request no longer reveals accounts
- no password kept in the session; real status codes on failures
This commit is contained in:
Thanakorn
2026-09-24 14:53:40 +07:00
parent ae98dcdcdd
commit 73c680e844
16 changed files with 538 additions and 282 deletions
+10 -2
View File
@@ -55,6 +55,8 @@ require_once '../../../config.php';
require_once '../../../dbconn.php';
require_once '../../../assets/utils/db_helpers.php';
require_once '../../../assets/utils/otp_policy.php';
require_once '../../../assets/utils/rate_limit.php';
require_once '../../../assets/utils/secret_box.php';
header('Content-Type: application/json; charset=utf-8');
@@ -85,7 +87,7 @@ if ($sth->fetchColumn() !== 'owner') {
// ── Step 2: CSRF check ────────────────────────────────────────────────────────
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$csrf = $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
if (empty($csrf) || $csrf !== ($_SESSION['csrf_token'] ?? '')) {
if (empty($csrf) || !hash_equals((string)($_SESSION['csrf_token'] ?? ''), $csrf)) {
http_response_code(403);
$answer['message'] = 'Invalid request.';
exit(json_encode($answer));
@@ -94,6 +96,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$data = json_decode($_POST['json'] ?? '{}', true) ?: [];
// ── Step 2b: Throttle — each attempt sends an SMTP test email ────────────────
rate_limit_guard($pdo1, [
['onboarding_ip', rate_limit_client_ip(), 20, 900],
['onboarding_user', (string)$user_id, 10, 900],
]);
try {
// ── Step 3: Sanitise input ────────────────────────────────────────────────
@@ -147,7 +155,7 @@ try {
// ── Step 7: Encrypt SMTP password ────────────────────────────────────
// Uses the same OpenSSL method/iv/key as the rest of the app (from config.php)
// so the stored password can be decrypted by the mailer module.
$encrypted_pass = openssl_encrypt($smtp_password, $method, $pinkey, 0, $iv);
$encrypted_pass = secret_encrypt($smtp_password, $pinkey);
// Assemble a temporary SMTP config for the test send (step 8)
$smtp_config = [