← Tilbage til security guides

Secure Authentication

Sikker autentificering beskytter brugerkonti mod uautoriseret adgang gennem proper implementation af login, session management og access control.

🚨Critical Severity
⚠️

Om Truslen

Autentificering er fundamentet for applikationssikkerhed og en af de mest kritiske områder at få korrekt implementeret. Svage autentificeringsmekanismer er ansvarlige for en betydelig del af alle sikkerhedsbrud og optræder konsekvent i OWASP Top 10 som 'Broken Authentication'. Problemer inkluderer svage passwords, mangelfuld session management, credential stuffing-angreb, brute force-angreb, og insecure password recovery flows. Statistikker viser at over 80% af databrud involverer svage, genbrugte eller stjålne passwords. Moderne autentificering kræver multi-faktor authentication (MFA), strong password policies, secure session management, protection mod brute force-angreb, og sikker håndtering af credentials. Konsekvenserne af svag autentificering kan være katastrofale, fra account takeover og identity theft til omfattende databrud og økonomisk svindel.

Key Points

  • Brug password_hash() og password_verify() med bcrypt eller Argon2 algoritmer
  • Implementer multi-faktor authentication (MFA) for alle brugere eller minimum admin-konti
  • Implementer rate limiting og account lockout efter gentagne fejlede login-forsøg
  • Brug secure, HTTP-only, og SameSite cookies til session management
  • Implementer stærke password policies: minimum 12 tegn, kompleksitet, ingen genbrugte passwords
  • Generer session IDs med kryptografisk sikre random generators
  • Regenerer session ID efter login for at forhindre session fixation
  • Implementer secure logout der invaliderer sessions både client- og server-side
  • Brug HTTPS for ALLE autentificerings-requests - aldrig send credentials over HTTP
  • Implementer sikker password reset flow med time-limited, single-use tokens
  • Log alle autentificerings-events for security monitoring og audit
  • Brug generiske fejlmeddelelser - aldrig afslør om username eller password er forkert
  • Implementer CAPTCHA eller lignende efter gentagne fejlede forsøg
  • Sæt reasonable session timeouts baseret på applikations-sensitivitet
  • Implementer "remember me" functionality sikkert med separate tokens

Sårbar Kode (UNDGÅ)

Brug ALDRIG denne kode i produktion!

<?php
// SÅRBAR KODE - Brug ALDRIG dette i produktion!

// 1. Usikker password storage med MD5 (ALDRIG ACCEPTABELT!)
$username = $_POST['username'];
$password = $_POST['password'];

// MD5 er ikke en password-hashing algoritme!
$hashed_password = md5($password);

$query = "INSERT INTO users (username, password) VALUES ('$username', '$hashed_password')";
mysqli_query($conn, $query);
// MD5 kan crackeres på sekunder med rainbow tables

// 2. Usikker login med detaljerede fejlmeddelelser
$username = $_POST['username'];
$password = $_POST['password'];

$query = "SELECT * FROM users WHERE username = '$username'";
$result = mysqli_query($conn, $query);

if (mysqli_num_rows($result) == 0) {
    die("Username does not exist"); // AFSLØRER at username ikke findes!
}

$user = mysqli_fetch_assoc($result);

if (md5($password) != $user['password']) {
    die("Incorrect password"); // AFSLØRER at password er forkert!
}
// Detaljerede fejlmeddelelser hjælper brute force-angreb

// 3. Ingen rate limiting eller brute force protection
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // Ingen begrænsning på antal login-forsøg
    $username = $_POST['username'];
    $password = $_POST['password'];
    
    // Attacker kan forsøge tusindvis af passwords
    if (checkCredentials($username, $password)) {
        $_SESSION['user'] = $username;
    }
}

// 4. Insecure session management
session_start();
// Ingen session configuration

$_SESSION['logged_in'] = true;
$_SESSION['username'] = $username;
// Session ID regenereres IKKE - sårbar for session fixation

// 5. Usikker password reset
if (isset($_GET['reset'])) {
    $email = $_GET['email'];
    $new_password = rand(100000, 999999); // Svagt password!
    
    // Sender password i plaintext via email
    mail($email, "Password Reset", "Your new password is: $new_password");
    
    // Opdaterer uden token-validering
    $query = "UPDATE users SET password = '" . md5($new_password) . "' WHERE email = '$email'";
    mysqli_query($conn, $query);
}

