How to do it: SQL Injection

Code-first cards that explain unsafe query building, how logic gets distorted, and how prepared statements shut the door.

Database safety Rendered preview Defense first
Safe learning mode: this page avoids harmful payloads and stays focused on secure coding, query separation, and least-privilege design.

Card 1 — the safe query pattern

Start by showing how application data should stay separate from the SQL instruction itself.

Preview + Code
safe query preview
SELECT * FROM users WHERE email = ? parameter: user@example.com code and input remain separate

Prepared statement example

This is the pattern to teach first because it prevents input from changing query structure.

// Node.js example
const sql = "SELECT * FROM users WHERE email = ?";
db.execute(sql, [email]);

// The value is bound safely instead of concatenated.

Card 2 — unsafe concatenation risk

Show the conceptual bug: when raw input is glued into a query string, logic can be reshaped.

Preview + Code
risky query preview
query = base_sql + raw_input unexpected operators change the result lesson: never trust raw form input inside SQL text

Bad pattern to avoid

Use this only to explain the bug, then move immediately to the fix.

// Unsafe pattern
const sql = "SELECT * FROM users WHERE email = '" + email + "'";

// Problem:
// raw input is being merged into the command text.

Card 3 — tools defenders use to find it

Focus on scanning and review tools that help teams detect risky database code during development.

Preview + Code
Static analysis
Flags concatenated SQL strings in source code.
Code review and SAST platforms catch risky query assembly early.
WAF and database monitoring
Spot suspicious query patterns in production.
detect early, fix early

PHP prepared statement example

Use a second language example so the lesson feels practical.

// PHP PDO example
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id");
$stmt->execute([':id' => $id]);
$result = $stmt->fetchAll();

// The query plan stays fixed.

Card 4 — reduce blast radius

Finish with least privilege, validation, logging, and safer database roles so one bug cannot become a disaster.

Preview + Code
Least privilege
App account can only read or write what it truly needs.
Validation
Input shape is checked before it reaches the database layer.
Overpowered DB roles
Turn one coding error into a bigger incident.

Defense checklist

Close the page with the protections that matter most in real applications.

# Recommended database safeguards
prepared_statements = "required"
input_validation = "enabled"
db_role = "least privilege"
query_logging = "enabled"
security_testing = "in CI"

# Best outcome: secure queries by design, not by luck.