Cross-Site Request Forgery - Threat To Open Web Applications
Cross-site request forgery (CSRF) is an attack that tricks a user's browser into sending a malicious HTTP request to another website. This malicious HTTP request looks like it was sent by the user, but it actually comes from the attacker.
A cross-site request forgery (CSRF) attempts to execute a change rather than trying to download personal data. Once an attack is executed there is no way for the attacker to directly monitor the result so attackers often execute multiple forgeries.
The attack can come from malicious emails, websites, or blogs, and targets another open website that a user has already logged into. When a basic user is targeted, the goal of the attacker is usually changing a password or transferring currency.
When an administrative user is targeted, a successful CSRF attack can compromise an entire web application.
Cross-Site Request Forgery (CSRF), is an attack type that can allow a hacker to perform dangerous actions on your site through a user's own browser.
This threat is one of the most common vulnerabilities in existence, and it allows attackers to commit all sorts of nefarious acts.
We'll look at how CSRF attacks work, what you can do to protect yourself from them, and how you can defend against them.
Table Of Contents
- How does CSRF work?
- Example of CSRF
- How to prevent CSRF attacks?
- How to implement CSRF token?
- Additional ways to protect your site
How does CSRF work?
CSRF vulnerabilities occur when a web application requires the user to be authenticated and performs a state-changing action without requiring the user's approval.
CSRF attacks have been used to steal personal data and defraud users, but they can also be used for much more serious security issues, such as session hijacking or cross-site scripting.
The attacker gains unauthenticated access to an authenticated user's session cookie by tricking them into visiting a malicious website or clicking on a Disguised link sent via email or instant message (IM).
The attacker then uses this cookie with their own site (the one hosting the malicious code) to gain access to sensitive information stored within it.
If you're not careful about site protection and how you handle authentication tokens in your application, CSRF is one-way attackers will be able to steal them from you and potentially use them later on.
Example of CSRF attack
Before executing a CSRF attack the attacker completely understands the working flow of the application to create the forged request a legitimate one.
Here is a GET request for ordering a product in mybank.com
.
GET https://mybank.com/api/send?beneficiary_account=45047580936&amount=100000
The request can be embedded into an innocent-looking hyperlink:
<a href="https://mybank.com/api/send?beneficiary_account=45047580936&amount=100000"> Get $100 free coupon! </a>
The attacker then sends the forged link to the customers via email to enormous clients. Those who are logged into the account and unintentionally clicks on the link will initiate the transfer.
Now, What if the bank's account uses only the POST requests?
Here the attacker cannot perform a CSRF attack via a <a>
href tag. However the attack can be performed using the auto submit <form>
.
Despite the fact that the user must click a button to submit the form, a script could just as easily automatically submit the form.
<h1> Hurry Up and grab your offer worth $250! </h1>
<form name="auto-submit-form" id="auto-submit-form" action="https://mybank.com/api/send" method="post">
<input type="hidden" name="beneficiary_account" value="45047580936" />
<input type="hidden" name="amount" value="100000" />
</form>
<script type="text/javascript">
window.onload = function() {
setTimeout(function() {
submitForm();
}, 100);
function submitForm() {
document.forms["auto-submit-form"].submit();
}
}
</script>
How to prevent your site from CSRF attacks?
CSRF attacks can be prevented using two methods:
- Using the
sameSite
flag in session cookies - By implementing CSRF tokens
1. Using the sameSite flag in session cookies
It is a new method to prevent CSRF attack and web application security. We can set an attribute called sameSite for every cookie in the website.
Setting SameSite:strict
will prevent session cookies being sent from third party site. Here the cookies will be sent only if the domain is same as the path for which the cookie has been set.
We will set up a route that records a cookie in your browser. Cookie information will be sent from the server to the client browser in this scenario. You can achieve this by using the res
object and cookie as the method, i.e. res.cookie()
as shown below.
app.get('/', (req,res)=>{
//.....Other Code
res.cookie('cookieName', 'cookieValue', { sameSite: 'strict', secure: true})
//.....Other Code
})
The sameSite
attribute has been set to the Set-Cookie
attribute when the server issues a cookie in the response header.
Set-Cookie: SessionId=sYMnfCUrAlmqVVZn9dqevxyFpKZt30NN; SameSite=Strict;
2. By implementing CSRF token
The most robust way to prevent CSRF attack is to include the token within the relevant requests.
"CSRF tokens"(anti-CSRF token) are commonly used to refer to synchronizer tokens. HTML challenge tokens, which are associated with server-side sensitive tasks, are included as part of the HTML form.
The token should be:
- possess significant entropy
- highly unpredictable
- tied to user session
- validated strictly before the relevant excecution
How to implement CSRF token?
CSRF token can be generated in different programming languages, here I have used Node.js to implement CSRF token.
In Node.js, it can be created and validated easily using the middleware csurf
. To implement CSRF token in your website:
- Generate the CSRF token
- Send token with every request from the front-end.
- Validate the token in the back-end and accept the request.
1. Token Generation in the back-end
To generate and validate CSRF token in the backend, we can use csurf
node package. To install it, use the below command:
npm install csurf
In general, the CSRF token can be generated in two different ways:
Option 1: Rendering CSRF token in the form field or meta field
The below code generates the token and place it in the form input field.
// server.js
var cookieParser = require('cookie-parser')
var csrf = require('csurf')
var express = require('express')
// create express app
var app = express()
// we need this because "cookie" is true in csrfProtection
var csrfProtectionMiddleware = csrf({ cookie: true })
app.use(cookieParser())
// Add the CSRF token in rendered in login pug template
app.get('/login', csrfProtection, function (req, res) {
// pass the csrfToken to the view
res.render('login', { csrfToken: req.csrfToken() })
})
// Add the CSRF token in rendered in meta tag of app template
app.get('/app', csrfProtectionMiddleware, function (req, res) {
// pass the csrfToken to the view
res.render('app', { csrfToken: req.csrfToken() })
})
Render the token directly into a hidden form input element in your Pug template as follows:
// login.pug
form(action="/login" method='POST')
//- Add CSRF Token in value attribute
input(type="hidden", name="_csrf" value=`${csrfToken}`)
input(type="text" placeholder="username")
input(type="password" placeholder="password")
button(type="submit") Submit
You can also send the CSRF token via a <meta>
tag instead of form field. This method is used in SPA application.
// app.pug
html
head
meta(name="description" content="Implementing CSRF tokens")
meta(name="csrf-token" content=`${csrfToken}`)
Option 2: Generate token From backend API
In some cases, the base html is rendered from static server or CDN, we can't place the CSRF token inside the html. In that case, we can implement backend route to returns the CSRF token.
Add the /csrf-token
route in the server side to generate csrf
token.
// server.js
var cookieParser = require('cookie-parser')
var csrf = require('csurf')
var express = require('express')
var bodyParser = require('body-parser')
// create express app
var app = express()
// we need this because "cookie" is true in csrfProtection
var csrfProtectionMiddleware = csrf({ cookie: true })
app.use(cookieParser())
var bodyParserMiddleware = bodyParser.urlencoded({ extended: false })
// Route to generate CSRF token.
app.post('/csrf-token', function (req, res) {
res.json({ csrfToken: req.csrfToken() })
})
2. Sending token from the client
Option 1: From form fields
Tokens that are contained within the form fields itself will be sent automatically along with other form fields.
Option 2: Using meta tag
In Single Page Applications (SPA), you can get the token via meta tag.
The CSRF token must be passed to the AJAX request when accessing protected routes. Request headers are typically used for this, since adding a request header can usually be accomplished at a central location easily without modifying the payload.
<html>
<head>
<meta name="description" content="Implementing CSRF tokens"/>
<meta name="csrf-token" content="CIwNZNlR4XbisJF39I8yWnWX9wX4WFoz"/>
</head>
<body>
<div></div>
</body>
</html>
Here is an example of how to get the CSRF token from the page's <meta>
tag and add it in the header of every fetch request:
// Client side
// Read the CSRF token from the <meta> tag
var token = document.querySelector('meta[name="csrf-token"]').getAttribute('content')
// Make a request using the Fetch API
fetch('/payment', {
//includes cookies in the request
credentials: 'same-origin',
headers: {
'CSRF-Token': token // is the csrf token in the header
},
method: 'POST',
body: {
...
}
})
Option 3: Using a AJAX call
Imagine that your React client interacts with a NodeJS and Express backend.
// server.js
// Generate the CSRF token
app.get('/csrf-token', csrfProtection, function (req, res) {
res.json({ csrfToken: req.csrfToken() })
})
Here is an example of a GET endpoint that returns a CSRF token. To obtain the CSRF token, send a GET request to that endpoint.
The X-CSRF-Token is attached to the request headers by using Axios, but you can also use Fetch API.
// Client side
const response = await axios.get('/csrf-token');
axios.defaults.headers.post['X-CSRF-Token'] = response.data.csrfToken;
3. Validating CSRF token in the back-end
We've imported and initialized the csurf module with a cookie.
The csrfProtectionMiddleware
should be included with all routes to ensure seamless token verification before executing a route.
This middleware would start accepting the Anti-CSRF tokens either via header or request body and validates them.
If the CSRF token is matched, it will accept the request and pass it to the next middleware. If it is not matched, it will return error.
// server.js
var cookieParser = require('cookie-parser')
var csrf = require('csurf')
var express = require('express')
var bodyParser = require('body-parser')
// create express app
var app = express()
// we need this because "cookie" is true in csrfProtection
var csrfProtectionMiddleware = csrf({ cookie: true })
app.use(cookieParser())
var bodyParserMiddleware = bodyParser.urlencoded({ extended: false })
// CSRF Token in the body will be validated by csrfProtectionMiddleware
app.post('/payment', bodyParserMiddleware, csrfProtectionMiddleware, function (req, res) {
res.send('Payment has been processed!')
})
Additional ways to protect your site
Protect your site and your users by taking precautions to prevent this type of attack from being successful.
To prevent this type of attack on your site, you should:
- Using SSL - All web traffic should be encrypted with SSL to prevent MITM attacks and eavesdropping.
- Encryption - It encrypts the content of any data that is communicated between a client and a server so that it cannot be read or modified by anyone who may intercept the information in transit.
- Authentication - It authenticates one or both of two parties involved in an interaction;
For instance,
When you log into your bank account online with an HTTP connection, your browser validates the identity of both itself (to prove it's really you) and the bank (to ensure that only authorized computers can access its systems).
Once authentication is complete, encryption kicks in to protect sensitive data from being intercepted or modified during an online session.
Wind-Up
To put it plainly, cross-site request forgery (CSRF) attacks are one of the most dangerous web vulnerabilities facing your site today.
If a malicious website were to exploit this vulnerability, it could potentially gain access to sensitive data that was previously protected by HTTPS and other security measures.
This includes things like credit card information as well as login credentials.
Perhaps even worse than that is how difficult this type of attack can be to defend against because attackers don’t need any special skills or equipment when performing CSRF attacks.
They simply need access to an unprotected and easily exploit page on your site where they can submit their malicious code.
Atatus Real User Monitoring
Atatus is a scalable end-user experience monitoring system that allows you to see which areas of your website are underperforming and affecting your users. Understand the causes of your front-end performance issues with the help of core web vitals and other front-end metrics.
By understanding the complicated frontend performance issues that develop due to slow page loads, route modifications, delayed static assets, poor XMLHttpRequest, JS errors, and more, you can discover and fix poor end-user performance with Real User Monitoring (RUM).
You can get a detailed view of each page-load event to quickly detect and fix front-end performance issues affecting actual users. With filterable data by URL, connection type, device, country, and more, you examine a detailed complete resource waterfall view to see which assets are slowing down your pages.
#1 Solution for Logs, Traces & Metrics
APM
Kubernetes
Logs
Synthetics
RUM
Serverless
Security
More