I recently decided I wanted to understand better what Cross-Site Scripting and Cross-Site Request Forgery were, and how they compared to that classic vulnerability, SQL Injection.
I also looked into some ways that sites protect against those attacks.
SQL Injection is a classic vulnerability. It probably dates back almost to punch cards.
Suppose a program uses data from a user in a database query.
For example, the company web site lets users enter a name of an employee, free-form, and the site will search for that employee and display their contact information.
A naive site might build a SQL query as a string using code like this, including whatever the user entered as NAME:
"SELECT * FROM employees WHERE name LIKE '" + NAME + "'"
If NAME is "John Doe", then we get:
SELECT * FROM employees WHERE name LIKE 'John Doe'
which is fine. But suppose someone types this into the NAME field:
John Doe'; DROP TABLE EMPLOYEES;
then the site will end up building this query:
SELECT * FROM employees WHERE name LIKE 'John Doe'; DROP TABLE EMPLOYEES;'
which might delete the whole employee directory. It could instead do something less obvious but even more destructive in the long run.
This is called a SQL Injection attack, because the attacker is able to inject whatever they want into a SQL command that the site then executes.
Cross Site Scripting
For example, maybe an attacker posts a comment on a page that looks to users like:
but what they really put in their comment was:
So, clearly, a site that accepts data uploaded by users, stores it, and then displays it, needs to be careful of what's in that data.
Why is it called Cross-Site Scripting? Because it allows an attacker to run their script on a site they don't control.
Cross Site Request Forgeries
The essence of a CSRF attack is a malicious site making a request to another site, the site under attack, using the current user's permissions.
That last XSS example could also be considered a CSRF attack.
As another, extreme example, suppose a site implemented account deletion by having a logged-in user visit (GET) /delete-my-account. Then all a malicious site would have to do is link to yoursite.com/delete-my-account and if a user who was logged into yoursite.com clicked the link, they'd make the /delete-my-account request and their account would be gone.
In a more sophisticated attack, a malicious site can build a form or make AJAX calls that do a POST or other request to the site under attack when a user visits the malicious site.
Protecting against vulnerabilities
Protections in the server and application
SQL Injection protection
Django's ORM, and most database interfaces I've seen, provide a way to specify parameters to queries directly, rather than having the programmer build the whole query as a string. Then the database API can do whatever is appropriate to protect against malicious content in the parameters.
Django templates apply "escaping" to all embedded content by default. This marks characters that ordinarily would be special to the browser, like "<", so that the browser will just display the "<" instead of interpreting it. That means if content includes "<SCRIPT>...</SCRIPT>", instead of the browser executing the "..." part, the user will just see "<SCRIPT>...</SCRIPT>" on the page.
We obviously can't disable links to other sites - that would break the entire web. So to protect against CSRF, we have to make sure that another site cannot build any request to our site that would actually do anything harmful.
The first level of protection is simply making sure that request methods like GET don't change anything, or display unvalidated data. That blocks the simplest possible attack, where a simple link from another site causes harm when followed.
A malicious site can still easily build a form or make AJAX calls that do a POST or other request to the site under attack, so how do we protect against that?
Django's protection is to always include a user-specific, unguessable string as part of such requests, and reject any such request that doesn't include it. This string is called the CSRF token. Any form on a Django site that does a POST etc has to include it as one of the submitted parameters. Since the malicious site doesn't know the token, it cannot generate a malicious POST request that the Django site will pay any attention to.
Protections in the browser
Modern browsers implement a number of protections against these kinds of attacks.
"But wait", I hear you say. "How can I trust browsers to protect my application, when I have no control over the browser being used?"
I frequently have to remind myself that browser protections are designed to protect the user sitting in front of the browser, who for these attacks, is the victim, not the attacker. The user doesn't want their account hacked on your site any more than you do, and these browser protections help keep the attacker from doing that to the user, and incidentally to your site.
Same-origin security policy
All modern browsers implement a form of Same Origin Policy, which I'll call SOP. In some cases, it prevents a page loaded from one site from accessing resources on other sites, that is, resources that don't have the same origin.
The most important thing about SOP is that AJAX calls are restricted by default. Since an AJAX call can use POST and other data-modifying HTTP requests, and would send along the user's cookies for the target site, an AJAX call could do anything it wanted using the user's permissions on the target site. So browsers don't allow it.
Content Security Policy (CSP)
CSP is a newer mechanism that browsers can use to better protect from these kinds of attacks.
I've barely touched the surface on these topics here. Any web developer ought to have at least a general knowledge of common vulnerabilities, if only to know what areas might require more research on a given project.
A reasonable place to start is Django's Security Overview.
The OWASP Top Ten is a list of ten of the most commonly exploited vulnerabilities, with links to more information about each. The ones I've described here are numbers 1, 3, and 8 on the list, so you can see there are many more to be aware of.