PHP Form Security


Why PHP Form Security is Critical

Every web form is a potential attack vector. Without proper security measures, your PHP forms can be exploited for:

  • Cross-Site Scripting (XSS) attacks
  • SQL Injection attacks
  • CSRF (Cross-Site Request Forgery)
  • Spam submissions
  • Data corruption

This guide covers professional-grade PHP form security techniques to bulletproof your web applications.



Essential PHP Form Security Layers

1. Input Sanitization

function secure_input($data) {
    $data = trim($data);                // Remove whitespace
    $data = stripslashes($data);        // Remove backslashes
    $data = htmlspecialchars($data, ENT_QUOTES | ENT_HTML5, 'UTF-8');
    return $data;
}

// Usage:
$username = secure_input($_POST['username']);

Why this matters:

  • Prevents XSS attacks by converting <script> to &lt;script&gt;
  • Standardizes input format
  • Protects against malformed data


2. Validation Rules with Regular Expressions

// Validate email format
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
    $errors['email'] = "Invalid email format";
}

// Validate username (alphanumeric + underscore, 3-20 chars)
if (!preg_match("/^[a-zA-Z0-9_]{3,20}$/", $username)) {
    $errors['username'] = "Only letters, numbers and underscore (3-20 chars)";
}

// Validate URL
if (!empty($website) && !filter_var($website, FILTER_VALIDATE_URL)) {
    $errors['website'] = "Invalid website URL";
}

Output Examples:

Valid: user_123, test@example.com

Invalid: user<script>, invalid.email



3. CSRF Protection

// Generate token
if (empty($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}

// Add to form
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token']; ?>">

// Validate on submission
if ($_POST['csrf_token'] !== $_SESSION['csrf_token']) {
    die("CSRF token validation failed");
}

Why this matters:

  • Prevents form hijacking
  • Stops unauthorized submissions
  • Essential for state-changing operations (logins, payments)


4. Secure File Uploads

$allowed_types = ['image/jpeg', 'image/png'];
$max_size = 2 * 1024 * 1024; // 2MB

if ($_FILES['upload']['error'] === UPLOAD_ERR_OK) {
    // Verify file type
    $finfo = new finfo(FILEINFO_MIME_TYPE);
    $mime = $finfo->file($_FILES['upload']['tmp_name']);
    
    if (!in_array($mime, $allowed_types)) {
        die("Invalid file type");
    }
    
    // Verify file size
    if ($_FILES['upload']['size'] > $max_size) {
        die("File too large");
    }
    
    // Move to secure location
    $new_name = uniqid('img_', true) . '.jpg';
    move_uploaded_file(
        $_FILES['upload']['tmp_name'],
        '/var/www/uploads/' . $new_name
    );
}

Security Checklist:

  • ✔ Verify MIME type (not just extension)
  • ✔ Set strict size limits
  • ✔ Rename files to prevent directory traversal
  • ✔ Store outside web root when possible


5. Password Security

// Hashing passwords
$hashed_password = password_hash($password, PASSWORD_BCRYPT);

// Verification
if (password_verify($input_password, $stored_hash)) {
    // Login successful
}

// Rehash if needed
if (password_needs_rehash($stored_hash, PASSWORD_BCRYPT)) {
    $new_hash = password_hash($input_password, PASSWORD_BCRYPT);
    // Update in database
}

Best Practices:

  • Always use password_hash() (never md5/sha1)
  • BCRYPT is currently the best algorithm
  • Consider peppering (additional secret salt)


Complete Secure Form Example

<?php
session_start();
$errors = [];

// CSRF Protection
if (empty($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // Validate CSRF token
    if (!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
        die("Security violation detected");
    }

    // Sanitize inputs
    $email = filter_var($_POST['email'], FILTER_SANITIZE_EMAIL);
    $username = preg_replace('/[^a-zA-Z0-9_]/', '', $_POST['username']);

    // Validate
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        $errors['email'] = "Invalid email address";
    }
    
    if (strlen($username) < 3) {
        $errors['username'] = "Username too short";
    }

    // Process if valid
    if (empty($errors)) {
        // Database insertion with prepared statements
        $stmt = $pdo->prepare("INSERT INTO users (email, username) VALUES (?, ?)");
        $stmt->execute([$email, $username]);
        header("Location: /success.php");
        exit;
    }
}
?>

<form method="post">
    <input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
    
    <label>Email:
        <input type="email" name="email" required 
               value="<?= htmlspecialchars($_POST['email'] ?? '') ?>">
        <?= $errors['email'] ?? '' ?>
    </label>

    <label>Username:
        <input type="text" name="username" required
               pattern="[a-zA-Z0-9_]{3,20}" 
               value="<?= htmlspecialchars($_POST['username'] ?? '') ?>">
        <?= $errors['username'] ?? '' ?>
    </label>

    <button type="submit">Register</button>
</form>


Advanced Security Measures

1. Rate Limiting

// Using Redis for rate limiting
$redis = new Redis();
$redis->connect('127.0.0.1');

$ip = $_SERVER['REMOTE_ADDR'];
$key = "form_submit:$ip";

if ($redis->get($key) > 5) {
    die("Too many submissions. Try again later.");
}
$redis->incr($key);
$redis->expire($key, 3600); // 1 hour limit

2. Content Security Policy (CSP)

header("Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'");

3. Database Security

// Using PDO with prepared statements
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = ?");
$stmt->execute([$email]);
$user = $stmt->fetch();


Security Checklist for PHP Forms

  • Always sanitize and validate all inputs
  • Implement CSRF protection
  • Use prepared statements for databases
  • Secure file uploads with MIME verification
  • Hash passwords with bcrypt
  • Set secure session configurations
  • Add rate limiting to prevent brute force
  • Implement CSP headers
  • Keep PHP updated to patch vulnerabilities