العربية

Module 16: Security Basics

Lesson 1: SQL Injection and Input Validation

Instructor notes

SQL injection is the most common web vulnerability. Show real examples of attacks. Emphasize that user input is NEVER safe by default. Use the "bobby tables" analogy to make it memorable.

Presentation outline

  1. What is SQL injection?
  2. How attackers exploit SQL injection
  3. Vulnerable code examples
  4. Prepared statements and parameterized queries
  5. Input validation techniques

Demonstration

  1. Show a vulnerable login form that accepts ' OR '1'='1
  2. Fix it with PDO prepared statements
  3. Add validation: required fields, email format, length checks
  4. Test with malicious input and verify it fails safely

AI prompts

  1. "Show me a vulnerable PHP login script with SQL injection. Then show the secure version using PDO prepared statements."
  2. "Write PHP input validation functions for: email, phone number, and password (min 8 chars, 1 number, 1 special char)."
  3. "What is the difference between client-side and server-side validation? Why do we need both?"

Starter code

<?php
// login.php - VULNERABLE CODE (DO NOT USE IN PRODUCTION)
$email = $_POST['email'] ?? '';
$password = $_POST['password'] ?? '';

$pdo = new PDO('mysql:host=localhost;dbname=store', 'root', '');
$query = "SELECT * FROM users WHERE email = '$email' AND password = '$password'";
$stmt = $pdo->query($query);
$user = $stmt->fetch();

if ($user) {
    echo "Welcome " . $user['name'];
} else {
    echo "Invalid login";
}
?>

Final code

<?php
// login.php - SECURE CODE
session_start();
require_once 'config.php';

function validateInput($data) {
    $data = trim($data);
    $data = stripslashes($data);
    $data = htmlspecialchars($data);
    return $data;
}

function validateEmail($email) {
    return filter_var($email, FILTER_VALIDATE_EMAIL);
}

function validatePassword($password) {
    if (strlen($password) < 8) {
        return "Password must be at least 8 characters";
    }
    if (!preg_match('/[A-Z]/', $password)) {
        return "Password must contain an uppercase letter";
    }
    if (!preg_match('/[a-z]/', $password)) {
        return "Password must contain a lowercase letter";
    }
    if (!preg_match('/[0-9]/', $password)) {
        return "Password must contain a number";
    }
    return true;
}

$email = validateInput($_POST['email'] ?? '');
$password = $_POST['password'] ?? '';
$errors = [];

// Server-side validation
if (empty($email)) {
    $errors[] = "Email is required";
} elseif (!validateEmail($email)) {
    $errors[] = "Invalid email format";
}

if (empty($password)) {
    $errors[] = "Password is required";
}

if (empty($errors)) {
    try {
        $stmt = $pdo->prepare("SELECT id, name, password_hash FROM users WHERE email = :email");
        $stmt->execute(['email' => $email]);
        $user = $stmt->fetch();
        
        if ($user && password_verify($password, $user['password_hash'])) {
            $_SESSION['user_id'] = $user['id'];
            $_SESSION['user_name'] = $user['name'];
            header('Location: dashboard.php');
            exit();
        } else {
            $errors[] = "Invalid email or password";
        }
    } catch(PDOException $e) {
        error_log("Login error: " . $e->getMessage());
        $errors[] = "An error occurred. Please try again.";
    }
}

foreach ($errors as $error) {
    echo "<p style='color: red;'>$error</p>";
}
?>

Exercises

  1. Write a login form and test it with SQL injection payloads like ' OR '1'='1.
  2. Create a registration form with validation for name, email, and password.
  3. Write a function that sanitizes user input for database queries.

Homework

  1. Take an existing CRUD application and fix all SQL injection vulnerabilities.
  2. Write a security report identifying 5 vulnerable code patterns you found online and how to fix them.