// 6. Insecure "Remember Me" functionality
if (isset($_POST['remember'])) {
    // Gemmer username og password i plaintext cookies!
    setcookie('username', $username, time() + (86400 * 30));
    setcookie('password', $password, time() + (86400 * 30));
}

if (isset($_COOKIE['username']) && isset($_COOKIE['password'])) {
    // Auto-login uden validering
    $_SESSION['user'] = $_COOKIE['username'];
}

// 7. Manglende HTTPS enforcement
// Ingen check om connection er sikker
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // Sender credentials over potentially usikker connection
    $username = $_POST['username'];
    $password = $_POST['password'];
}

// 8. Usikker logout
if (isset($_GET['logout'])) {
    // Sletter kun session variable, ikke selve sessionen
    unset($_SESSION['user']);
    // Session cookie forbliver valid - sårbar for session replay
}
?>

Sikker Kode (ANBEFALET)

Best practices for sikker implementation

<?php
// SIKKER KODE - Best practices for secure authentication

// 1. Sikker session configuration
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_secure', 1);
ini_set('session.cookie_samesite', 'Strict');
ini_set('session.use_strict_mode', 1);
ini_set('session.use_only_cookies', 1);
ini_set('session.cookie_lifetime', 0); // Session cookie

session_start();

// 2. HTTPS enforcement
if (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] !== 'on') {
    header('Location: https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
    exit;
}

// 3. Sikker user registration med password hashing
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['register'])) {
    $username = trim($_POST['username'] ?? '');
    $password = $_POST['password'] ?? '';
    $email = trim($_POST['email'] ?? '');
    
    // Validér password strength
    if (strlen($password) < 12) {
        die("Password must be at least 12 characters");
    }
    
    if (!preg_match('/[A-Z]/', $password) || 
        !preg_match('/[a-z]/', $password) || 
        !preg_match('/[0-9]/', $password) || 
        !preg_match('/[^A-Za-z0-9]/', $password)) {
        die("Password must contain uppercase, lowercase, numbers, and special characters");
    }
    
    // Check for common passwords
    $common_passwords = ['Password123!', 'Welcome123!', 'Admin123!'];
    if (in_array($password, $common_passwords, true)) {
        die("Password is too common");
    }
    
    // Hash password med bcrypt (default) eller Argon2
    $hashed_password = password_hash($password, PASSWORD_ARGON2ID, [
        'memory_cost' => 65536,
        'time_cost' => 4,
        'threads' => 3
    ]);
    
    // Prepared statement for at forhindre SQL injection
    $stmt = $pdo->prepare("INSERT INTO users (username, email, password, created_at) VALUES (?, ?, ?, NOW())");
    
    try {
        $stmt->execute([$username, $email, $hashed_password]);
        echo "Registration successful";
    } catch (PDOException $e) {
        // Generisk fejlmeddelelse - afslør ikke specifik årsag
        error_log("Registration error: " . $e->getMessage());
        die("Registration failed. Please try again.");
    }
}

// 4. Sikker login med rate limiting
class RateLimiter {
    private $pdo;
    
    public function __construct($pdo) {
        $this->pdo = $pdo;
    }
    
    public function checkRateLimit($identifier, $max_attempts = 5, $window = 900) {
        $stmt = $this->pdo->prepare(
            "SELECT COUNT(*) FROM login_attempts 
             WHERE identifier = ? AND attempt_time > DATE_SUB(NOW(), INTERVAL ? SECOND)"
        );
        $stmt->execute([$identifier, $window]);
        $attempts = $stmt->fetchColumn();
        
        return $attempts < $max_attempts;
    }
    
    public function logAttempt($identifier) {
        $stmt = $this->pdo->prepare(
            "INSERT INTO login_attempts (identifier, attempt_time) VALUES (?, NOW())"
        );
        $stmt->execute([$identifier]);
    }
}

$rateLimiter = new RateLimiter($pdo);

