-
Notifications
You must be signed in to change notification settings - Fork 0
Quick Start Guide
Azizul Hakim edited this page Nov 4, 2024
·
7 revisions
After installing nestjs-xsecurity, follow these steps to implement security in your NestJS application.
You can configure the XSecurity module using one of two approaches:
The async configuration approach is recommended as it provides better flexibility and integration with NestJS's configuration system:
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { XSecurityModule } from 'nestjs-xsecurity';
@Module({
imports: [
ConfigModule.forRoot(),
XSecurityModule.registerAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
enabled: config.get('XSECURITY_ENABLED', true),
secret: config.get('XSECURITY_SECRET'),
rateLimit: {
enabled: config.get('XSECURITY_RATE_LIMIT_ENABLED', true),
maxAttempts: config.get('XSECURITY_MAX_ATTEMPTS', 5),
decayMinutes: config.get('XSECURITY_DECAY_MINUTES', 1),
storeLimit: config.get('XSECURITY_RATE_LIMIT_STORE_LIMIT', 10000)
},
exclude: ['/health', '/metrics', '/api/docs/*'],
}),
}),
],
})
export class AppModule {}
For simpler applications, you can use static configuration:
import { Module } from '@nestjs/common';
import { XSecurityModule } from 'nestjs-xsecurity';
@Module({
imports: [
XSecurityModule.register({
enabled: true,
secret: process.env.XSECURITY_SECRET,
rateLimit: {
enabled: true
maxAttempts: 5,
decayMinutes: 1,
storeLimit: 100
},
exclude: ['/health'],
}),
],
})
export class AppModule {}
For better organization, especially in larger applications, you can separate the configuration logic into a dedicated file:
// xsecurity.config.ts
import { ConfigService } from '@nestjs/config';
import { XSecurityConfig } from 'nestjs-xsecurity';
export class XSecurityConfig {
constructor(private readonly configService: ConfigService) {}
configureOptions(): XSecurityConfig {
return {
enabled: this.configService.get<boolean>('XSECURITY_ENABLED') ?? true,
secret: this.configService.get<string>('XSECURITY_SECRET'),
token: {
headerName: 'X-SECURITY-TOKEN',
},
rateLimit: {
enabled: config.get('XSECURITY_RATE_LIMIT_ENABLED', true),
maxAttempts: 3,
decayMinutes: 1,
storeLimit: config.get('XSECURITY_RATE_LIMIT_STORE_LIMIT', 10000)
},
exclude: ['/health'],
};
}
}
export default (configService: ConfigService) =>
new XSecurityConfig(configService).configureOptions();
Then use it in your app module:
// app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { XSecurityModule } from 'nestjs-xsecurity';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import xsecurityConfig from './xsecurity.config';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
envFilePath: '.env',
cache: true,
}),
XSecurityModule.registerAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (configService: ConfigService) =>
xsecurityConfig(configService),
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
- Review Configuration Options for detailed settings
Here are implementations for generating security tokens in various languages:
import crypto from 'crypto';
function generateXsecurityToken(secretKey: string, expirySeconds = 300): string {
const expiryTimestamp = Math.floor(Date.now() / 1000) + expirySeconds;
const randomBytes = crypto.randomBytes(16).toString('hex'); // Add randomness
const payload = {
expiry: expiryTimestamp,
nonce: randomBytes,
iat: Date.now()
};
const token = Buffer.from(JSON.stringify(payload)).toString('base64');
const signature = crypto
.createHmac('sha256', secretKey)
.update(token)
.digest('hex');
return `${token}.${signature}`;
}
// Usage
const token = generateXSecurityToken('your-secret-key');
import time
import json
import hmac
import base64
import secrets
import hashlib
from typing import Optional
def generate_xsecurity_token(secret_key: str, expiry_seconds: int = 300) -> str:
"""
Generate a secure token with expiry and nonce.
Args:
secret_key (str): Secret key for signing
expiry_seconds (int): Token validity duration in seconds
Returns:
str: Generated security token
"""
expiry_timestamp = int(time.time()) + expiry_seconds
random_bytes = secrets.token_hex(16) # 16 bytes = 32 hex chars
payload = {
"expiry": expiry_timestamp,
"nonce": random_bytes,
"iat": int(time.time() * 1000) # milliseconds
}
# Convert payload to base64
token = base64.b64encode(
json.dumps(payload).encode('utf-8')
).decode('utf-8')
# Generate signature
signature = hmac.new(
secret_key.encode('utf-8'),
token.encode('utf-8'),
hashlib.sha256
).hexdigest()
return f"{token}.{signature}"
import 'dart:convert';
import 'dart:math';
import 'package:crypto/crypto.dart';
class XSecurityToken {
static String generate(String secretKey, {int expirySeconds = 300}) {
final expiryTimestamp = (DateTime.now().millisecondsSinceEpoch ~/ 1000) + expirySeconds;
// Generate random bytes for nonce
final random = Random.secure();
final randomBytes = List<int>.generate(16, (i) => random.nextInt(256));
final nonce = randomBytes.map((byte) => byte.toRadixString(16).padLeft(2, '0')).join();
final payload = {
'expiry': expiryTimestamp,
'nonce': nonce,
'iat': DateTime.now().millisecondsSinceEpoch
};
// Convert payload to base64
final token = base64Encode(utf8.encode(jsonEncode(payload)));
// Generate signature
final hmacSha256 = Hmac(sha256, utf8.encode(secretKey));
final signature = hmacSha256.convert(utf8.encode(token)).toString();
return '$token.$signature';
}
}
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.time.Instant;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.databind.ObjectMapper;
public class XSecurityToken {
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
public static String generate(String secretKey, int expirySeconds)
throws NoSuchAlgorithmException, InvalidKeyException {
long expiryTimestamp = Instant.now().getEpochSecond() + expirySeconds;
// Generate random bytes for nonce
byte[] randomBytes = new byte[16];
SECURE_RANDOM.nextBytes(randomBytes);
String nonce = bytesToHex(randomBytes);
// Create payload
Map<String, Object> payload = new HashMap<>();
payload.put("expiry", expiryTimestamp);
payload.put("nonce", nonce);
payload.put("iat", System.currentTimeMillis());
try {
// Convert payload to base64
String jsonPayload = OBJECT_MAPPER.writeValueAsString(payload);
String token = Base64.getEncoder()
.encodeToString(jsonPayload.getBytes(StandardCharsets.UTF_8));
// Generate signature
Mac hmac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKeySpec = new SecretKeySpec(
secretKey.getBytes(StandardCharsets.UTF_8),
"HmacSHA256"
);
hmac.init(secretKeySpec);
byte[] signatureBytes = hmac.doFinal(token.getBytes(StandardCharsets.UTF_8));
String signature = bytesToHex(signatureBytes);
return token + "." + signature;
} catch (Exception e) {
throw new RuntimeException("Failed to generate security token", e);
}
}
private static String bytesToHex(byte[] bytes) {
StringBuilder result = new StringBuilder();
for (byte b : bytes) {
result.append(String.format("%02x", b));
}
return result.toString();
}
}
- Review Configuration Options for detailed settings
- Follow Security Best Practices for security recommendations
- Explore Token Management for advanced token handling
Copyright 2024, @AHS12 All Right Reserved