Quiz

  1. What is SQL injection?
    • A) A database optimization technique
    • B) Inserting malicious SQL through user input
    • C) A backup method
    • D) A query builder
    • Answer: B
  2. Which is the safest way to query a database in PHP?
    • A) String concatenation
    • B) Prepared statements
    • C) eval()
    • D) mysql_query()
    • Answer: B
  3. What does htmlspecialchars() do?
    • A) Encrypts data
    • B) Converts special characters to HTML entities
    • C) Validates email
    • D) Hashes passwords
    • Answer: B
  4. Where should input validation happen?
    • A) Only in JavaScript
    • B) Only in PHP
    • C) Both client-side and server-side
    • D) Only in the database
    • Answer: C
  5. What is a prepared statement?
    • A) A SQL query with placeholders
    • B) A PHP array
    • C) A database backup
    • D) A connection method
    • Answer: A

Lesson 2: XSS and Output Escaping

Instructor notes

XSS lets attackers inject JavaScript into your pages. Show how a simple comment form can become an attack vector. Emphasize: escape on output, not input.

Presentation outline

  1. What is XSS?
  2. Types of XSS: stored, reflected, DOM-based
  3. How XSS attacks work
  4. Escaping output with htmlspecialchars()
  5. Content Security Policy

Demonstration

  1. Create a vulnerable comment form
  2. Post a comment with <script>alert('XSS')</script>
  3. Fix with htmlspecialchars() on output
  4. Add CSP header as defense in depth

AI prompts

  1. "Explain the three types of XSS with simple examples. Include stored, reflected, and DOM-based."
  2. "Write a PHP comment system that is vulnerable to XSS, then fix it with proper output escaping."
  3. "What is Content Security Policy? Show me a basic CSP header for a PHP website."

Starter code

<?php
// comments.php - VULNERABLE TO XSS
$comments = [];

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $comments[] = $_POST['comment'];
}

foreach ($comments as $comment) {
    echo "<div class='comment'>$comment</div>";
}
?>

<form method="POST">
    <textarea name="comment"></textarea>
    <button type="submit">Post</button>
</form>

Final code

<?php
// comments.php - SECURE AGAINST XSS
session_start();
require_once 'config.php';

// Set security headers
header('Content-Type: text/html; charset=utf-8');
header('X-Content-Type-Options: nosniff');
header('X-Frame-Options: DENY');

// Simple in-memory storage (use database in production)
$comments = $_SESSION['comments'] ?? [];

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $comment = trim($_POST['comment'] ?? '');
    if (!empty($comment) && strlen($comment) < 500) {
        $comments[] = [
            'text' => $comment,
            'date' => date('Y-m-d H:i')
        ];
        $_SESSION['comments'] = $comments;
    }
}

function escapeOutput($string) {
    return htmlspecialchars($string, ENT_QUOTES | ENT_HTML5, 'UTF-8');
}
?>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Secure Comments</title>
    <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';">
</head>
<body>
    <h1>Comments</h1>
    
    <?php foreach ($comments as $comment): ?>
        <div class="comment">
            <p><?php echo escapeOutput($comment['text']); ?></p>
            <small><?php echo escapeOutput($comment['date']); ?></small>
        </div>
    <?php endforeach; ?>
    
    <form method="POST">
        <textarea name="comment" maxlength="500" required></textarea><br>
        <button type="submit">Post Comment</button>
    </form>
</body>
</html>

Exercises

  1. Post <script>alert('XSS')</script> as a comment in the vulnerable version. See it execute.
  2. Fix the vulnerable code by escaping output with htmlspecialchars().
  3. Add a CSP header to block inline scripts.

Homework

  1. Audit three public websites for XSS vulnerabilities using browser developer tools.
  2. Write a secure comment system that stores comments in a MySQL database.

Quiz

  1. What does XSS stand for?
    • A) Cross-Site Scripting
    • B) Extra Style Sheet Syntax
    • C) Extended Server Script
    • D) XML Site Security
    • Answer: A
  2. Which PHP function prevents XSS?
    • A) htmlspecialchars()
    • B) strip_tags()
    • C) Both can help
    • D) mysqli_real_escape_string()
    • Answer: C
  3. What is stored XSS?
    • A) Malicious script saved in the database
    • B) Script in the URL
    • C) Script in JavaScript variables
    • D) Script in CSS
    • Answer: A
  4. When should you escape output?
    • A) When storing data
    • B) When displaying data
    • C) When receiving data
    • D) Never
    • Answer: B
  5. What does CSP stand for?
    • A) Content Security Policy
    • B) Cross-Site Protection
    • C) Cascading Style Protection
    • D) Content Script Protocol
    • Answer: A

