如何提升HTML联系表单的安全性?附现有表单代码
Great question! You’re already off to a strong start by including a CSRF token with HMAC—let’s build on that to lock down your form against common web security threats. Here’s a step-by-step breakdown:
1. Harden Your CSRF Protection
Your current token uses hash_hmac('sha256', 'some_string', $_SESSION['token'])—let’s make this more robust:
- Use a unique, random session secret: Replace the static
'some_string'with a cryptographically secure random value generated once per user session. For example:// Generate a secure secret when the session starts if (!isset($_SESSION['csrf_secret'])) { $_SESSION['csrf_secret'] = bin2hex(random_bytes(32)); } // Create a token tied to the user's session and timestamp $csrf_token = hash_hmac('sha256', session_id() . time(), $_SESSION['csrf_secret']); - Validate tokens securely on the backend: When processing the form, recalculate the HMAC and compare it with the submitted token using
hash_equals()to prevent timing attacks:if (!isset($_POST['token']) || !hash_equals( hash_hmac('sha256', session_id() . time(), $_SESSION['csrf_secret']), $_POST['token'] )) { die("Invalid request. Please try again."); } - Secure your session cookie: Configure session cookies to block XSS and cross-site misuse:
session_set_cookie_params([ 'httponly' => true, // Prevent JS access to session cookie 'secure' => true, // Only send over HTTPS 'samesite' => 'Strict', // Block cross-site requests 'lifetime' => 3600 // Expire after 1 hour ]); session_start();
2. Enforce Strict Input Validation
Never trust user input—frontend validation is just for UX; backend validation is non-negotiable:
- Email validation: Use PHP’s built-in filter to ensure valid format:
$email = filter_var(trim($_POST['email']), FILTER_VALIDATE_EMAIL); if (!$email) { // Handle invalid email (e.g., show error message) } - Name validation: Restrict allowed characters to letters, spaces, and common accents (avoid special characters that could enable injection):
$nome = trim($_POST['nome']); if (!preg_match('/^[a-zA-ZÀ-ÿ\s]{1,50}$/', $nome)) { // Handle invalid name } - Add HTML constraints: Guide users and reduce invalid submissions with attributes like
required,maxlength, andtype="email":<input type="email" class="form-control" placeholder="Email" id="email" name="email" required maxlength="255"> <input type="text" class="form-control" placeholder="Nome" id="nome" name="nome" required maxlength="50">
3. Prevent Email Injection (If Sending Emails)
If your form sends emails via PHP’s mail() function, attackers can inject malicious headers. Fix this by:
- Stripping newline characters: Remove
\r,\n, and URL-encoded equivalents from inputs:$nome = str_replace(["\r", "\n", "%0A", "%0D"], "", $_POST['nome']); $email = str_replace(["\r", "\n", "%0A", "%0D"], "", $_POST['email']); - Using a library like PHPMailer: It automatically handles header injection and is more secure than raw
mail():use PHPMailer\PHPMailer\PHPMailer; $mail = new PHPMailer(); $mail->addAddress('your-contact-email@example.com'); $mail->setFrom($email, $nome); $mail->Subject = 'New Contact Form Submission'; $mail->Body = "Name: $nome\nEmail: $email"; $mail->send();
4. Use HTTPS Exclusively
Always serve your form over HTTPS to encrypt data in transit—this prevents attackers from intercepting form inputs (including your CSRF token). Configure your server to redirect all HTTP traffic to HTTPS.
5. Block XSS Attacks
If you ever display user input back to the browser (e.g., a "thank you" page), escape it with htmlspecialchars() to prevent cross-site scripting:
echo "Thank you, " . htmlspecialchars($nome, ENT_QUOTES, 'UTF-8') . "! We'll get back to you soon.";
6. Add Rate Limiting
Prevent spam or brute-force attacks by limiting how often a user can submit the form:
// Track submission frequency in the session if (!isset($_SESSION['submission_count'])) { $_SESSION['submission_count'] = 0; $_SESSION['last_submission'] = time(); } // Block if more than 5 submissions in an hour if ($_SESSION['submission_count'] >= 5 && time() - $_SESSION['last_submission'] < 3600) { die("Too many submissions. Please try again in an hour."); } // Reset count after an hour if (time() - $_SESSION['last_submission'] >= 3600) { $_SESSION['submission_count'] = 0; } // Update submission tracking $_SESSION['submission_count']++; $_SESSION['last_submission'] = time();
7. Avoid Information Disclosure in Errors
Never reveal specific failure reasons to users. Instead of saying "CSRF token invalid", use generic messages like "Invalid request. Please try again." to avoid giving attackers clues.
内容的提问来源于stack exchange,提问作者eskimopest

