Skip to main content

Security

Comprehensive security architecture and best practices for AsaHome Cloud.

Multi-Layer Security

AsaHome Cloud implements defense-in-depth with multiple security layers:

Network Layer

TLS/SSL Encryption

All traffic is encrypted using TLS 1.3:

# Nginx SSL configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;

HSTS (HTTP Strict Transport Security)

Forces browsers to use HTTPS:

add_header Strict-Transport-Security "max-age=63072000" always;

WebSocket Security

WebSocket connections use WSS (WebSocket Secure):

wss://cloud.asahome.io/tunnel

Transport Layer

Security Headers

Nginx injects security headers on all responses:

# Prevent clickjacking
add_header X-Frame-Options "SAMEORIGIN" always;

# Prevent MIME sniffing
add_header X-Content-Type-Options "nosniff" always;

# XSS Protection
add_header X-XSS-Protection "1; mode=block" always;

# Content Security Policy
add_header Content-Security-Policy "default-src 'self'; script-src 'self'" always;

# Referrer Policy
add_header Referrer-Policy "strict-origin-when-cross-origin" always;

Rate Limiting

Protection against brute force and DDoS:

Nginx Level

# Limit requests per IP
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
limit_req zone=api burst=20 nodelay;

# Limit connections per IP
limit_conn_zone $binary_remote_addr zone=conn:10m;
limit_conn conn 10;

Application Level

// NestJS throttler
@Module({
imports: [
ThrottlerModule.forRoot({
ttl: 60,
limit: 100,
}),
],
})

Per-endpoint throttling:

@Throttle(5, 60) // 5 requests per 60 seconds
@Post('login')
async login() { ... }

CORS Policy

Restrict cross-origin requests:

app.enableCors({
origin: process.env.CORS_ORIGINS?.split(','),
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
credentials: true,
allowedHeaders: ['Authorization', 'Content-Type'],
});

Application Layer

JWT Authentication

Token Security

FeatureImplementation
AlgorithmHS256 (HMAC-SHA256)
Secret256-bit random key
Access Token TTL15 minutes
Refresh Token TTL30 days
Refresh RotationYes (each use)

Token Validation

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(private configService: ConfigService) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: configService.get('JWT_SECRET'),
});
}

async validate(payload: JwtPayload) {
// Validate user exists and is active
const user = await this.usersService.findById(payload.sub);
if (!user || !user.isActive) {
throw new UnauthorizedException();
}
return user;
}
}

Password Security

Hashing

Passwords are hashed using bcrypt with 12 rounds:

const saltRounds = 12;
const hashedPassword = await bcrypt.hash(plainPassword, saltRounds);

Validation

const isValid = await bcrypt.compare(plainPassword, hashedPassword);

Password Requirements

  • Minimum 8 characters
  • At least one uppercase letter
  • At least one lowercase letter
  • At least one number
@IsString()
@MinLength(8)
@Matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/, {
message: 'Password must contain uppercase, lowercase, and number',
})
password: string;

Input Validation

All inputs are validated using class-validator:

export class LoginDto {
@IsEmail()
@Transform(({ value }) => value.toLowerCase().trim())
email: string;

@IsString()
@MinLength(8)
password: string;
}

Global validation pipe:

app.useGlobalPipes(
new ValidationPipe({
whitelist: true, // Strip unknown properties
forbidNonWhitelisted: true, // Error on unknown properties
transform: true, // Auto-transform types
transformOptions: {
enableImplicitConversion: false,
},
}),
);

Role-Based Access Control

User Roles

RolePermissions
userAccess own devices, standard API
adminFull access, user management

Device Roles

RolePermissions
ownerFull control, delete, share
editorControl devices, modify settings
viewerView status only

Implementation

@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('admin')
@Get('admin/users')
async getAllUsers() { ... }

Data Layer

SQL Injection Prevention

TypeORM uses parameterized queries:

// Safe - parameterized
const user = await this.userRepository.findOne({
where: { email: userInput }
});

// Also safe - query builder with parameters
const users = await this.userRepository
.createQueryBuilder('user')
.where('user.email = :email', { email: userInput })
.getMany();

Refresh Token Storage

Refresh tokens are hashed before storage:

import * as crypto from 'crypto';

// Hash token before storing
const hashedToken = crypto
.createHash('sha256')
.update(refreshToken)
.digest('hex');

await this.refreshTokenRepository.save({
userId: user.id,
token: hashedToken,
expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
});

Sensitive Data Handling

Data TypeStorage Method
Passwordsbcrypt hash (12 rounds)
Refresh TokensSHA-256 hash
Access TokensNot stored (stateless)
API KeysSHA-256 hash
Device SecretsAES-256 encrypted

WebSocket Security

Authentication

WebSocket connections require JWT authentication:

@WebSocketGateway()
export class TunnelGateway {
async handleConnection(client: Socket) {
try {
const token = client.handshake.auth?.token;
const payload = this.jwtService.verify(token);

// Attach user to socket
client.data.user = payload;
} catch (error) {
client.emit('error', { message: 'Unauthorized' });
client.disconnect();
}
}
}

Message Validation

All WebSocket messages are validated:

@SubscribeMessage('tunnel:message')
async handleMessage(
@ConnectedSocket() client: Socket,
@MessageBody(new ValidationPipe()) data: TunnelMessageDto,
) {
// Message is validated before processing
}

Rate Limiting

Per-connection rate limiting:

const rateLimiter = new Map<string, number[]>();

function checkRateLimit(clientId: string): boolean {
const now = Date.now();
const windowMs = 1000; // 1 second
const maxRequests = 10;

const timestamps = rateLimiter.get(clientId) || [];
const recent = timestamps.filter(t => now - t < windowMs);

if (recent.length >= maxRequests) {
return false;
}

recent.push(now);
rateLimiter.set(clientId, recent);
return true;
}

Security Checklist

Before Deployment

  • Change default JWT_SECRET
  • Set strong DB_PASSWORD
  • Configure CORS_ORIGINS for your domains
  • Enable HTTPS with valid SSL certificate
  • Review and restrict firewall rules
  • Set up log monitoring

Ongoing

  • Rotate JWT secrets periodically
  • Update dependencies regularly
  • Monitor failed login attempts
  • Review audit logs
  • Run security scans (npm audit)

Environment Variables

Never commit secrets. Use environment variables:

# Generate secure secrets
JWT_SECRET=$(openssl rand -base64 32)
DB_PASSWORD=$(openssl rand -base64 24)
INTERNAL_API_KEYS=$(openssl rand -base64 32)

Incident Response

Suspected Breach

  1. Rotate Secrets: Immediately rotate JWT_SECRET and API keys
  2. Revoke Tokens: Clear all refresh tokens from database
  3. Review Logs: Check audit logs for unauthorized access
  4. Notify Users: If user data is affected, notify users
  5. Post-mortem: Document and fix the vulnerability

Force All Users to Re-authenticate

-- Revoke all refresh tokens
UPDATE refresh_tokens SET is_revoked = true;

Security Headers Reference

HeaderValuePurpose
Strict-Transport-Securitymax-age=63072000Force HTTPS
X-Frame-OptionsSAMEORIGINPrevent clickjacking
X-Content-Type-OptionsnosniffPrevent MIME sniffing
X-XSS-Protection1; mode=blockXSS filter
Content-Security-Policydefault-src 'self'Restrict resources
Referrer-Policystrict-origin-when-cross-originControl referrer

Next Steps