Lesson 3: CSRF Protection and Session Security

Instructor notes

CSRF tricks users into performing actions they did not intend. Use the "bank transfer" example. Emphasize that GET requests should never change state.

Presentation outline

  1. What is CSRF?
  2. How CSRF attacks work
  3. CSRF tokens
  4. Session security best practices
  5. Secure session configuration

Demonstration

  1. Create a money transfer form without CSRF protection
  2. Show how an attacker could use a hidden image tag to trigger it
  3. Add CSRF token generation and validation
  4. Configure secure session settings in php.ini or .htaccess

AI prompts

  1. "Explain CSRF with a simple bank transfer example. How does the attack happen?"
  2. "Write a PHP function that generates and validates CSRF tokens for forms."
  3. "What are the best practices for securing PHP sessions? Show code examples."

Starter code

<?php
// transfer.php - VULNERABLE TO CSRF
session_start();

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $to = $_POST['to'];
    $amount = $_POST['amount'];
    
    // Transfer money without verification
    echo "Transferred $amount to $to";
}
?>

<form method="POST">
    <input type="text" name="to" placeholder="Recipient">
    <input type="number" name="amount" placeholder="Amount">
    <button type="submit">Transfer</button>
</form>

Final code

<?php
// transfer.php - SECURE WITH CSRF PROTECTION
session_start();

function generateCSRFToken() {
    if (empty($_SESSION['csrf_token'])) {
        $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
    }
    return $_SESSION['csrf_token'];
}

function validateCSRFToken($token) {
    if (empty($_SESSION['csrf_token']) || empty($token)) {
        return false;
    }
    return hash_equals($_SESSION['csrf_token'], $token);
}

function regenerateSession() {
    session_regenerate_id(true);
}

// Secure session configuration
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_secure', 1);
ini_set('session.cookie_samesite', 'Strict');

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $token = $_POST['csrf_token'] ?? '';
    
    if (!validateCSRFToken($token)) {
        die("Invalid CSRF token");
    }
    
    $to = filter_input(INPUT_POST, 'to', FILTER_SANITIZE_STRING);
    $amount = filter_input(INPUT_POST, 'amount', FILTER_VALIDATE_FLOAT);
    
    if ($amount > 0 && $amount <= 1000) {
        echo "Transferred $" . number_format($amount, 2) . " to " . htmlspecialchars($to);
        regenerateSession();
    } else {
        echo "Invalid amount";
    }
}

$csrf_token = generateCSRFToken();
?>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Secure Transfer</title>
</head>
<body>
    <form method="POST">
        <input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrf_token); ?>">
        <input type="text" name="to" placeholder="Recipient" required>
        <input type="number" name="amount" placeholder="Amount" required min="1" max="1000">
        <button type="submit">Transfer</button>
    </form>
</body>
</html>
<?php
// secure_session.php - Session security configuration
session_start();

// Regenerate session ID periodically
if (!isset($_SESSION['created'])) {
    $_SESSION['created'] = time();
} else if (time() - $_SESSION['created'] > 1800) {
    session_regenerate_id(true);
    $_SESSION['created'] = time();
}

// Check user agent consistency
if (isset($_SESSION['user_agent'])) {
    if ($_SESSION['user_agent'] !== $_SERVER['HTTP_USER_AGENT']) {
        session_destroy();
        header('Location: login.php?error=session_invalid');
        exit();
    }
} else {
    $_SESSION['user_agent'] = $_SERVER['HTTP_USER_AGENT'];
}

// Check IP address (optional, can break mobile users)
if (isset($_SESSION['ip_address'])) {
    if ($_SESSION['ip_address'] !== $_SERVER['REMOTE_ADDR']) {
        session_destroy();
        header('Location: login.php?error=session_invalid');
        exit();
    }
} else {
    $_SESSION['ip_address'] = $_SERVER['REMOTE_ADDR'];
}
?>

