Introduction à la sécurité avec node-redis
L'utilisation de bases de données en mémoire comme Redis offre des performances exceptionnelles, mais nécessite une attention particulière concernant la sécurité des données. Par défaut, les échanges et le stockage s'effectuent en clair. Pour se conformer aux normes de conformité et prévenir les fuites d'informations, le client node-redis propose plusieurs mécanismes pour sécuriser les communications et les données elles-mêmes.
Sécurisation des données en transit : Configuration TLS/SSL
Le chiffrement des communicasions entre le client Node.js et le serveur Redis est indispensable pour éviter les attaques de l'homme du milieu (MITM).
Établissement d'une connexion TLS basique
Voici comment initialiser un client avec le protocole TLS activé :
import { createClient } from 'redis';
const redisConfig = {
socket: {
tls: true,
host: 'secure-redis-instance.example.com',
port: 6379
},
password: 'strongAuthPassword123!'
};
const secureRedisClient = createClient(redisConfig);
Intégration de certificats CA pour la production
Dans un environnement de production, il est crucial de valider la chaîne de certificats en fournissant les fichiers CA, client et clé privée :
import { readFileSync } from 'node:fs';
const tlsOptions = {
tls: true,
ca: readFileSync('./certs/root-ca.pem'),
cert: readFileSync('./certs/client-cert.pem'),
key: readFileSync('./certs/client-key.pem'),
rejectUnauthorized: true
};
const authenticatedClient = createClient({ socket: tlsOptions });
Utilisation de certificats auto-signés pour le développement
Pour les environnements locaux ou de test, la vérification stricte des certificats peut être désactivée :
const devClient = createClient({
socket: {
tls: true,
rejectUnauthorized: false, // À utiliser exclusivement en local
cert: readFileSync('./certs/self-signed.pem')
}
});
Stratégies de chiffrement des données au repos
Bien que Redis ne chiffre pas nativement les données sur le disque ou en mémoire, il est possible d'appliquer un chiffrement de bout en bout au niveau du client.
Chiffrement symétrique côté client
L'implémentation suivante utilise l'algorithme AES-256-GCM pour chiffrer les valeurs avant leur insertion dans Redis :
import crypto from 'node:crypto';
class SecureRedisWrapper {
#redisInstance;
#derivedKey;
#algo = 'aes-256-gcm';
constructor(redisInstance, passphrase) {
this.#redisInstance = redisInstance;
this.#derivedKey = crypto.scryptSync(passphrase, 'secure-salt-value', 32);
}
async storeSecureData(recordKey, plainText) {
const iv = crypto.randomBytes(12);
const cipher = crypto.createCipheriv(this.#algo, this.#derivedKey, iv);
const encryptedBuffer = Buffer.concat([
cipher.update(plainText, 'utf8'),
cipher.final()
]);
const authTag = cipher.getAuthTag();
const payload = {
iv: iv.toString('base64'),
ct: encryptedBuffer.toString('base64'),
tag: authTag.toString('base64')
};
await this.#redisInstance.set(recordKey, JSON.stringify(payload));
}
async retrieveSecureData(recordKey) {
const rawPayload = await this.#redisInstance.get(recordKey);
const { iv, ct, tag } = JSON.parse(rawPayload);
const decipher = crypto.createDecipheriv(
this.#algo,
this.#derivedKey,
Buffer.from(iv, 'base64')
);
decipher.setAuthTag(Buffer.from(tag, 'base64'));
const decryptedBuffer = Buffer.concat([
decipher.update(Buffer.from(ct, 'base64')),
decipher.final()
]);
return decryptedBuffer.toString('utf8');
}
}
Hachage des informations sensibles
Pour les données nécessitant une vérification d'intégrité sans besoin de déchiffrement, l'utilisation de fonctions de hachage est recommandée :
import crypto from 'node:crypto';
async function persistHashedInfo(redisClient, userIdentifier, confidentialData) {
const hashedValue = crypto
.createHash('sha256')
.update(confidentialData)
.digest('hex');
await redisClient.hSet(`profile:${userIdentifier}`, {
secureHash: hashedValue,
lastUpdated: Date.now().toString()
});
}
Authentification avancée et contrôle d'accès
Intégration avec Microsoft Entra ID
Pour les architectures d'entreprise, le package @redis/entraid permet une authentification sans mot de passe via les identités managées :
import { createClient } from '@redis/entraid';
const entraRedisClient = createClient({
socket: {
host: 'enterprise-redis.redis.cache.windows.net',
port: 6380,
tls: true
},
credentials: {
tenantId: '00000000-0000-0000-0000-000000000000',
clientId: '11111111-1111-1111-1111-111111111111',
clientSecret: 'super_secret_app_key'
}
});
Gestion des utilisateurs via ACL
Depuis la version 6.0, Redis supporte les listes de contrôle d'accès (ACL). L'authentification s'effectue directement via l'URL de connexion :
const aclRedisClient = createClient({
url: 'rediss://app_user:complex_password@localhost:6379',
socket: {
tls: true
}
});
Optimisation des performances pour les connexions chiffrées
Mise en place d'un pool de connexions
Le chiffrement TLS introduit une surcharge lors de l'initialisation. L'utilisation d'un pool de connexions permet de réutiliser les sockets établies :
import { createClientPool } from 'redis';
const securePool = await createClientPool({
socket: {
tls: true,
},
isolationPoolOptions: {
min: 10,
max: 50
}
}).connect();
Réutilisation des sessions TLS
Configurer le délai d'expiration des sessions TLS permet de réduire la fréquence des négociations cryptographiques complètes :
const sessionClient = createClient({
socket: {
tls: true,
sessionTimeout: 600, // Validité de la session en secondes
}
});
Résolution des incidents de sécurité courants
- Échec de validation du certificat TLS : Vérifiez que la chaîne de certificats est complète, que le certificat n'a pas expiré et que le nom d'hôte correspond bien à celui du serveur.
- Dégradation des performances lors du chiffrement : Activez la réutilisation des sessions TLS, assurez-vous que le matériel supporte les instructions AES-NI, et ajustez la taille du pool de connexions.
- Complexité de la gestion des clés : Déléguez la gestion et la rotation des clés de chiffrement à des services dédiés comme AWS KMS ou Azure Key Vault plutôt que de les stocker en dur dans le code.