Maximize Code Security in Your NestJS Applications (Part 2)
Top Secure Code Best Practices for NestJS Developers
In Part 1 of this article, We walk through 4 common security risks and their prevention in the context of NestJS. It is worth noting that although NestJS is used here (as one of my favored API frameworks), those best practices are framework agnostic.
Let’s continue to dive into other common security vulnerabilities.
Injection
As one of the most well-known vulnerabilities, Injection occurs when an attacker manages to execute arbitrary code or commands by injecting them into an app. Injection attacks can take many forms, such as SQL injection, command injection, and expression injection.
Although injection is a well-known risk, it is still occurring frequently. Some recent incidents include the 2017 Equifax data breach affecting 147 million users, the 2018 British airway data breach leaking 380,000 credit card details, and the 2019 Capital One breach exposing 100 million users’ personal information due to SQL injection.
Below is an example of SQL injection
1 | import { Injectable } from '@nestjs/common'; |
In this example, the ClientService
class use the name
parameter to construct an SQL query that is executed against a database. If the name the
parameter isn’t properly sanitized; an attacker could inject malicious code into the query by including special characters. For example, an attacker could send a request with a name
parameter such as '; DROP TABLE client; --
, which would delete the client
table.
SQL injection can be prevented by sanitizing user input and using prepared statements or parameterized queries whenever possible. Here is an improved version of the previous example.
1 | import { Injectable } from '@nestjs/common'; |
The revised ClientService
uses a parameterized query, which helps to ensure that user input is treated as data rather than executable code.
Another common type of Injection attack is OS command injection. It happens when an attacker injects an arbitrary OS command and executes it; the injection can be done via a request header or parameter, etc. Here is an example.
1 | () |
Without proper sanitizing or validating the command
input, the above code opens the door for a malicious command like “rm rf /var/www
”.
To prevent OS command injection, the best way is to replace the OS commands in the app with framework-specific API. Alternatively, we should escape and validate the user input to ensure that only the expected input will pass the validation.
Lack of Resources and Rate limiting
Many clients can call an API at the same time. If the amount of simultaneous requests exceeds the limit, the API will become unresponsive or even crash.
This vulnerability can be triggered by a sudden surge of legitimate requests during peak hours or malicious DDoS attacks. One of the recent DDoS attacks is the 2020 AWS web service attack .
Any API endpoint without rate limiting can be vulnerable when an attacker sends a large number of requests in a short period of time. To prevent it, we can use a rate-limiting middleware in NestJS. You have a few choices available, such as [nestjs/throttler](https://github.com/nestjs/throttler)
or e``[xpress-rate-limit](https://github.com/express-rate-limit/express-rate-limit)``.
In the below example, we use nestjs/throttler
to restrict that maximum of 10 requests from the same IP can be made to a single endpoint in 1 minute. It applies to all the incoming requests for the app.
1 | ({ |
There are other options available to customize the throttling. For example, you can use @SkipThrottle
decorator to disable rate limiting for an endpoint or use @Throttle()
decorator to override the limit
and ttl
set in the global module.
Besides the number of requests, the following limits should also be considered.
- Execution timeout: If a request takes too long to complete, it should be terminated.
- Payload size/Maximum number of data in response, if a request returns a potentially large amount of data
- Maximum allocable memory: excessive memory usage can cause the App to crash.
Identification and Authentification Failure
Identification and authentication failures are vulnerabilities related to applications’ authentication and identification processes.
It can happen when a system or application does not have robust methods in place to verify the identity of users or when the authentication process can be easily bypassed or manipulated.
This vulnerability can come in many different forms. The most common one is known as session hijacking. Here is an example of how this could happen in a NestJS application:
- The attacker intercepts the normal user’s session cookies. These cookies may contain information such as the user’s ID or Role and other identifying information.
1
2
3
4// Cookies content
eyJ1c2VySWQiOjEyMzQ1LCJ1c2VyUm9sZSI6Im5vcm1hbCJ9
// JSON
{"userId":12345,"userRole":"normal"} - The attacker modifies the session cookies to change the user Role and other identifying information to match an administrative account.
- The attacker returns the modified cookies to the server, pretending to be the administrator. The server receives the modified cookies and grants the attacker access to the app with administrative privileges.
The above example is obviously caused by insufficient authentication at the server. One way to prevent this type of attack in NestJS is to implement the authentication using JWT (JSON Web token). NestJS provides a [@nestjs/jwt](https://github.com/nestjs/jwt)
package for JWT manipulation. You can find more details on implementing JWT in your NestJS app here .
You can consider implementing MFA (multi-factor authentication) to tighten the authentication further. The most popular form of MFA is OTP (one-time passcode).
You can create your own OTP service to store the one-time passcode and manage the sending via phone or email. If you don’t want to reinvent the wheel, we can use existing libraries like otplib .
Below are the basic steps to use otplib in NestJS
1 | // install Otplib |
If MFA can’t be implemented, the other options to consider for additional security includes the following:
- security questions
- CAPTCHA
- require strong password
Missing Object level access control
Object-level access control is a security mechanism that can control access to a specific object or resource based on the permissions or roles of the user requesting access.
Below is an example of how the risk might occur in a NestJS application:
1 | import { Injectable } from '@nestjs/common'; |
In this example, thegetFile
method in the AttachmentController
doesn’t have object-level access control in place to ensure that only the attachment owner or users with permissions can access it. An attacker could potentially access any file in the system by guessing attachment Ids.
To prevent the risk, we can verify the user is the owner or has permission to access the object or resource.
1 | () |
Please note that the above is a contrived example based on the assumption that only the attachment file’s owner can access it.
To further reduce the risk, we can also use random values that are difficult to predict as record Ids. This can help to prevent attackers from guessing or enumerating record IDs. Below is an example to use uuid
module to generate a random unique value for the document id.
1 | import { v4 as uuid } from 'uuid'; |
The best practice is to ensure the server-side functions are protected with a role-based authorization guard in your NestJS app.
Final thoughts
It is a common myth that security is the final step in the software development cycle, either in the form of a penetration test or through the use of a static scanning tool. However, this approach is not enough.
Instead, security should be integrated into every development process step, from design to coding and testing. Security considerations should be a key part of the planning and development a software application rather than an afterthought.
In this article, We discuss several common security risks that can impact NestJS applications, there are many others that need to be considered. I hope you found this article helpful in building a secure and resilient app.
If you are interested to learn more, welcome to my NestJS course.
- Title: Maximize Code Security in Your NestJS Applications (Part 2)
- Author: Sunny Sun
- Created at : 2023-01-09 00:00:00
- Updated at : 2024-07-09 21:32:34
- Link: http://coffeethinkcode.com/2023/01/09/maximize-code-security-in-your-nestjs-applications-part-2/
- License: This work is licensed under CC BY-NC-SA 4.0.