πŸ›‘οΈ How to Secure Your Express.js Server from CSRF Attacks (Code Analysis)

Hello, web developers! πŸ’»

Today, I’d like to talk about a really, really important but easily overlooked security setting when building web applications with Node.js and Express.js. It’s about how to prevent CSRF (Cross-Site Request Forgery) attacks.

Have you ever seen code like the following in your middleware configuration?

// Enable Express csrf protection
app.use(csrf());

// Make csrf token available in templates
app.use((req, res, next) => {
    res.locals.csrftoken = req.csrfToken();
    next();
});

Let’s easily and thoroughly explore how these two blocks of code protect our server and their underlying principles!

πŸ€” Wait, what exactly is a CSRF attack?

Before we dive into the code, let’s briefly understand what CSRF, the enemy we’re trying to stop, is.

CSRF is an attack that tricks a user into performing an action intended by an attacker, without the user’s knowledge or consent.

Let’s look at an example:

  1. User πŸ‘€ is logged into my-bank.com. The browser retains my-bank.com’s authentication information (cookies).
  2. At this point, the user clicks a link evil-site.com included in a malicious email or post. 😈
  3. The evil-site.com page contains hidden code. For example, an image tag or a form that automatically sends a request like my-bank.com/transfer?to=hacker&amount=1000 might be hidden.
  4. The user’s browser sends this request, automatically including my-bank.com’s authentication cookie along with it. πŸͺ
  5. From the bank server’s 🏦 perspective? A request with a legitimate authentication cookie has arrived, so it mistakenly thinks, “Ah, a legitimate user is requesting a transfer!” and sends money to the attacker’s account.

Terrible, right? The user just clicked a link, but without their knowledge, important information can be changed or financial damage can occur. It’s a very dangerous attack.

πŸ” Step 1: Activate the CSRF Shield! app.use(csrf());

Now let’s look at the first piece of code.

// Enable Express csrf protection
app.use(csrf());

This single line of code is like installing a powerful shield on our Express server. When this code is added, the server begins to do the following:

  • Issue a Secret Key (CSRF Token) πŸ”‘: The server generates a unique and unpredictable string, a ‘CSRF token,’ for each user session. This token is like a one-time password.
  • Monitor and Validate Requests 🧐: From now on, the server checks if this secret key (CSRF token) is correctly included in all requests that change state (POST, PUT, DELETE, etc.).
  • If the request lacks the token, or if it doesn’t match the token issued by the server? ➑️ Block! ❌ The server considers the request dangerous, throws an error, and rejects the request.

Now, even if an attacker 😈 secretly sends a request from evil-site.com, the server will block the request because the attacker doesn’t know this secret key (CSRF token). This prevents most CSRF attacks!

πŸ’Œ Step 2: Safely Deliver the Secret Key to the Frontend

Okay, the server is now ready to create and validate the secret key. But one problem remains.

“How do I include this secret key in requests sent normally by the user?” πŸ€”

It’s not enough for only the server to know the secret key. The user side (frontend) also needs to receive this key and send it along with requests. This is the role of the second piece of code.

// Make csrf token available in templates
app.use((req, res, next) => {
    res.locals.csrftoken = req.csrfToken();
    next();
});

This code performs the following operations sequentially for every request:

  1. Call req.csrfToken(): Calls the csrfToken() function that the csrf() middleware created on the request (req) object. This function returns the secret key (CSRF token) for the current session.
  2. Store in res.locals: res.locals is a special pocket for storing data valid only during the processing of that request. The secret key just obtained is stored here under the name `csrftoken`.
  3. Available in Templates: Values stored in res.locals can be used directly like variables in template engines such as EJS, Pug, and Nunjucks. This means the secret key value can be easily embedded when rendering HTML!

✨ Final Step: Use the Secret Key on the Frontend

Now all preparations are complete. In a frontend template (e.g., form.ejs file), you can use the token stored in res.locals as follows:

<form action="/update-profile" method="POST">
  
  <input type="hidden" name="_csrf" value="<%= csrftoken %>">
  
  <div>
    <label for="username">이름:</label>
    <input type="text" id="username" name="username">
  </div>
  
  <button type="submit">ν”„λ‘œν•„ μ—…λ°μ΄νŠΈ</button>
</form>

This way, when the user clicks the ‘Update Profile’ button to submit the form, our secret key (CSRF token) is included in the invisible _csrf field and sent to the server. The server sees this token and determines, “Ah, this is a legitimate request with the secret key I issued!” and safely processes the request. βœ…

In Summary

The two lines of code we looked at today build a perfect CSRF defense system as follows:

  1. app.use(csrf());: Activates the shield that generates CSRF tokens and monitors and validates incoming requests.
  2. app.use((req, res, next) => { … });: Safely delivers the necessary CSRF token to the frontend template.
  3. Frontend: Includes the received token in a hidden field of the form, sending it to the server as a token of “I am a legitimate user.”

Web security cannot be overemphasized. Especially for services handling important user information, CSRF defense is not an option but a necessity. I hope this code helps you make your valuable services even more secure.

Happy coding! πŸš€


Tags: Nodejs, Express, CSRF, Security, WebDev, csurf, Web Security, Node.js


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *