Lab14: DVWA Security Levels Explained 2026 — Low, Medium, High & Impossible Complete Guide

Lab14: DVWA Security Levels Explained 2026 — Low, Medium, High & Impossible Complete Guide
🧪 DVWA LAB SERIES
FREE

Part of the DVWA Lab Series — 30 Labs

Lab 14 of 30 · 46.7% complete

DVWA security levels explained 2026 — after completing 13 DVWA labs you have seen the attack techniques. Lab 14 is different: instead of exploiting a vulnerability, you are reading the PHP source code at each security level to understand exactly what changes between Low and Impossible, and why those changes close the vulnerabilities you have been exploiting. This is the lab that transforms you from someone who can run attacks into someone who can explain to developers precisely what code changes would have prevented them. That is the difference between a script-kiddie and a professional penetration tester.

🎯 What You’ll Learn in Lab 14

Understand exactly what each DVWA security level changes in the PHP source code
Use View Source effectively to analyse vulnerability root causes and fixes
Compare Low vs Impossible implementations of SQL injection, XSS, and CSRF defences
Identify which defences are bypassable (Medium) and which are genuinely secure (Impossible)
Write technical recommendations based on source code analysis

⏱️ 40 min · 3 source code analysis exercises

✅ Prerequisites

  • DVWA Labs 1–13 completed (or working knowledge of each vulnerability type)
  • DVWA running locally with PHP source accessible via View Source
  • Basic PHP reading ability (you do not need to write PHP — just read logic)

In Lab 13 you saw a logic flaw in CAPTCHA implementation. Lab 14 zooms out to view the entire DVWA security framework — understanding how defences are layered, why some fail, and what genuine security looks like in code. This knowledge is directly applicable to the remaining 16 DVWA labs and to writing remediation recommendations in real penetration test reports.


What Each Security Level Actually Changes

securityelites.com
DVWA Security Level Comparison — What Changes
LOW
No input validation · No prepared statements · Raw string concatenation in SQL · No output encoding · No CSRF tokens · No lockout · No logging

MEDIUM
mysql_real_escape_string() (bypassable with techniques like UNION) · Basic strip_tags() · Some input length limits · Partial blacklist filtering (easily bypassed) · Still vulnerable to most attacks with minor modifications

HIGH
PDO prepared statements (no SQL injection) · htmlspecialchars() on output (prevents XSS) · CSRF tokens (prevents CSRF) · Server-side reCAPTCHA verification · Stricter input validation · Still some academic bypasses in edge cases

IMPOSSIBLE
PDO + parameterised queries · htmlspecialchars ENT_QUOTES · Anti-CSRF token + session binding · Rate limiting + account lockout + logging · Minimum password length enforcement · All user input treated as untrusted

📸 DVWA security level comparison — Low removes all defences to demonstrate pure vulnerability; Medium adds partial (bypassable) defences; High implements strong defences; Impossible implements security best practices.


SQL Injection — Low vs Impossible Source Comparison

⚡ EXERCISE 1 — DVWA SOURCE CODE ANALYSIS (15 MIN)
Compare SQL Injection Source Code Across All Four Security Levels

⏱️ Time: 15 minutes · DVWA running · navigate to SQL Injection module

SQL INJECTION SOURCE CODE — LEVEL COMPARISON
═══ LOW SECURITY — Pure vulnerability ═══
$id = $_GET[ ‘id’ ];
$query = “SELECT first_name, last_name FROM users WHERE user_id = ‘$id’;”;
// $id goes directly into the query — any SQL injected immediately executes
═══ MEDIUM SECURITY — Partial defence ═══
$id = $_POST[ ‘id’ ];
$id = mysqli_real_escape_string($GLOBALS[“___mysqli_ston”], $id);
$query = “SELECT first_name, last_name FROM users WHERE user_id = $id;”;
// Note: integer ID not quoted — UNION injection still works without quotes
═══ HIGH SECURITY — Strong defence ═══
$id = $_SESSION[‘id’];
$query = “SELECT first_name, last_name FROM users WHERE user_id = ‘$id’ LIMIT 1;”;
// LIMIT 1 prevents some multi-row UNION attacks; id from session not GET/POST
═══ IMPOSSIBLE SECURITY — Best practice ═══
$data = $db->prepare(‘SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;’);
$data->bindParam(‘:id’, $id, PDO::PARAM_INT);
$data->execute();
// PDO prepared statement — $id is NEVER part of the SQL string itself
// Even if $id = “1′ OR ‘1’=’1”, it is treated as a literal value, not SQL

✅ What you just learned: The progression from raw string concatenation (Low) to parameterised queries (Impossible) illustrates the only reliable SQL injection defence. Medium’s mysql_real_escape_string is often cited as a mitigation but it is context-dependent and can be bypassed with integer injection when the field is unquoted. High’s LIMIT 1 helps but the id is still from user-session which in some configurations is user-controlled. Only Impossible’s PDO prepared statement with PARAM_INT type binding provides a complete SQL injection defence — the query structure is fixed at compilation, user input is always a literal value, never executable SQL. This is the exact code you recommend in every SQL injection penetration test finding.