Exercises

  1. Add CSRF tokens to a contact form.
  2. Test session fixation by manually setting PHPSESSID before login.
  3. Configure PHP to use secure cookie settings.

Homework

  1. Add CSRF protection to all forms in your CRUD application.
  2. Write a session security guide for your team explaining why each setting matters.

Quiz

  1. What does CSRF stand for?
    • A) Cross-Site Request Forgery
    • B) Client-Side Request Form
    • C) Cascading Style Request Format
    • D) Cross-Server Response Function
    • Answer: A
  2. What prevents CSRF?
    • A) CSRF tokens
    • B) HTML escaping
    • C) HTTPS
    • D) Password hashing
    • Answer: A
  3. What is session fixation?
    • A) Setting a known session ID before login
    • B) Fixing session errors
    • C) Remembering user preferences
    • D) Encrypting sessions
    • Answer: A
  4. What does session_regenerate_id() do?
    • A) Creates a new session ID
    • B) Deletes all sessions
    • C) Validates the session
    • D) Encrypts session data
    • Answer: A
  5. What does HttpOnly cookie flag do?
    • A) Prevents JavaScript access
    • B) Forces HTTPS
    • C) Encrypts the cookie
    • D) Extends cookie lifetime
    • Answer: A

Lesson 4: Password Hashing and Authentication Security

Instructor notes

Password breaches are common. Show real-world examples of leaked databases. Emphasize that bcrypt (default in PHP) is slow by design to resist brute force.

Presentation outline

  1. Password security fundamentals
  2. Hashing vs encryption
  3. PHP password hashing functions
  4. Secure login implementation
  5. Rate limiting and account lockout

Demonstration

  1. Hash a password with password_hash()
  2. Verify it with password_verify()
  3. Show how the hash changes each time
  4. Implement a simple login with rate limiting

AI prompts

  1. "Show me how to securely hash and verify passwords in PHP using password_hash() and password_verify()."
  2. "Write a PHP login system with rate limiting that locks accounts after 5 failed attempts for 15 minutes."
  3. "What are the recommended password policies? How do they balance security and usability?"

Starter code

<?php
// auth.php - STORING PASSWORDS IN PLAIN TEXT (NEVER DO THIS)
$password = $_POST['password'];

// Save to database as-is
$pdo->query("INSERT INTO users (email, password) VALUES ('$email', '$password')");
?>

Final code

<?php
// auth.php - SECURE AUTHENTICATION SYSTEM
session_start();
require_once 'config.php';

function hashPassword($password) {
    return password_hash($password, PASSWORD_DEFAULT);
}

function verifyPassword($password, $hash) {
    return password_verify($password, $hash);
}

function rateLimitCheck($identifier) {
    $maxAttempts = 5;
    $lockoutTime = 900; // 15 minutes
    
    $stmt = $pdo->prepare("SELECT attempts, last_attempt FROM login_attempts WHERE identifier = :identifier");
    $stmt->execute(['identifier' => $identifier]);
    $attempt = $stmt->fetch();
    
    if ($attempt) {
        $timeDiff = time() - strtotime($attempt['last_attempt']);
        
        if ($attempt['attempts'] >= $maxAttempts && $timeDiff < $lockoutTime) {
            return [
                'allowed' => false,
                'remaining' => $lockoutTime - $timeDiff
            ];
        }
        
        if ($timeDiff >= $lockoutTime) {
            $stmt = $pdo->prepare("DELETE FROM login_attempts WHERE identifier = :identifier");
            $stmt->execute(['identifier' => $identifier]);
        }
    }
    
    return ['allowed' => true];
}

