惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

B
Blog
量子位
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Last Week in AI
Last Week in AI
酷 壳 – CoolShell
酷 壳 – CoolShell
人人都是产品经理
人人都是产品经理
Jina AI
Jina AI
雷峰网
雷峰网
博客园_首页
WordPress大学
WordPress大学
博客园 - 司徒正美
爱范儿
爱范儿
博客园 - 聂微东
IT之家
IT之家
美团技术团队
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 三生石上(FineUI控件)
有赞技术团队
有赞技术团队
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
T
Tailwind CSS Blog
博客园 - Franky
V
V2EX
GbyAI
GbyAI
阮一峰的网络日志
阮一峰的网络日志

NodeJS Security & NodeJS Secure Coding's Blog

Hardening Your npm and pnpm Configs in the Age of Shai-Hulud Argument Injection vulnerability in git-blame@1.4.0 Argument Injection vulnerability in `gits@0.1.8` Command Injection vulnerability in `@fab1o/git@1.4.0` Command Injection vulnerability in `git-contributors` via unsanitized CLI arguments Command Injection vulnerability in `git-q@0.0.3` Command injection vulnerability via unsanitized CLI arguments in touxing/fast-git-clone Command Injection vulnerability in `willitmerge@0.2.1` A Directory Traversal Vulnerability I found in Mastra AI Frameworks MCP Server Mastering NPX: A Cheatsheet for npm and Node.js Power Users Mitigate Supply Chain Security with DevContainers and 1Password for Node.js Local Development The Tale of the Vulnerable MCP Database Server Bad Security Defaults in Mastra AI Frameworks Templates SQL Injection and Bypassing "Read-Only" Mode in Xata's MCP Server Security Advisory for qix npm supply-chain compromise affecting debug and billions of weekly download users How to Mitigate SQL Bypass in MCP Servers Enhancing MCP Server Security: A Guide to Using execFile Argument Injection Vulnerability in ggit How to Bypass Access Control in PostgreSQL in Simple PSQL MCP Server for SQL Injection Command Injection Flaws in ggit: Unveiling a Vulnerability Command Injection Vulnerability in Create MCP Server STDIO Tool Exposes System Monitoring Functions GitHub Kanban MCP Server Command Injection Vulnerability Threatens Developer Workflows Critical Command Injection Flaw in iOS Simulator MCP Server Exposes Development Environments Command Injection Vulnerability Discovered in Codehooks MCP Server: A Critical Security Analysis SSRF Shenanigans in safe-axios: Redirects Open the Backdoor SSRF Vulnerability in safe-axios: Unintended Public Address Classification Bypassing SSRF Safeguards in ssrfcheck: A Case of Incomplete Denylists Don't Be Fooled by Multicast, SSRF Bypass in private-ip Node.js Authentication from Lucia to Better Auth Bypassing SSRF Protection in nossrf: When Your Safeguards Become Loopholes
OWASP Node.js Authentication, Authorization and Cryptogra...
Authorization Practices in Node.js · 2024-01-20 · via NodeJS Security & NodeJS Secure Coding's Blog

Let’s learn about authentication best practices, authorization and cryptograph guidelines for Node.js. Properly implementing authentication is crucial for securing Node.js apps.

This section covers guidelines like proper session management, password hashing, and attack prevention. I previously reviewed OWASP Node.js Best Practices Guide which covers other areas like input validation, injection attacks, cross-site scripting, and more.

In this post, you’ll learn about:

  • Brief introduction to Authentication in Node.js
  • Brief introduction to Authorization in Node.js
  • Brief cryptography guidelines for Node.js

Manage Sessions Securely

Failing to properly manage user sessions can lead to account hijacking. You’ll probably and up working on authentication controls and would need to be familiar with techniques like session cookies, token rotation, and destroying sessions after logout.

Here are some key session authentication tips you want to keep in mind for Node.js security hygiene:

  • Use session cookies. It’s often easier to go for the JWT authentication method and storing them in local storage, however if you’re not sure on the exact security details and requirements then a session cookie and a server-side store is best and most time-tested approach.

  • When using session cookies, set the httpOnly cookie flag. This ensures mitigation against XSS attacks, which would allow attackers to steal session cookies and hijack other user accounts on your app.

  • Generate a long, random session ID token for each user. Rotate the session token on each request if possible, or at least on a regular basis and when the user performs sensitive actions such as changing their password, email, or other account details.

  • Destroy and invalidate sessions on the server after logout. Don’t rely on front-end session removal.

Hash and Salt Passwords

Plaintext passwords are vulnerable to interception, but realistically even more so to data theft due to data breaches and data leak. Learn how bcrypt, scrypt, and other algorithms can securely hash passwords in Node.js.

  • Use bcrypt or scrypt for password hashing. These algorithms are designed to be slow to hinder brute force attacks. In Node.js, you can use the native module via the npm package bcrypt or the built in scrypt Node.js API in the crypto module.

  • Salt each password hash with a unique, random salt value. Salting ensures no two password hashes are identical. If you’re using bcrypt, the salt is automatically generated and stored in the hash, and unique for each password.

  • Compare hashes rather than plain passwords during authentication. Never store unhashed passwords. Even for something as simple as comparing two data variables, you have to take security in account. Use a constant-time string comparison function to prevent timing attacks.

Prevent Brute Force Attacks

Implementing rate limiting, CAPTCHAs, lockouts after failed attempts, and other defenses can prevent account brute forcing.

  • Enforce rate limiting after a number of failed login attempts from an IP address and other key identifiers.

  • Temporarily lockout accounts after repeated failed attempts to access that account. This prevents attackers from brute forcing a single account.

  • The bcrypt algorithm is designed to be slow to hinder brute force attacks, via a configurable work factor. Use a high work factor to slow down attackers.

Proper session management, password hashing, and attack prevention are crucial for securing Node.js authentication. Following OWASP guidelines helps mitigate common account vulnerabilities.

Authorization is another critical aspect for securing access to sensitive resources and functionality in Node.js applications.

Proper authorization prevents unauthorized users from accessing pages or data they should not see. Here I’ll cover several best practices for implementing authorization in Node.js, including role-based access control, maintaining allowlist and denylist routes, and layered authentication checks.

Implement Role-Based Access Control

Assigning users specific roles with predefined permissions provides fine-grained control over access to Node.js application resources and functions.

For example, an admin role may have full access while a guest role can only view certain pages. Popular Node.js frameworks like Fastify (and Express) have a plugin or middleware capability to enable role-based access control.

Here’s a stock example for a route handler that checks if a user has the admin role, and relies on the req.user object to determine the role, which is set by the authentication middleware that precedes this authorization route handler:

function checkRole(role) {

return (req, res, next) => {

if (req.user.role !== role) {

res.status(401);

return res.send('Not allowed');

}

next();

};

}

app.get('/admin', checkRole('admin'), (req, res) => {

// Admin page

});

Defining and assigning roles appropriately restricts access for unauthorized users. A lot of time though, that’s harder than to do in practice for production-grade applications.

I recommend using a third-party authorization service to ease up the process. One such service is Permit.io which is a lightweight authorization library for Node.js that supports role-based access control, allowlist and denylist routes, and more.

Allow-list Allowed Routes and Methods

Denylist and allowlist specific routes helps restrict access to authorized users. For example, you may allowlist paths starting with /app to only allow admin while blocking everything else:

function checkPath(path) {

const allowedPaths = ['/app'];

if (!allowedPaths.find((p) => path.startsWith(p))) {

res.status(401);

return res.send('Not allowed');

}

next();

}

app.get('/api/users', checkPath, (req, res) => {

// Show users

});

Implementing these access control lists prevents access to unauthorized routes. In larger scale applications you’d want to centralize the access control list and use a middleware to check the path against the list.

Layered Authorization Checks

Verifying permissions at multiple points, like middleware and route handlers, prevents unapproved access even if one check is bypassed.

function checkAuth(req, res, next) {

if (!req.user) {

res.redirect('/login');

}

next();

}

app.get('/account', checkAuth, checkRole('user'), (req, res) => {

// Show account

});

Here the checkAuth middleware and checkRole handler together act as layered auth to increase security.

Following authorization best practices like these helps protect access to Node.js apps and their sensitive data. Proper access controls are crucial for security.

Cryptography Guidelines for Node.js

Cryptography provides critical protections for data confidentiality and integrity when implemented properly. This section briefly reviews a high-level security overview for encryption, key management, hashing, and related areas in Node.js.

Properly Use Encryption Algorithms

Choosing the right encryption algorithm and configuring it correctly is essential to prevent data exposure.

Here are some key tips for choosing and configuring encryption algorithms to prevent data exposure, referencing industry standards like NIST and FIPS.

  • NIST Compliance: adhere to algorithms approved by the National Institute of Standards and Technology (NIST). Their guidelines are widely recognized for security and reliability.

  • FIPS 140-3: Prioritize algorithms validated under the Federal Information Processing Standard (FIPS) 140-3, ensuring they meet stringent government security requirements.

  • AES for Confidentiality: Adopt the Advanced Encryption Standard (AES) for symmetric encryption, offering robust protection for sensitive data. AES-256 is widely considered one of the strongest encryption algorithms available today, offering a very high level of protection for data.

  • RSA for Key Exchange: Utilize RSA for secure key exchange and digital signatures in asymmetric encryption, such as when you need to use a public key and a private key to encrypt and decrypt data. RSA is a widely used and trusted algorithm for this purpose.

  • SHA-3 for Hashing: Employ SHA-3 (Secure Hash Algorithm 3) for data integrity verification. SHA-3 is a cryptographic hash function that generates a unique hash value for each input. It’s a secure and reliable algorithm for verifying data integrity.