📸 Screenshot all four source code snippets in DVWA and share in #dvwa-labs on Discord.


XSS — How Each Level Tries (and Sometimes Fails) to Filter Output

⚡ EXERCISE 2 — DVWA SOURCE ANALYSIS (12 MIN)
Compare XSS Reflected Source Code and Test Which Payloads Bypass Each Level

⏱️ Time: 12 minutes · DVWA XSS Reflected module · View Source

XSS REFLECTED — LEVEL COMPARISON
═══ LOW — No filtering ═══
$name = $_GET[ ‘name’ ];
echo “

Hello ${name}

“;

// Payload: <script>alert(1)</script> → direct XSS
═══ MEDIUM — Blacklist (bypassable) ═══
$name = str_replace( ‘<script>’, ”, $_GET[ ‘name’ ] );
// Strips <script> but NOT: <SCRIPT> <ScRiPt> <img onerror=…>
// Bypass: <SCRIPT>alert(1)</SCRIPT> or <img src=x onerror=alert(1)>
═══ HIGH — Whitelist approach (harder to bypass) ═══
$name = preg_replace( ‘/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i’, ”, $_GET[ ‘name’ ] );
// Blocks <script> case-insensitively and with characters in between
// Still bypassed with: <img src=x onerror=alert(1)> (no “script” substring)
═══ IMPOSSIBLE — Correct encoding ═══
$name = htmlspecialchars( $_GET[ ‘name’ ], ENT_QUOTES, ‘UTF-8’ );
// All HTML special characters encoded to entities — &lt; &gt; &amp; &quot; &#039;
// <script>alert(1)</script> is OUTPUT as literal text — never executes
// ENT_QUOTES encodes both single and double quotes — prevents attribute injection

✅ What you just learned: The XSS progression reveals why blacklist filtering (Medium and High) is fundamentally weaker than output encoding (Impossible). Blacklists block known bad patterns but attackers find equivalent patterns that achieve the same result without matching the filter. Output encoding transforms the meaning of special characters entirely — a less-than sign becomes the entity &lt; which displays as text but never executes as HTML. htmlspecialchars with ENT_QUOTES and UTF-8 is the single most important function in PHP web security for preventing XSS — recommend it by name in every XSS finding you write.

📸 Screenshot the Medium and Impossible source side by side and share in #dvwa-labs on Discord.


CSRF and Brute Force — Token and Lockout Implementation

⚡ EXERCISE 3 — DVWA BRUTE FORCE SOURCE ANALYSIS (10 MIN)
Compare Brute Force Protection Source Code and Understand Anti-CSRF Token Implementation

⏱️ Time: 10 minutes · DVWA Brute Force module · View Source

BRUTE FORCE PROTECTION — LEVEL COMPARISON
═══ LOW — No protection ═══
$user = $_GET[ ‘username’ ];
$pass = $_GET[ ‘password’ ];
$query = “SELECT * FROM users WHERE user = ‘$user’ AND password = ‘$pass’;”;
// No rate limiting, no lockout, vulnerable to SQLi too
═══ HIGH — Anti-CSRF token ═══
// Token generated per session
$token = md5(uniqid());
$_SESSION[‘session_token’] = $token;
// Verified on each request
if( $_GET[‘user_token’] !== $_SESSION[‘session_token’] ) {
exit; // invalid token = request rejected
}
═══ IMPOSSIBLE — Lockout + logging ═══
// After 3 failed attempts → 15 minute lockout
if($total_failed_login >= 3) {
$html .= “<p>Too many login failures. Account locked for 15 minutes.</p>”;
exit;
}
// Failed attempts logged to database with timestamp and IP
// PDO prepared statements prevent SQL injection bypass

✅ What you just learned: The High security CSRF token implementation — md5(uniqid()) stored server-side and verified per request — is exactly the pattern Lab 15 will teach you to bypass using Burp macros that extract the token before each request. The Impossible lockout after 3 attempts makes even that bypass impractical. Understanding both the attack (Lab 15) and the complete defence (this source code) gives you the complete picture for any brute force finding in a real penetration test report.

📸 Screenshot the Impossible lockout source code and share in #dvwa-labs on Discord. Tag #dvwasecurity2026


Impossible Security Patterns — The Code Standards Worth Memorising

The DVWA Impossible security level consistently implements the same five patterns across all modules. Memorising these makes you faster at writing accurate recommendations in penetration test reports — you can name the exact function and pattern the developer should implement rather than writing generic “validate input” advice.

THE FIVE IMPOSSIBLE SECURITY PATTERNS
# 1. SQL: PDO Prepared Statements with type binding
$data = $db->prepare(‘SELECT * FROM users WHERE id = :id LIMIT 1’);
$data->bindParam(‘:id’, $id, PDO::PARAM_INT);
$data->execute();
# 2. XSS: htmlspecialchars with ENT_QUOTES and charset
echo htmlspecialchars($input, ENT_QUOTES, ‘UTF-8’);
# 3. CSRF: Cryptographic token per session
$token = bin2hex(random_bytes(32)); // or md5(uniqid()) minimum
$_SESSION[‘csrf_token’] = $token;
// Verify: if ($token !== $_SESSION[‘csrf_token’]) die();
# 4. Auth: lockout after N failures + logging
if ($failed_attempts >= 3) { lockout_for(900); log_attempt($ip, $user); }
# 5. CAPTCHA: Server-side API verification
$resp = recaptcha_check_answer($private_key, $_POST[‘g-recaptcha-response’]);
if (!$resp->is_valid) die(“CAPTCHA verification failed”);

