Skip to content

Security ​

Secure your Redis deployment and data.

TLS/SSL Configuration ​

Basic TLS ​

typescript
RedisModule.forRoot({
  clients: {
    host: 'redis.example.com',
    port: 6379,
    tls: {},  // Use default TLS settings
  },
})

With Certificate Verification ​

typescript
import * as fs from 'fs';

RedisModule.forRoot({
  clients: {
    host: 'redis.example.com',
    port: 6379,
    tls: {
      ca: fs.readFileSync('/path/to/ca.crt'),
      cert: fs.readFileSync('/path/to/client.crt'),
      key: fs.readFileSync('/path/to/client.key'),
      rejectUnauthorized: true,
    },
  },
})

Self-Signed Certificates (Dev Only) ​

typescript
// Development only - do not use in production
{
  tls: {
    rejectUnauthorized: false,
  },
}

Redis ACL (Redis 6+) ​

User Configuration ​

# redis.conf or ACL file
user app-user on >secure-password ~cache:* ~lock:* +@read +@write -@admin
user admin-user on >admin-password ~* +@all

Application Connection ​

typescript
RedisModule.forRoot({
  clients: {
    host: 'redis.example.com',
    port: 6379,
    username: 'app-user',
    password: process.env.REDIS_PASSWORD,
  },
})
RoleKeysCommands
Applicationcache:*, lock:*, ratelimit:*read, write
Monitoring*read only
Admin*all

Secrets Management ​

Environment Variables ​

typescript
// Use environment variables
RedisModule.forRoot({
  clients: {
    host: process.env.REDIS_HOST,
    port: parseInt(process.env.REDIS_PORT),
    password: process.env.REDIS_PASSWORD,
  },
})

Secrets Manager ​

typescript
// AWS Secrets Manager example
import { SecretsManager } from '@aws-sdk/client-secrets-manager';

async function getRedisConfig() {
  const sm = new SecretsManager({});
  const secret = await sm.getSecretValue({ SecretId: 'redis-credentials' });
  const { host, port, password } = JSON.parse(secret.SecretString);
  return { host, port, password };
}

// Use in module
RedisModule.forRootAsync({
  useFactory: async () => ({
    clients: await getRedisConfig(),
  }),
})

Data Classification ​

What Can Be Cached ​

Data TypeCache?Notes
Public contentYesProduct info, blog posts
User preferencesYesUI settings, language
Session referencesYesSession ID → user ID mapping
Aggregated metricsYesNon-sensitive statistics

What Should NOT Be Cached ​

Data TypeCache?Reason
PasswordsNeverSecurity risk
API keys/tokensNeverCredential exposure
Credit card numbersNeverPCI compliance
Social Security NumbersNeverPII regulations
Medical recordsNeverHIPAA compliance
Full authentication tokensNeverSession hijacking risk

Borderline Cases ​

Data TypeRecommendationConditions
Email addressesMaybeIf encrypted or hashed
Phone numbersMaybeIf encrypted
NamesMaybeConsider privacy laws
AddressesMaybeIf needed for functionality

PII Handling ​

Hash Sensitive Keys ​

typescript
import { createHash } from 'crypto';

function hashPii(value: string): string {
  return createHash('sha256').update(value).digest('hex').slice(0, 16);
}

// Hash before calling cached method — raw PII never in key
async getUserByEmail(email: string) {
  return this.findByEmailHash(hashPii(email));
}

@Cached({ key: 'user:email:{0}' })
private async findByEmailHash(emailHash: string) {
  // DB query...
}

Encrypt Sensitive Values ​

typescript
import { createCipheriv, createDecipheriv, randomBytes } from 'crypto';

class SecureCache {
  private key = Buffer.from(process.env.ENCRYPTION_KEY, 'hex');

  encrypt(data: string): string {
    const iv = randomBytes(16);
    const cipher = createCipheriv('aes-256-gcm', this.key, iv);
    const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
    const tag = cipher.getAuthTag();
    return Buffer.concat([iv, tag, encrypted]).toString('base64');
  }

  decrypt(data: string): string {
    const buf = Buffer.from(data, 'base64');
    const iv = buf.subarray(0, 16);
    const tag = buf.subarray(16, 32);
    const encrypted = buf.subarray(32);
    const decipher = createDecipheriv('aes-256-gcm', this.key, iv);
    decipher.setAuthTag(tag);
    return decipher.update(encrypted) + decipher.final('utf8');
  }
}

Key Injection Prevention ​

Validate Key Components ​

typescript
function validateKeyComponent(value: string): string {
  // Reject dangerous characters
  if (/[:\s\n\r\0]/.test(value)) {
    throw new Error('Invalid characters in cache key');
  }
  // Limit length
  if (value.length > 100) {
    throw new Error('Key component too long');
  }
  return value;
}

// Usage
@Cached({
  key: (args) => `user:${validateKeyComponent(args.userId)}`,
})

Use Allowlists ​

typescript
const VALID_ENTITY_TYPES = ['user', 'order', 'product'];

function buildKey(entity: string, id: string): string {
  if (!VALID_ENTITY_TYPES.includes(entity)) {
    throw new Error('Invalid entity type');
  }
  return `${entity}:${id}`;
}

Compliance Considerations ​

GDPR ​

  • Implement data deletion (cache eviction)
  • Respect data minimization
  • Document what's cached

PCI-DSS ​

  • Never cache card data
  • Use encryption in transit (TLS)
  • Implement access controls (ACL)

HIPAA ​

  • Encrypt PHI at rest and in transit
  • Implement audit logging
  • Strict access controls

Security Checklist ​

Pre-Production Review

Verify these items before deploying to production:

ItemStatus
TLS enabled for all connections
Strong passwords configured
ACL configured with least privilege
Secrets in environment/secrets manager
No PII in cache keys
Sensitive values encrypted
Key components validated
Network access restricted
Monitoring for anomalies

Next Steps ​

Released under the MIT License.