Web Security
Many web applications contains security vulnerabilities hackers can exploit. Let's take a look at the most commonly used ones, and how we can prevent them.
Lecture material
Fun from xkcd:
Recommended reading
The rest of the recommended readings are primarily articles giving examples of vulnerabilities that have existed. You don't need to read all of them, but look through a few to get a good understanding of what a vulnerability can be in practice.
Hardware related articles
- High Severity BIOS Flaws Affect Numerous Intel Processors
- DDR4 Memory Protections Are Broken Wide Open By New Rowhammer Technique
- Nine WiFi Routers Used by Millions Were Vulnerable to 226 Flaws
OS related articles
Framework/Library related articles
- Example of hijacked npm package
- Sequelize ORM npm library found vulnerable to SQL Injection attacks* Somebody Tried to Hide a Backdoor in a Popular JavaScript npm Package
- Popular 'coa' NPM library hijacked to steal user passwords
- Incident report: npm, Inc. operations incident of July 12, 2018
- Loose .zips sink chips: How poisoned archives can hack your computer
- 5 RCEs in npm for $15,000
App related articles
- The MySpace Worm that Changed the Internet Forever
- TweetDeck wasn't actually hacked, and everyone was silly
- Spyware Company Leaves ‘Terabytes’ of Selfies, Text Messages, and Location Data Exposed Online
- Are You on Tinder? Someone May Be Watching You Swipe
- How Facebook Was Hacked And Why It's A Disaster For Internet Security
- A Security Flaw In Grindr Let Anyone Easily Hijack User Accounts
- Starbucks Devs Leave API Key in GitHub Public Repo
- Cryptocurrency wallet caught sending user passphrases to Google's spellchecker
- Dating Apps Exposed 845GB of Explicit Photos, Chats, and More
- A Twitter app bug was used to match 17 million phone numbers to user accounts
- Undermålig säkerhet i artisten Linda Piras app Workish
- Report: Data Breach in Biometric Security Platform Affecting Millions of Users
- Critical Magento SQL injection flaw could be targeted by hackers soon
- BrewDog Exposes Data of 200,000 Customers and Shareholders
- Click Studios Asks Customers To Stop Tweeting About Its Passwordstate Data Breach
- A New Facebook Bug Exposes Millions of Email Addresses
- A Security Bug in Health App Docket Exposed COVID-19 Vaccine Records
- Zales.com Leaked Customer Data, Just Like Sister Firms Jared and Kay Jewelers Did In 2018
- LINE Pay Leaks Around 133,000 Users' Data To GitHub
- DigitalOcean Says Customer Billing Data 'Exposed' by a Security Flaw
- Experian API Exposed Credit Scores of Most Americans* Hole Blasted In Guntrader: UK Firearms Sales Website's CRM Database Breached, 111K Users' Info Spilled Online
- Apps With 5.8 Million Google Play Downloads Stole Users' Facebook Passwords
- Robinhood Says It Was Hacked and Extorted But Nobody Lost Any Money
- 'Incompetent Developers' Blamed For NZ Patient Privacy Breach of COVID-19 Vaccine Booking Systems
- Unpatched US Government Website Gets Pwned By Pro-Iran Script Kiddie
- The Secret Parameter, LFR, and Potential RCE in NodeJS Apps
App with device related articles
- The 'World's Worst' Smart Padlock Is Even Worse Than Previously Thought
- Hacker Locks Internet-Connected Chastity Cage, Demands Ransom
- Smart alarms left 3 million cars vulnerable to hackers who could turn off motors
- 'Smart' Doorbells For Sale On Amazon, eBay Came Stocked With Security Vulnerabilities
- Passwords In Amazon Echo Dots Live On Even After You Factory-Reset the Device
Web browser related
- 'Scheme Flooding' Technique May Be Used To Deanonymize You
- Race conditions in security dialogs
- Malicious Chrome extensions infect 100,000-plus users, again
- Browser 'Favicons' Can Be Used as Undeletable 'Supercookies' To Track You Online
- CSS Is So Overpowered It Can Deanonymize Facebook Users
- Google Chrome impacted by new Magellan 2.0 vulnerabilities
Various articles
- Cops Are Playing Music While Citizens Are Filming To Trigger Copyright Filters
- Breached Water Plant Employees Used the Same TeamViewer Password and No Firewall
- Thousands of Firefox Users Accidentally Commit Login Cookies On GitHub
- GitHub Fixes a Private-Package-Names Leak and Serious Authorization Bug
Cross-Site Scripting (XSS)
Vulnerability
The vulnerability Cross-Site Scripting (XSS) is when a hacker manage to inject client-side code (HTML, CSS or JavaScript, etc.) on your website that is executed by your users' web browsers when they visit your website. By injecting client-side JavaScript code a hacker can tell the users' web browsers to do some bad things for the user, such as:
- Read information found on the webpage (which might be sensitive information about the user if she has logged in to an account) and then send that information to the hacker's own server in an HTTP request.
- Send HTTP requests to various websites. These websites will think the requests are intentionally sent by the user, so they will happily carry out the requests. This is especially troublesome if the user has logged in to an account on these websites.
Example
In the search form below users should be able to enter search terms, and then get back a list with the search result. To indicate to the user what she has searched for, it also displays the search term. In this example, the search result is never displayed, only what the user searched for. Try entering a search term containing HTML code, for example <b>Hi!</b>
, or <b onclick="alert('Hi!')">Click me!</b>
(client-side JavaScript must be enabled in your web browser for the example to work). Although these examples are not harmful to the user, it shows the potential danger with the vulnerability if a hacker takes advantage of it.
Protection
When data comes from an untrusted source (for example a user or a third-party application you are using) you must escape the HTML code before sending it to the web browser as part of the webpage in the HTTP response. You can escape the data by replacing the following characters in it (the characters that have special meaning in HTML syntax) with their corresponding entity names:
<
can be replaced with<
>
can be replaced with>
"
can be replaced with"
'
can be replaced with'
When the web browser for example sees <
, it will display the <
character.
Example
With proper protection, the search form in the previous example would work like the one shown below (client-side JavaScript must be enabled in your web browser for the example to work).
Express
When you use request.send("Input from a client.")
you are responsible for escaping the input yourself. When you pass input to a view, the view engine usually escapes the data for you. For example, when using Handlebars as your view engine, data you insert into the view using {{expression}}
will escape the HTML code in `expression`. This is usually what you want to happen, but if you for some reason don't want that, you can use {{{expression}}}
, which won't escape the HTML code in `expression`, but then you are responsible to make sure that no XSS vulnerability exists.
Cross-Site Request Forgery (CSRF)
Vulnerability
The vulnerability Cross-Site Request Forgery (CSRF) is about tricking the user's web browser into sending HTTP requests to other websites that is gainable for the hacker. This is especially troublesome if the user is logged in to an account on these websites, for example:
- Alice sends her correct login credentials to bank.com.
- bank.com creates a session containing an identifier for Alice's bank account.
- Alice web browser stores the session's identifier in a cookie.
- Alice somehow ends up at bad-website.com, which contains client-side code from a hacker.
- The bad client-side code tells Alice's web browser to send an HTTP request to bank.com to transfer $1000 to the hacker's own bank account. Since Alice before logged in to bank.com, that cookie with the session identifier is attached to the request, so to bank.com it looks like the HTTP request is intentionally sent by Alice, so bank.com carries out the request.
SQL injection
Vulnerability
The vulnerability SQL injection is when a hacker manages to alter an SQL query the web application sends to the database. This happen when the web application tries to dynamically build the SQL query with some values it has received from the client, and the hacker can alter the query by providing a carefully chosen value.
Imagine a website containing user accounts. The URI /accounts/ACCOUNT_ID
is used to identify the account with the id ACCOUNT_ID
. For example, /accounts/3
identifies the account with the id 3
. When a GET request for this URI is received, the web application needs to send a query like SELECT * FROM accounts WHERE id = ACCOUNT_ID
to the database. If that query is dynamically generated like this:
app.get("/accounts/:ACCOUNT_ID", function(request, response){
const ACCOUNT_ID = request.params.ACCOUNT_ID
const query = "SELECT * FROM accounts WHERE id = "+ACCOUNT_ID
// Send query to DB.
})
Then the hacker can use a clever ACCOUNT_ID
to alter the query sent to the database. If an expected account id like 3
is used, then the query sent to the database will be SELECT * FROM accounts WHERE id = 3
, but if the hacker uses an account id like -1 OR username = 'Alice'
, then the query sent to the database will be SELECT * FROM accounts WHERE id = -1 OR username = 'Alice'
, which the hacker can use to to retrieve Alice's account. This might not be a big deal, but if the hacker would use the account id -1 OR password = 'abc123'
, he would get back the first user to have the password abc123
, and can then simply login to that account (assuming passwords aren't hashed).
Example
Below you can change the URI and see how the query to the database changes (requires client-side JavaScript to be enabled).
GET /accounts/
SELECT * FROM accounts WHERE id =
Protection
Don't use input from the client (a query string parameter, a cookie, the body of the request, a dynamic URI parameter, etc.) to dynamically generate SQL queries sent to the database. Instead, use placeholders for dynamic values in the query, and pass the values separately to the database.
Express and SQLite 3
app.get("/accounts/:ACCOUNT_ID", function(request, response){
const ACCOUNT_ID = request.params.ACCOUNT_ID
const query = "SELECT * FROM accounts WHERE id = ?"
const values = [ACCOUNT_ID]
db.get(query, values, function(error, account){
// ...
})
})