SQL Injection Lab
Runs locally in your browserLearn how unsafe SQL differs from parameterized queries
Defensive education lab: queries run only against built-in fake data in an in-memory SQLite database. There is no URL or external target.
Input
One SQL statement with {{value}} exactly once where the user value goes.
Treat this as untrusted input from a form, URL or API request.
Seed data (fake): users(id, email, role) with 5 rows and orders(id, user_id, amount) with 6 rows. Each side uses its own copy.
What SQL Injection Safety Lab does
SQL Injection Safety Lab shows, side by side, what happens when a user-controlled value is pasted into SQL text versus bound as a parameter. The unsafe query highlights which characters of the value were parsed as SQL (closed quotes, added OR conditions, comments, extra clauses), while the parameterized query keeps the SQL text fixed and lists the value separately as data. It is a defensive learning tool, not a vulnerability scanner: there is no URL input and no external target.
How to use
- 1.Load an example or write one SQL statement that contains {{value}} exactly once.
- 2.Type the user-controlled value. The unsafe and parameterized queries and the explanation update as you type.
- 3.Choose Run comparison to execute both queries against fake seed data in an in-memory SQLite sandbox and compare the rows returned or changed.
- 4.Open Secure code to copy a parameterized version for PostgreSQL, MySQL, SQLite or SQL Server in SQL, JavaScript/TypeScript, Python, PHP, Java or Go.
- 5.Reset database restores the seed data after any write.
Binding values and allow-listing identifiers
Values belong in parameters: PostgreSQL uses $1, MySQL and SQLite use ?, SQL Server uses @p1, and drivers add their own conventions such as %s in psycopg or :name in PDO. The lab follows each driver when it generates code. Table names, column names and ORDER BY fields cannot be bound, so the safe path maps the input to a fixed identifier from an allow-list and rejects anything else. The Code checker tab scans pasted code for interpolated, formatted or concatenated SQL and reports each match as a potential SQL injection risk, because it does not trace where values come from.
Sandbox, limits and privacy
Only SQLite runs, inside a Web Worker using a self-hosted WebAssembly build of sql.js. Each side of the comparison uses its own in-memory copy of fake users and orders, so an unsafe write never affects the safe result. One statement runs per side; stacked statements are reported instead of executed. Queries stop after 3 seconds, results show at most 100 rows and 16 columns, templates are limited to 4,000 characters and values to 500. PostgreSQL, MySQL and SQL Server snippets are reference only. Templates, values and code stay in your browser and are never sent to a server or to analytics.
