Safeguard
Security Guides•

SQL injection lab: teach the fix with parameterized queries

How to run a hands-on SQL injection lab that ends with parameterized queries, handles the allow-list case for column names, and warns against escaping.

Safeguard Security Team
3 min read

A good SQL injection lab has learners exploit a vulnerable query, then fix it with a parameterized query, then deal with the one case parameters cannot cover: dynamic table names, column names and sort order. Finish by showing why escaping is a weak fix.

Set the lesson up

Give learners a small search endpoint that builds its query by joining strings. Ask them to read another user's row. Most pairs get there within ten minutes, and the exploit makes the next step feel earned.

State the model in one line: the database should never be able to confuse data with commands.

Step one: the primary fix

The OWASP SQL Injection Prevention Cheat Sheet lists four defenses. The first is prepared statements with parameterized queries. Have learners rewrite the vulnerable function so that user input is passed as a bound parameter and never joined into the query text. Then rerun the exploit and watch it fail.

Ask for a test that encodes the exploit string and asserts that no extra rows come back. A passing test is the proof.

Step two: stored procedures are not magic

The cheat sheet says properly constructed stored procedures can be as effective as prepared statements. It also warns that they can raise risk in some setups, because web apps may need broader database roles to execute them, and that dynamic SQL inside a stored procedure should be avoided. The OWASP Top 10 injection page adds that stored procedures can still be vulnerable if they concatenate user data into queries. Discuss this in the debrief, since many learners assume "stored procedure" means "safe."

Step three: the case parameters cannot cover

Add a requirement: let users sort results by a column they choose. Bind variables do not work for column names. The cheat sheet recommends allow-list input validation here, ideally with values coming from the code and user input mapped to expected names. Let learners implement a small map from a few permitted sort keys to fixed column names, rejecting anything else.

Step four: why not just escape

The cheat sheet strongly discourages escaping all user-supplied input. It is database-specific and described as fragile compared to other defenses, and the authors say they cannot guarantee it prevents all SQL injection. Show a payload that defeats a naive filter, so the point lands with evidence.

Debrief questions

  • Where in our own services is a query built by joining strings?
  • Does our ORM ever let us drop to raw SQL, and who reviews that?
  • Which database role does each service use, and does it need that much power?

Follow-up

Send the three questions to each team lead and ask for a short search of their repositories. Static analysis can help find string-built queries, and the OWASP page recommends SAST, DAST and IAST in the pipeline.

See also writing a lab exercise that teaches.

Quick check

Is input validation enough to stop SQL injection?

Not by itself. Use parameterized queries as the primary defense and keep validation as a secondary layer, with an allow-list for structural parts of a query.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.