Whatever you choose, evaluate the strengths and limitations for each algorithm. For example, AES-256 is a strong encryption algorithm but it’s computationally intensive and may not be suitable for all use cases, especially on low-powered devices.

Securely Encrypting Data at Rest in Node.js Apps

Using the built-in Node.js crypto module, you can encrypt data at rest using the AES-256-GCM algorithm. Here’s a simple example:

const crypto = require('crypto');

function encrypt(data) {

const algorithm = 'aes-256-gcm';

const key = crypto.randomBytes(32);

const iv = crypto.randomBytes(16);

// Create the cipher

const cipher = crypto.createCipheriv(algorithm, key, iv);

// Encrypt the data

let encrypted = cipher.update('my secret data', 'utf8', 'hex');

encrypted += cipher.final('hex');

// Add the auth tag

const authTag = cipher.getAuthTag();

// The encrypted data

const encryptedText = `${encrypted}:${authTag}`;

return encryptedText;

}

And for decrypting the data later:

const crypto = require('crypto');

function decrypt(encryptedText) {

// Split the encrypted data and auth tag

const [encrypted, authTag] = encryptedText.split(':');

const algorithm = 'aes-256-gcm';

const key = crypto.randomBytes(32);

const iv = crypto.randomBytes(16);

// Create the decipher

const decipher = crypto.createDecipheriv(algorithm, key, iv);

// Verify the auth tag

decipher.setAuthTag(authTag);

// Decrypt the data

let decrypted = decipher.update(encrypted, 'hex', 'utf8');

decrypted += decipher.final('utf8');

// The decrypted data

const decryptedText = decrypted;

}

Key takeaways from the above Node.js encryption and decryption code examples:

  • AES-256-GCM: Provides strong encryption, authentication, and integrity protection.
  • Strong Key: Use crypto.randomBytes(32) to generate a secure key.
  • Secure Key Storage: Store the key securely (e.g., secrets manager, even though most developers will end up relying on environment variables).
  • Authentication Tag: Use the authentication tag to verify data integrity during decryption.

Additional considerations for even stronger security, consider using a key derivation function (KDF) to derive the encryption key from a password or passphrase, and regularly rotate keys to mitigate the impact of potential compromises.

What is AES-256-GCM ?

The AES (Advanced Encryption Standard) is a symmetric encryption algorithm, meaning the same key is used for both encryption and decryption. It’s widely adopted as a strong and efficient standard for encrypting sensitive data.

This algorithm is available in different key lengths, with AES-256 offering the highest level of security due to its 256-bit key size.

What is GCM (Galois/Counter Mode)?

It’s an authenticated encryption mode of operation that combines encryption and authentication. It provides confidentiality by encrypting data to prevent unauthorized access. It also provides integrity to ensure data hasn’t been tampered with during transmission or storage.

The authenticity verifies the origin of the data. It offers high performance and security, making it a popular choice for modern applications.

As a whole, the AES-256-GCM combines AES-256 encryption with GCM mode. This offers robust protection for sensitive data by combining confidentiality, integrity, and authenticity in a single algorithm. It’s a NIST-approved and widely used in various security-critical domains.

Secure Generation and Storage of Keys

The codecov security incident from April 2021 is a good example of how a compromised key can lead to a data breach.

Compromised cryptography keys completely bypass encryption defenses. Follow these practices for robust key management:

  • Use a key derivation function like PBKDF2 to generate keys from high entropy passphrases.

  • Store keys securely - not in code or configs. Use hardware security modules where possible. Otherwise, use a secure key management system such as AWS KMS or HashiCorp Vault.

  • Enforce key rotation policies and carefully destroy old keys. Rotate keys when employees leave the company or when they are suspected to be compromised.

Use Password Hashing Algorithms

Don’t fall for the oldest trap in the book of storing passwords in plaintext, or encrypting them. Account passwords should be hashed, not encrypted.

Apply strong cryptographically secure password hashing algorithms like bcrypt when storing user passwords to prevent plaintext exposure. Here are some key tips for password hashing:

  • Salt and hash passwords before storing them.

  • Use adaptive hash functions like BCrypt, which are designed to be computationally intensive.

  • Compare hashed passwords using constant-time string comparison functions.

Following cryptography best practices is crucial for building secure Node.js apps. Proper implementation provides powerful protections while mistakes can lead to total failure.