if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['login'])) {
    $username = trim($_POST['username'] ?? '');
    $password = $_POST['password'] ?? '';
    $ip_address = $_SERVER['REMOTE_ADDR'];
    
    // Rate limiting check
    if (!$rateLimiter->checkRateLimit($ip_address)) {
        http_response_code(429);
        die("Too many login attempts. Please try again later.");
    }
    
    // Hent bruger fra database
    $stmt = $pdo->prepare("SELECT id, username, password, mfa_secret, account_locked FROM users WHERE username = ?");
    $stmt->execute([$username]);
    $user = $stmt->fetch(PDO::FETCH_ASSOC);
    
    // Timing-safe comparison
    $login_successful = false;
    
    if ($user && !$user['account_locked']) {
        if (password_verify($password, $user['password'])) {
            $login_successful = true;
        }
    }
    
    if (!$login_successful) {
        // Log failed attempt
        $rateLimiter->logAttempt($ip_address);
        
        // Generisk fejlmeddelelse
        sleep(2); // Delay for at gøre brute force langsommere
        die("Invalid credentials");
    }
    
    // Check om password skal rehashes (f.eks. hvis algoritme opdateres)
    if (password_needs_rehash($user['password'], PASSWORD_ARGON2ID)) {
        $new_hash = password_hash($password, PASSWORD_ARGON2ID);
        $stmt = $pdo->prepare("UPDATE users SET password = ? WHERE id = ?");
        $stmt->execute([$new_hash, $user['id']]);
    }
    
    // Regenerer session ID for at forhindre session fixation
    session_regenerate_id(true);
    
    // Sæt session variables
    $_SESSION['user_id'] = $user['id'];
    $_SESSION['username'] = $user['username'];
    $_SESSION['login_time'] = time();
    $_SESSION['ip_address'] = $ip_address;
    
    // Log successful login
    $stmt = $pdo->prepare(
        "INSERT INTO login_log (user_id, ip_address, login_time) VALUES (?, ?, NOW())"
    );
    $stmt->execute([$user['id'], $ip_address]);
    
    echo "Login successful";
}

// 5. Sikker logout
if (isset($_GET['logout'])) {
    // Invalidér session data
    $_SESSION = [];
    
    // Slet session cookie
    if (isset($_COOKIE[session_name()])) {
        setcookie(session_name(), '', time() - 3600, '/');
    }
    
    // Destroy session
    session_destroy();
    
    header('Location: /login.php');
    exit;
}

// 6. Session timeout check
if (isset($_SESSION['user_id'])) {
    $timeout = 1800; // 30 minutter
    
    if (isset($_SESSION['login_time']) && (time() - $_SESSION['login_time'] > $timeout)) {
        // Session expired
        session_unset();
        session_destroy();
        header('Location: /login.php?timeout=1');
        exit;
    }
    
    // Opdater login time for aktivitet
    $_SESSION['login_time'] = time();
}
?>

Almindelige Fejl

  • At bruge svage hashing-algoritmer som MD5 eller SHA1 til passwords - brug kun password_hash() med bcrypt eller Argon2
  • At ikke implementere rate limiting, hvilket tillader ubegrænset brute force-angreb
  • At give specifikke fejlmeddelelser der afslører om username eller password er forkert
  • At ikke regenerere session ID efter login, hvilket gør applikationen sårbar for session fixation
  • At sende credentials over usikre HTTP-forbindelser i stedet for HTTPS
  • At implementere usikre 'remember me' funktioner der gemmer passwords i plaintext cookies

Forebyggelsesteknikker

  • 🛡️Brug password_hash() og password_verify() med PASSWORD_ARGON2ID eller PASSWORD_BCRYPT
  • 🛡️Implementer rate limiting og account lockout efter 5-10 fejlede login-forsøg
  • 🛡️Konfigurer secure session settings: HTTP-only, Secure, SameSite cookies
  • 🛡️Implementer multi-faktor authentication (MFA) med TOTP eller hardware tokens
  • 🛡️Brug generiske fejlmeddelelser og timing-safe comparisons
  • 🛡️Implementer session regeneration efter login og privilege escalation
  • 🛡️Enforce HTTPS for alle autentificerings-relaterede requests
  • 🛡️Implementer comprehensive logging og monitoring af authentication events

Testing Metoder

  • 🔍Brute force testing med værktøjer som Hydra eller Burp Suite Intruder
  • 🔍Test password policies ved at forsøge at oprette svage passwords
  • 🔍Test session management for session fixation og session hijacking sårbarheder
  • 🔍Verificer at rate limiting virker ved at forsøge mange login-forsøg
  • 🔍Test password reset flow for timing-attacks og token-gætning

Quick Info

Severity Level
🚨Critical
Relaterede Trusler
  • Session Hijacking - Tyveri af session tokens
  • Credential Stuffing - Brug af stjålne credentials fra andre brud
  • Session Fixation - Forcering af en kendt session ID

⚠️Sikkerhedsadvarsel

Sikkerhedssårbarheder kan have alvorlige konsekvenser. Test altid grundigt i et sikkert miljø og implementer alle anbefalede forebyggelsesteknikker.