If you take only one thing away from this post, let it be this: Always treat every piece of data provided by the user as untrusted.
This is the first post in a series about the OWASP Top 10; the ten most prevalent vulnerability categories in modern web applications. The list is updated every three years, but Injection has been at or near the top of the list since it originated in 2004.
OWASP defines injection as when “untrusted data is sent to an interpreter as part of a command or query. The attacker’s hostile data can trick the interpreter into executing unintended commands or accessing data without proper authorization.”
Put simply, SQL Injection fools your application’s database into either spilling unauthorized data, or letting the attacker modify the database in unintended ways, or even delete all of the data!
SQL injection is arguably the most relevant form of injection in the context of web applications. (Other forms such as LDAP, XPath, OS Commands, XML parsers and ORM queries will not be covered in this post.)
Nearly any web application will require user input at some point. Most commonly, this happens when:
- A user enters their username and password entered into a login form
- A user enters data into a search field on the site or application
- A user posts a comment in response to another user’s original post
If this user input is not properly sanitized, the deliberately mal-formed user input will run as code. And that’s the essence of an injection attack; injecting malicious code into a vulnerable spot in the application to provoke an unintended, generally malicious outcome.
Always treat user data as untrusted. (Are we sensing a pattern here?)
Mitigating the Consequences
- Client-Side and Server-Side Validation
These should be two separate bullet points because any protections you put in place on the front end can be bypassed using an HTTP Proxy such as Burp Suite or OWASP ZAP that sends your request directly to the server without touching your browser.
Data validation will include the use of regular expressions where appropriate, like in data fields with an expected format (ie: A U.S. phone number, ZIP code or SSN will have a predictable length. An email address will have an appropriate format.) Other fields meant for free-form text don’t benefit from regex, but these fields could disallow SQL keywords such as SELECT, UPDATE, DROP, UNION, and so on. There may be other special characters you want to exclude.
Form fields that expect a single integer value can be validated by requiring the number to be within a valid range (and to be an actual number in the first place).
Allowlists are preferred to Denylists. No denylist can contain every conceivable malicious payload.
- Parameterized Queries, Stored Procedures and Bind Parameters
These approaches constrain what a database query can accomplish, and separate this from the user-provided data.
- Least Privilege
Read and write operations on a database are performed by a “database user” account, not to be confused with the human being interacting with the application. Which operations is this database user allowed to perform? A successful injection attack gives the malicious user the same level of privilege you have given this database user; does that database user necessarily need to be admin? There is an opportunity here to limit the scope of a breach by controlling what that user can touch.
This is not exactly the same thing as validation… Do you want blog commenters to be able to include their own html tags in their comment? Maybe up to a point, but probably not iframes and <script> tags, right? We’ll talk about cross-site scripting attacks (XSS) in a later post.