function recordLoginAttempt($identifier, $success) {
    if ($success) {
        $stmt = $pdo->prepare("DELETE FROM login_attempts WHERE identifier = :identifier");
        $stmt->execute(['identifier' => $identifier]);
    } else {
        $stmt = $pdo->prepare("
            INSERT INTO login_attempts (identifier, attempts, last_attempt) 
            VALUES (:identifier, 1, NOW())
            ON DUPLICATE KEY UPDATE attempts = attempts + 1, last_attempt = NOW()
        ");
        $stmt->execute(['identifier' => $identifier]);
    }
}

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
    $password = $_POST['password'] ?? '';
    
    if (!$email || empty($password)) {
        $error = "Please fill all fields";
    } else {
        $ip = $_SERVER['REMOTE_ADDR'];
        $rateLimit = rateLimitCheck($ip);
        
        if (!$rateLimit['allowed']) {
            $minutes = ceil($rateLimit['remaining'] / 60);
            $error = "Too many attempts. Try again in $minutes minutes.";
        } else {
            $stmt = $pdo->prepare("SELECT id, name, password_hash FROM users WHERE email = :email");
            $stmt->execute(['email' => $email]);
            $user = $stmt->fetch();
            
            if ($user && verifyPassword($password, $user['password_hash'])) {
                $_SESSION['user_id'] = $user['id'];
                $_SESSION['user_name'] = $user['name'];
                recordLoginAttempt($ip, true);
                header('Location: dashboard.php');
                exit();
            } else {
                recordLoginAttempt($ip, false);
                $error = "Invalid credentials";
            }
        }
    }
}
?>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Login</title>
</head>
<body>
    <h1>Login</h1>
    <?php if (isset($error)): ?>
        <p style="color: red;"><?php echo htmlspecialchars($error); ?></p>
    <?php endif; ?>
    
    <form method="POST">
        <input type="email" name="email" placeholder="Email" required><br>
        <input type="password" name="password" placeholder="Password" required><br>
        <button type="submit">Login</button>
    </form>
</body>
</html>
<?php
// register.php - Secure registration with password hashing
require_once 'config.php';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $name = trim($_POST['name'] ?? '');
    $email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
    $password = $_POST['password'] ?? '';
    
    $errors = [];
    
    if (strlen($name) < 2) {
        $errors[] = "Name must be at least 2 characters";
    }
    
    if (!$email) {
        $errors[] = "Valid email required";
    }
    
    if (strlen($password) < 8) {
        $errors[] = "Password must be at least 8 characters";
    }
    
    if (empty($errors)) {
        $passwordHash = hashPassword($password);
        
        try {
            $stmt = $pdo->prepare("INSERT INTO users (name, email, password_hash) VALUES (:name, :email, :hash)");
            $stmt->execute([
                'name' => $name,
                'email' => $email,
                'hash' => $passwordHash
            ]);
            echo "Registration successful!";
        } catch(PDOException $e) {
            if ($e->getCode() == 23000) {
                echo "Email already exists";
            } else {
                echo "Error: " . $e->getMessage();
            }
        }
    } else {
        foreach ($errors as $error) {
            echo "<p style='color: red;'>$error</p>";
        }
    }
}
?>

Exercises

  1. Hash a password and verify it matches the original.
  2. Attempt to login 6 times with wrong credentials and observe the rate limit.
  3. Create a password strength meter in JavaScript.

Homework

  1. Implement password reset functionality with secure tokens.
  2. Write a security audit of your authentication system, identifying 3 potential improvements.

Quiz

  1. What is password hashing?
    • A) Encrypting passwords
    • B) Converting passwords to irreversible strings
    • C) Compressing passwords
    • D) Encoding passwords
    • Answer: B
  2. Which PHP function hashes passwords?
    • A) md5()
    • B) sha1()
    • C) password_hash()
    • D) crypt()
    • Answer: C
  3. Why should passwords never be stored in plain text?
    • A) It uses too much space
    • B) If the database is hacked, passwords are exposed
    • C) It is slow
    • D) It is illegal
    • Answer: B
  4. What does bcrypt do?
    • A) Encrypts data
    • B) Hashes passwords with a salt
    • C) Compresses files
    • D) Validates emails
    • Answer: B
  5. What is rate limiting?
    • A) Limiting database queries
    • B) Restricting login attempts
    • C) Limiting file uploads
    • D) Restricting API calls
    • Answer: B