🧠 QUICK CHECK — Lab 14

A developer tells you they have fixed SQL injection in their PHP code by adding mysql_real_escape_string() around all user input. Is this a complete fix?



📋 Lab 14 Reference — Impossible Security Quick Reference

SQL injection fixPDO prepared statements with bindParam(:name, $value, PDO::PARAM_INT/STR)
XSS fixhtmlspecialchars($input, ENT_QUOTES, ‘UTF-8’) on ALL output — not selected output
CSRF fixCryptographic token per session, verified on every state-changing request
Brute force fixAccount lockout after 3-5 failures + delay + IP logging + alerting
CAPTCHA fixServer-side API verification only — never trust client-supplied bypass parameters
Medium = bypassableBlacklists, escaping functions, and client-side controls are all bypassable with technique

🏆 Mark Lab 14 as Complete

You now understand what every DVWA security level actually changes in the source code, why Medium defences fail, and what Impossible-level implementation looks like for each vulnerability class. These five secure coding patterns belong in every penetration test finding recommendation you write.


❓ Frequently Asked Questions

What do the DVWA security levels change?
The PHP source code of each module. Low = no defences. Medium = partial/bypassable defences (blacklists, escaping). High = strong defences (prepared statements, CSRF tokens, htmlspecialchars). Impossible = best-practice hardening with PDO, rate limiting, and logging.
What is DVWA Impossible security and can it be bypassed?
Impossible is DVWA’s reference implementation of correctly secured code. PDO prepared statements prevent SQL injection; htmlspecialchars prevents XSS; CSRF tokens prevent CSRF; server-side reCAPTCHA prevents CAPTCHA bypass; rate limiting prevents brute force. No practical attack path using the techniques from earlier labs works against Impossible.
How do I use DVWA View Source effectively?
Attempt exploitation first. Then: open View Source to see why it worked. Switch security levels and compare source changes. For each added line, ask what attack it prevents and whether it can be bypassed. This source comparison builds the code review skill for real-world vulnerability analysis.
Is mysql_real_escape_string a complete SQL injection fix?
No — it is context-dependent and bypassable in specific scenarios. The correct fix is PDO prepared statements with parameterised queries and type binding (PDO::PARAM_INT for integers, PDO::PARAM_STR for strings). User input must never form part of the SQL string structure.
What comes after DVWA Lab 14?
Lab 15: DVWA Brute Force Advanced — Anti-CSRF Token Bypass. Using the token implementation knowledge from Lab 14, Lab 15 teaches a Burp macro that extracts the CSRF token automatically before each login attempt, bypassing the High security brute force protection.
← Previous Lab

Lab 13: DVWA Insecure CAPTCHA 2026

Next Lab →

Lab 15: DVWA Brute Force Advanced — Anti-CSRF Bypass

📚 Further Reading

  • DVWA Insecure CAPTCHA Lab 2026 — Lab 13 demonstrated the logic flaw that the Impossible source code in this lab shows how to fix — server-side API verification replacing client-trusted step parameters.
  • DVWA Labs Hub — All 30 DVWA labs in sequence — with the source code analysis skills from Lab 14, all subsequent labs include a deeper understanding of why each vulnerability exists and how it is fixed.
  • SQL Injection Complete Guide — The complete SQL injection category covering manual techniques, automated tools, and the PDO prepared statement remediation demonstrated in Lab 14’s Impossible source.
  • OWASP DVWA Project Page — The official DVWA project documentation including security level explanations, installation guides for Docker/XAMPP, and the complete list of vulnerability modules in the current release.
  • OWASP SQL Injection Prevention Cheat Sheet — The definitive reference for PDO prepared statement implementation in multiple languages — the practical extension of the Impossible security pattern shown in Lab 14.
ME
Mr Elite
Owner, SecurityElites.com
Lab 14 is the lab I wish had existed when I was learning. For the first year of my practice I could run attacks — I knew how SQL injection worked, I knew how to pop XSS alerts. But when I wrote my first professional penetration test findings, the remediation recommendations were embarrassingly generic. “Use parameterised queries.” “Validate all input.” Technically correct, practically useless to a developer who does not know what parameterised queries look like in their language. DVWA’s View Source feature, combined with the security level comparison, is the fastest way to learn to write specific, actionable code-level recommendations. When I started telling clients exactly what function to call with exactly which parameters — htmlspecialchars($output, ENT_QUOTES, ‘UTF-8’) rather than “escape output” — the quality of my reports changed overnight. So did the feedback.

Leave a Reply

Your email address will not be published. Required fields are marked *