Wednesday, 21 September, 2022 UTC


Summary

On September 11th, 2022, Snyk published a vulnerability report for the popular CSRF token management csurf npm package. The vulnerability impacts all known versions, which are currently yielding more than 400,000 downloads per week. The vulnerability report is based on the public disclosure by security consultant Adrian Tiron and their write-up on the Fortbridge blog.
Just three days later, on September 13th, the csurf npm package was archived and given a deprecated status by its maintainer, effective with the change in this commit.
If you wish to understand the details involving a CSRF token misconfiguration, continue with the next section. If you only wish to quickly remediate the security concern and understand what to do in response to this CSRF attack disclosure, jump to the section on How to address the csurf CSRF token vulnerability.
What happened to the csurf npm package?
The csurf npm package is a popular library that developers use to mitigate cross-site request forgery (CSRF) vulnerabilities in their Node.js applications. This CSRF library implements a technique called the double submit cookie pattern to prevent such a vulnerability. However, it was found that the package’s implementation of this mitigation technique was ineffective and required implementation of additional web security controls to function properly — and was therefore enabling possible CSRF attacks. In particular, it doesn’t properly validate cookie values for the double submit cookie pattern, which results in attackers being able to spoof the CSRF token. Adding to the problem, it relies on a deprecated encryption algorithm (SHA1) for hashing CSRF tokens.
In response to the security issues raised, the package has now been deprecated and its GitHub repository was archived on September 13th, 2022. The package still receives over 400k weekly downloads, and many projects are still dependent on it.
The GitHub repository displays the following message from the owners:
This npm module is currently deprecated due to the large influx of security vulnerability reports received, most of which are simply exploiting the underlying limitations of CSRF itself. The Express.js project does not have the resources to put into this module, which is largely unnecessary for modern SPA-based applications.
Please instead use an alternative CSRF protection package if you do need one: https://www.npmjs.com/search?q=express%20csrf
How is the csurf package vulnerable to a CSRF attack?

What is cross-site request forgery (CSRF)?

To understand how the csurf package is vulnerable, it helps to first understand what a cross-site request forgery (CSRF) vulnerability is. A CSRF is an attack that tricks a user into performing an action they did not intend to perform. It’s made possible under the assumption that the user is already authenticated, or otherwise authorized to perform an action, with a particular site and is mostly attributed to the use and handling of the cookies mechanism that web browsers implement. A common example used to describe this attack is a malicious actor tricking a bank client into transferring funds to another account. You can read more about CSRF in the following article by the Open Web Application Security Project (OWASP): https://owasp.org/www-community/attacks/csrf.
The following diagram from the Snyk Learn Lesson on CSRF also provides a visual step-by-step flow of a CSRF attack:
Learn more with the Snyk Learn course about CSRF.
A common technique for mitigating the risk of this attack and ensuring that requests received by your server are legitimate, and were intended to be executed by the client, is to use the double submit cookie pattern. This is one technique out of several other CSRF mitigations that can be employed by web application servers.

What is the double submit cookie pattern?

The double submit cookie pattern is a mitigation technique for CSRF attacks in cases where users opt out of implementing a CSRF token with server-side sessions, and can only rely on client-side browser cookies. The application server creates a cryptographically secure random value, such as a SHA256, that is sent to the client browser to set as a cookie. The client then sends that cookie and uses its value in some other transport mechanism, such as a request parameter, HTTP header, or in the request body. The server will validate that the CSRF token matches between both the cookie value and the value in whichever transport mechanism was used. If they do match, the request will proceed as usual. If the values don’t match, a 403 Unauthorized response should be returned by the server. Doing this helps ensure the request is coming from a legitimate source and was an intended action by the user. You can read more about this CSRF mitigation technique in the OWASP CSRF Prevention Cheat Sheet.
Now that you better understand cross-site request forgery, let’s examine how the csurf package is vulnerable to this attack.

What makes csurf vulnerable to CSRF?

The package provides an option to enable the double submit cookie pattern through a configuration option {cookie: true}. While this is set to false by default, much of the documentation and guidance demonstrating the use of this package shows the option set to true. Due to this common suggestion and potential project requirements, it is likely that most developers will set up their projects with the configuration option set to true.
The issue, then, is how the csurf package implements the double submit cookie pattern that is often used by developers. First, it doesn’t use a secure hashing algorithm when creating the random CSRF token for the cookie value. Instead, it uses a deprecated algorithm called SHA1 that is no longer accepted as cryptographically strong due to modern compute capabilities. Ideally it should use a newer standard, like SHA256, to create the CSRF token.
The more severe issue is that the CSRF token isn’t encrypted or signed by the server, thus allowing anyone to spoof the token if the server is susceptible to a man in the middle attack (MitM). It is also possible to bypass the CSRF protection through a technique called Cookie Tossing, in which it is possible to inject new cookies with higher priority. Such cases are also made possible due to cross-site scripting (XSS) vulnerabilities in the application that enable attackers to inject their own cookies to the browser of a victim for the CSRF token.
How to address the csurf CSRF token vulnerability
By this point you’re wondering what you should do about this issue. First, you could potentially continue using the csurf module in your projects with better configuration options or implementing additional mitigation options. Some of those options are:
  • Validating request origin
  • Adding a cookie prefix
  • Using server sessions