Lesson 5: HTTPS and Security Headers

Instructor notes

HTTPS is no longer optional. Google ranks HTTPS sites higher and browsers mark HTTP sites as "Not Secure." Show how to check the lock icon in the browser.

Presentation outline

  1. What is HTTPS?
  2. SSL/TLS certificates
  3. Free certificates with Let's Encrypt
  4. Security headers explained
  5. Testing security

Demonstration

  1. Show the difference between HTTP and HTTPS in browser
  2. Enable SSL in cPanel
  3. Add security headers in .htaccess
  4. Test with securityheaders.com

AI prompts

  1. "What security headers should every PHP website have? Show me the .htaccess configuration."
  2. "How do I get a free SSL certificate for my website? Explain Let's Encrypt step by step."
  3. "How can I test my website security? List 5 free online tools."

Starter code

<?php
// .htaccess - No security headers
// Empty or missing .htaccess file
?>

Final code

# .htaccess - Security headers configuration

# Force HTTPS
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

# Security Headers
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "DENY"
Header always set X-XSS-Protection "1; mode=block"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "geolocation=(), microphone=(), camera=()"

# Content Security Policy
Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; img-src 'self' data: https:; font-src 'self' https://fonts.googleapis.com https://fonts.gstatic.com; connect-src 'self'; frame-ancestors 'none';"

# Hide PHP version
Header always unset X-Powered-By

# Prevent access to sensitive files
<FilesMatch "^\.">
    Require all denied
</FilesMatch>

<FilesMatch "\.(env|log|sql|txt)$">
    Require all denied
</FilesMatch>

# Disable directory listing
Options -Indexes

# PHP settings
php_value upload_max_filesize 10M
php_value post_max_size 10M
php_value max_execution_time 60
<?php
// security-check.php - Security testing script
$checks = [];

// Check HTTPS
$checks['HTTPS'] = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || $_SERVER['SERVER_PORT'] == 443;

// Check security headers
$headers = getallheaders();
$checks['X-Content-Type-Options'] = isset($headers['X-Content-Type-Options']);
$checks['X-Frame-Options'] = isset($headers['X-Frame-Options']);
$checks['Content-Security-Policy'] = isset($headers['Content-Security-Policy']);

// Check PHP version
$checks['PHP Version'] = version_compare(PHP_VERSION, '8.0.0', '>=');

// Check error display
$checks['Display Errors Off'] = ini_get('display_errors') == 0;

echo "<h1>Security Check Results</h1>";
foreach ($checks as $name => $status) {
    echo $status ? "<p style='color: green;'>[PASS] $name</p>" : "<p style='color: red;'>[FAIL] $name</p>";
}
?>

Exercises

  1. Install an SSL certificate on your hosting account.
  2. Add security headers to your .htaccess file.
  3. Run your site through securityheaders.com and fix any issues.

Homework

  1. Force HTTPS on your website and set up automatic HTTP to HTTPS redirects.
  2. Test your website with SSLLabs.com. Write a report on the results.

Quiz

  1. What does HTTPS stand for?
    • A) HyperText Transfer Protocol Secure
    • B) High Tech Transfer Protocol
    • C) HyperText Transfer Protocol Standard
    • D) None
    • Answer: A
  2. What is Let's Encrypt?
    • A) A hosting provider
    • B) A free SSL certificate authority
    • C) A domain registrar
    • D) A CDN
    • Answer: B
  3. What does X-Frame-Options: DENY do?
    • A) Blocks all framing
    • B) Allows framing
    • C) Blocks scripts
    • D) Enables HTTPS
    • Answer: A
  4. What is a Content Security Policy?
    • A) A list of allowed resources
    • B) A password policy
    • C) A backup schedule
    • D) A firewall rule
    • Answer: A
  5. What does the lock icon in the browser mean?
    • A) Connection is secure (HTTPS)
    • B) Site is bookmarked
    • C) JavaScript is enabled
    • D) Cookies are enabled
    • Answer: A
Back to Module README