Files
4WDCSA.co.za/send_reset_link.php
twotalesanimation a4526979c4 Phase 2: Add rate limiting and session regeneration
- Created RateLimitMiddleware class with 8 public methods
  * isLimited() - check if limit exceeded
  * incrementAttempt() - increment attempt counter
  * getRemainingAttempts() - get remaining attempts
  * getTimeRemaining() - get time remaining in window
  * reset() - reset counter after success
  * requireLimit() - check and die if exceeded
  * getStatus() - get status info for monitoring
  * Support for time-window based rate limiting

- Integrated rate limiting into critical endpoints:
  * validate_login.php: 5 attempts per 900 seconds (15 minutes)
  * send_reset_link.php: 3 attempts per 1800 seconds (30 minutes)
  * Prevents brute force attacks and password reset abuse
  * Still increments counter for non-existent emails (prevents enumeration)

- Integrated session regeneration on successful login:
  * Google OAuth login (both new and existing users)
  * Email/password login
  * Uses AuthenticationService::regenerateSession()
  * Prevents session fixation attacks

- Rate limit counters stored in PHP session
- Time-window based with 15-minute and 30-minute windows
- Graceful error messages with retry_after in JSON responses
- AJAX-aware error handling
2025-12-02 21:10:48 +02:00

65 lines
2.4 KiB
PHP

<?php
require_once("env.php");
require_once("connection.php");
require_once("functions.php");
use Middleware\RateLimitMiddleware;
$response = array('status' => 'error', 'message' => 'Something went wrong');
if (isset($_POST['email'])) {
// Check rate limit first (3 attempts per 30 minutes to prevent abuse)
if (RateLimitMiddleware::isLimited('password_reset', 3, 1800)) {
$remaining = RateLimitMiddleware::getTimeRemaining('password_reset', 1800);
$response['status'] = 'error';
$response['message'] = "Too many password reset requests. Please try again in {$remaining} seconds.";
$response['retry_after'] = $remaining;
echo json_encode($response);
exit();
}
$email = $_POST['email'];
// Check if the email exists
$sql = "SELECT user_id FROM users WHERE email = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("s", $email);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows > 0) {
$user = $result->fetch_assoc();
$user_id = $user['user_id'];
// Generate a unique token
$token = bin2hex(random_bytes(50));
// Store the token and expiration time in the database
$expiry = date("Y-m-d H:i:s", strtotime('+3 hour')); // Token expires in 3 hour
$sql = "INSERT INTO password_resets (user_id, token, expires_at) VALUES (?, ?, ?)
ON DUPLICATE KEY UPDATE token = VALUES(token), expires_at = VALUES(expires_at)";
$stmt = $conn->prepare($sql);
$stmt->bind_param("iss", $user_id, $token, $expiry);
$stmt->execute();
// Send the reset link to the user
$reset_link = "https://www.4wdcsa.co.za/reset_password.php?token=$token";
$subject = "Password Reset Request";
$message = "Click the following link to reset your password: $reset_link";
sendEmail($email, $subject, $message);
// Reset rate limit on successful request
RateLimitMiddleware::reset('password_reset');
$response['status'] = 'success';
$response['message'] = 'Password reset link has been sent to your email.';
} else {
// Increment rate limit even for non-existent emails (prevent email enumeration)
RateLimitMiddleware::incrementAttempt('password_reset', 1800);
$response['message'] = 'Email not found.';
}
}
echo json_encode($response);
?>