How SQL Injection Works

Injection happens when code pastes input into SQL text: the server cannot tell your characters from the visitor's, so input that closes a string literal adds SQL of its own. Trust no field, cookie or header.

One template, parsed with honest and hostile input
One template, parsed with honest and hostile input

This lookup concatenates its argument into SQL and, like too many applications, runs as root:

A lookup that builds SQL by concatenationSQL
lookup() { sudo mysql -t shop -e "SELECT id, name FROM customers WHERE email = '$1'"; }
lookup "x' OR '1'='1" | tail -n 2
lookup "x' UNION SELECT user, plugin FROM mysql.user -- " | head -n 6
Output
|  8 | Hana Sato     |
+----+---------------+
+------------------+-----------------------+
| id               | name                  |
+------------------+-----------------------+
| root             | caching_sha2_password |
| shop_read        | caching_sha2_password |
| shop_write       | caching_sha2_password |

The always-true condition returned all eight customers (the last is shown), and the UNION listed the server's accounts from a table the query never named; -- comments out the leftover quote. When a page shows no results, blind injection asks yes-or-no questions such as ' AND SUBSTR(@@version,1,1) = '9 and reads the answer from the page or from SLEEP(5) delays. Second-order injection stores a payload that fires when later code concatenates it. Escaping quotes is no cure: WHERE id = 1 OR 1=1 needs none. Keep values out of SQL text.