Prevent Cross-Site Scripting and Injection Attacks
After completing this unit, you’ll be able to:
- Explain why your organization may be vulnerable to external attacks.
- Identify cross-site scripting (XSS) attacks.
- Describe how to block injection attacks.
What Are External Attacks?
External attacks occur when someone outside your organization’s systems manages to gain entry in order to inflict damage. There are many kinds of external attacks, ranging from full-on social engineering attacks to injection and scripting attacks. Regardless, as with all things security, it’s up to you to make sure you’re always on guard. Remember, many security breaches at organizations are due to self-inflicted errors. Luckily, these are the ones developers have the most control over.
Let’s start by looking at one of the most common external attacks: cross-site scripting (XSS). While the Open Web Application Security Project (OWASP) team lists XSS as the seventh most risky attack, it continues to be one of the most common issues identified by the HackerOne Bug Bounty team.
What Are XSS Attacks?
The Three Types of XSS Attacks
There are three kinds of XSS attacks: stored XSS attacks, reflected XSS attacks, and document object model (DOM)-based XSS attacks. Learning which one you’re dealing with is crucial to understanding how to protect your code from these vulnerabilities.
Stored XSS attacks occur when malicious input is permanently stored on a server and later returned to users who browse the site normally, such as with a message board post or data in a user profile.
Reflected XSS attacks occur when malicious input is sent to a server and reflected back to the user on the response page. The attacker convinces the user to visit a link that contains the malicious input, such as this one:
Document Object Model (DOM)-based XSS attacks occur as a result of modifying the DOM in the user’s browser. The DOM is the interface that allows programs to dynamically update a website’s content, structure, and style. With a DOM-based XSS attack, the web page isn’t changed, but its client-side code executes in a malicious way due to the DOM modifications. In this case, the web application’s server or database is never involved. Many security products can’t catch this kind of attack if the malicious input does not reach the server.
Mitigating XSS Attacks
XSS is caused by weak separation between code context (the actual commands and variables used in a program) and user data (the input from a user). To defend against it, you have to strengthen the barrier between these two components. Use one of two basic techniques to do this: input filtering and output encoding.
Input filtering is based on the idea that malicious attacks are best caught at the point of user input. Using an allowlist is the most secure method of input filtering, since the developer only needs to know expected input values. Allowlisting only permits characters or words from a known list of entries. For example, if users enter anything besides numbers in a phone number field, the application presents an error.
Injection Attacks: Why They’re So Dangerous
Injection is the most common type of attack on the web today. Injection attacks occur when untrusted user input is interpreted as legitimate commands to be executed instead of data to be processed.
It is essential to guard against leaving your work open to injection attacks when developing code for your organization. While there are many types of injection attacks, including Lightweight Directory Access Protocol (LDAP) injection, operating system (OS) command injection, and Extensible Markup Language (XML) Path (XPath) injection, we focus on SQL injection.
How SQL Injection Attacks Work
SQL is a standard language for storing, manipulating, and retrieving data in databases. SQL query statements are constructed using string (or alphanumeric) variables. In this example, they concatenate (or link) with the name field variable from a comment form.
Var sqlquery = "select * from users where name = '" +name+ "'";
This name variable statement supplies the necessary information so that the SQL query will look up the right person. In a normal scenario, after collecting the name from the comment form, the expected SQL query would be:
Select * from users where name = 'Bob'
Attackers know that if user input data is not properly separated from code instructions, they can “inject” SQL statements in place of legitimate user input.
For example, an attacker starts by crafting a SQL statement to place in the name field:
idontexist' OR 1=1--
When that code travels to the web server and into the database, it matches perfectly with the current SQL quotation marks:
Select * from users where name = 'idontexist' OR 1=1--'
Now, instead of the database returning the row for a single user, it returns the rows for the entire database. In an ecommerce situation, this means exposing all customers’ personal data, including credit card numbers.
Depending on how much customer data you store at your organization, an attack of this nature could be catastrophic.
How to Prevent Injection Attacks
As a developer, you're responsible for ensuring attackers can't steal user information or put business information at risk. How can you stop them? For Java or .NET developers (a software framework developed by Microsoft that runs primarily on Microsoft Windows), both frameworks come with built-in functions that can help sanitize user data. Sanitization is the process of ensuring that the user data conforms to the requirements of the subsystem to which it is being passed. Sanitation typically occurs by removing or replacing unwanted characters.
In Java, PreparedStatement enforces the user input data type. This means that the input is restricted to a data type you specify:
String selectStatement = “SELECT * FROM User WHERE userId =?
PreparedStatement prepStmt = con.prepareStatemen(selectStatement);
ResultSet rs = prepStmt.executeQuery();
In .NET, ParameterizedStatements instead of PreparedStatements enforces the user input data type:
Using (SqlConnection connection = new SqlConnection (connectionString))
DataSet userDataset = new DataSet ();
SQLDataAdapter myDataAdaper = new SQLDataAdapter (
“SELECT au_lname, au_fname FROM Authors WHERE au_id = @au_id”,
myCommand.SelectCommand.Parameters.Add (“au_id”, SqlDbType.VarChar, 11);
myCommand.SelectCommand.Parameters [“au_id”]. Value = SSN.Text
Dynamic SQL statements are built and executed at runtime based on input parameters passed. Dynamic SQL can open up the door to SQL injection, which can lead to data corruption and the leaking of sensitive data. You can avoid dynamic SQL statements by using stored procedures. When passing user input from a web application to stored procedures, you can only pass the information through PreparedStatements or ParameterizedStatements.
Escaping is a method that instructs a computer to do something special with the text you supply or to ignore the special function of a character. Use a special escape marker to tell the computer that it should expect an escape character. Be especially careful when escaping special characters—use the specific escape syntax for that interpreter.
Developers can use escaping routines to prevent the database from mistaking user input for SQL code. Positive or allowlist input validation with appropriate canonicalization (conversion of data that has more than one possible representation into a standard, normal, or canonical form) can protect against injection, but it is not foolproof. Make sure to use an automated testing tool to catch anything you might miss.
To become even more skilled at preventing injection, head over to the Develop Secure Web Apps trail, which covers all these vulnerabilities in depth, including Salesforce Object Query Language (SOQL) and Apex vulnerabilities.