Validating request origin

Since cross-site request forgery attacks originate from a separate domain from your own, you can check the origin of incoming requests to determine whether the request is legitimate before processing it. This involves inspecting the request headers for the origin source and/or origin target. When inspecting the origin target, if your server is directly accessed by users, you can parse the request’s hostname and port from the URL to determine the validity of the request. However, in some cases your application server is behind a proxy which makes this a bit more difficult (but not impossible). You can learn more about this mitigation option in the OWASP CSRF prevention cheat sheet.

Adding a cookie prefix

Cross-site request forgery relies heavily on cookies, and is therefore an area we can focus on to implement preventative security measures. A measure that could be implemented for this purpose is the use of a __Host- prefix for the CSRF token cookie name. Adding this into your Node.js Express project as part of the configuration signals browsers to enable additional security protections around the CSRF token cookie. The OWASP CSRF prevention cheat sheet shares more on this “defense in depth” technique. The code sample below shows how you can configure this through the csurf npm package in a Node.js Express application:
app.use(csrf({
    cookie: {
        key: ‘__Host-token’,
        path: ‘/’,
        secure: true
    }
});
Note: One of the requirements for a cookie with the __Host- prefix to be set properly in the browser is that your application must be accessed over HTTPS. If not, the cookie will not be set, and will be invalid.

Using server sessions

If you’re continuing to use Express.js and the csurf npm package, consider using the session-based approach in place of the double submit cookie approach. The following steps show how you can update your project from using the double submit cookie pattern to the session-based approach in a Node.js Express application.
  1. Install the express-session package: npm install express-session.
  2. Initialize the express-session package: const session = require(‘express-session’);.
  3. Set up the express-session package: 
const app = express()
app.use(session({
    secret: ‘<load secure secret from environment or storage solution>’,
    cookie: {
        path: ‘/’,
        secure: true,
        httpOnly: true,
        sameSite: ‘strict’
    },
    saveUninitialized: false,
    resave: false
}));
  1. Set up the csurf package:
const csrf = require(‘csurf’);
const csrfProtection = csrf();
  1. Set the csrfToken in any pages that can be susceptible to CSRF attacks:
app.get(‘/form, csrfProtection, (req, res) => {
    res.render(‘index.ejs’, { csrfToken: req.csrfToken() });
});
Note: This example uses ejs as the view template engine to render HTML pages server side, and therefore can inject the token where needed within the view.
  1. Add the csrfToken as a hidden field in your views:
<form method="POST" action="/process">
    <p>Title: <input type="text" name="title" /></p>
    <p>Name: <input type="text" name="name" /></p>
    <p><input type="submit" value="Submit" /></p>
    <input type="hidden" name="_csrf" value="<%= csrfToken %>" /
</form>
  1. Set up the route that accepts your view requests with csrfProtection:
app.post(‘/process’, csrfProtection, (req, res) => {
	// your route code goes here
});
Now you’re set to be protected from CSRF attacks with the csurf session-based approach instead of the double submit cookie approach. The big difference in using the double submit cookie pattern in the csurf package is that you don’t set the cookie configuration option to true and you add the express-session package.
Consider other frameworks
The configuration and mitigation options outlined above are helpful, and they will protect you from CSRF attacks as described. But the csurf npm package is deprecated and no longer maintained, so it’s best to move on to something else. In this case, you should consider using a framework different than Express.js, such as fastify. Fastify is a fast and low overhead web framework for Node.js and has its own CSRF package. You can read more about using fastify in this getting started guide. Once you have a project set up with fastify, you can add their csrf-protection package to protect it from CSRF attacks. You can learn how to implement the package in your project through the documentation found in its GitHub repository.
Prioritize open source security
Given how widespread a vulnerability like this can become, it’s always wise to consider the securing of your open source packages as an integral part of your SDLC. The csurf npm package library was popular, but even with many eyes on it due to widespread use, a vulnerability still crept in.
Identifying and remediating vulnerabilities early is the best way to save development hours and speed up deployment. Snyk Open Source makes it easy to find and fix vulnerabilities in open source dependencies.
Use open source securely with Snyk
Find and fix vulnerabilities in all of your npm dependencies.
Sign up for free