Introduction
Un Cloud Access Security Broker (CASB) agit comme un intermédiaire intelligent entre vos utilisateurs et les services cloud (SaaS, IaaS comme AWS S3, Google Drive). Il applique des politiques de sécurité : authentification, détection de fuites de données (DLP), logging granulaire et blocage d'accès non conformes. En 2026, avec la multiplication des menaces zero-day et des régulations comme DORA ou NIS2, un CASB maison est essentiel pour les entreprises sans budget pour Netskope ou Zscaler.
Ce tutoriel intermédiaire vous guide pour implémenter un CASB proxy avec Node.js et TypeScript. Nous créerons un serveur qui intercepte les requêtes HTTP vers AWS S3, vérifie l'authentification JWT, scanne les payloads pour des mots sensibles (DLP basique), logge tout et forwarde ou bloque. Pourquoi c'est crucial ? 70% des breaches cloud viennent d'accès non contrôlés (rapport Gartner 2025). À la fin, vous aurez un prototype production-ready, scalable avec Docker. Temps estimé : 30 min. (128 mots)
Prérequis
- Node.js 20+ et npm/yarn installés
- Connaissances intermédiaires en TypeScript et Express.js
- Compte AWS avec accès S3 (créez un bucket de test)
- Variables d'environnement :
JWT_SECRET,AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,S3_BUCKET - Outils : Docker pour le déploiement optionnel
- IDE avec support TS (VS Code recommandé)
Initialisation du projet
mkdir casb-proxy && cd casb-proxy
npm init -y
npm install express http-proxy-middleware jsonwebtoken aws-sdk @types/express @types/jsonwebtoken @types/node typescript ts-node nodemon
npm install -D @types/http-proxy-middleware
npx tsc --init
touch src/server.ts src/policies.ts src/logger.ts .envCette commande initialise un projet Node.js, installe Express pour le serveur, http-proxy-middleware pour forwarder les requêtes S3, jsonwebtoken pour l'auth, AWS SDK pour interagir avec S3, et TypeScript. Les types assurent la sécurité statique. Évitez les versions outdated pour prévenir les vulnérabilités CVE-2025.
Structure du projet
Votre arborescence :
````
casb-proxy/
├── src/
│ ├── server.ts # Serveur principal et proxy
│ ├── policies.ts # Logiques DLP et auth
│ └── logger.ts # Logging structuré
├── .env # Secrets
├── tsconfig.json # Config TS
└── package.json
Le flux : Client → CASB (auth + DLP) → AWS S3. Analogie : un portier d'hôtel qui vérifie l'identité et scanne les bagages avant d'accéder aux chambres.
Configuration TypeScript et package.json
{
"name": "casb-proxy",
"version": "1.0.0",
"main": "src/server.ts",
"scripts": {
"dev": "nodemon --exec ts-node src/server.ts",
"build": "tsc",
"start": "node dist/server.js"
},
"dependencies": {
"express": "^4.19.2",
"http-proxy-middleware": "^3.0.0",
"jsonwebtoken": "^9.0.2",
"@aws-sdk/client-s3": "^3.600.0",
"@types/express": "^4.17.21",
"@types/jsonwebtoken": "^9.0.6",
"@types/node": "^22.5.5",
"typescript": "^5.5.4",
"ts-node": "^10.9.2",
"nodemon": "^3.1.4"
},
"devDependencies": {
"@types/http-proxy-middleware": "^2.0.7"
}
}Ce package.json définit les scripts pour dev/prod et liste les dépendances critiques. Notez @aws-sdk/client-s3 pour les interactions S3 natives. Piège : oubliez les types, TypeScript ne détectera pas les erreurs runtime comme les malformés JWT.
Module de logging
import { createWriteStream } from 'fs';
import { format } from 'util';
export interface LogEntry {
timestamp: string;
userId?: string;
action: string;
target: string;
status: 'ALLOW' | 'BLOCK' | 'ERROR';
details?: string;
}
const logStream = createWriteStream('casb-logs.jsonl', { flags: 'a' });
export function log(entry: LogEntry): void {
const line = JSON.stringify({ ...entry, timestamp: new Date().toISOString() }) + '\n';
logStream.write(line);
console.log(`[CASB] ${entry.status} - ${entry.action} to ${entry.target} by ${entry.userId || 'anonymous'}`);
}
log({ action: 'STARTUP', target: 'CASB Proxy', status: 'ALLOW' });Ce logger JSONL enregistre chaque événement avec timestamp, userId et détails. Format ligne-par-ligne pour ELK Stack ou Splunk. Avantage : scalable, queryable. Piège : sans 'flags: a', les logs se réécrivent au redémarrage.
Mise en place des politiques DLP
Les politiques vérifient : 1) JWT valide, 2) Scan payload pour mots sensibles (ex: 'confidentiel', regex pour SSN). Si non-conforme, block + log. Imaginez un filtre anti-spam mais pour données sensibles.
Module des politiques de sécurité
import jwt from 'jsonwebtoken';
import { Request, Response, NextFunction } from 'express';
const JWT_SECRET = process.env.JWT_SECRET || 'dev-secret-change-me';
const SENSITIVE_PATTERNS = [/confidentiel/i, /\b\d{3}-\d{2}-\d{4}\b/, /SSN:/i];
export interface AuthUser {
userId: string;
roles: string[];
}
export function authenticate(req: Request, res: Response, next: NextFunction): void {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
res.status(401).json({ error: 'Token requis' });
return;
}
try {
const user = jwt.verify(token, JWT_SECRET) as AuthUser;
req.user = user;
next();
} catch (err) {
res.status(401).json({ error: 'Token invalide' });
}
}
export function dlpScan(body: string): boolean {
return !SENSITIVE_PATTERNS.some(pattern => pattern.test(body));
}
export function applyPolicies(req: Request, res: Response, next: NextFunction): void {
if (!dlpScan(JSON.stringify(req.body) + req.url)) {
res.status(403).json({ error: 'Données sensibles détectées' });
return;
}
next();
}authenticate middleware parse JWT et attache user à req. dlpScan utilise regex pour détecter fuites (SSN, mots-clés). applyPolicies chaîne les checks. Piège : regex non-échappées causent false positives ; testez avec payloads variés.
Configuration AWS et .env
Ajoutez dans .env :
```
JWT_SECRET=your-super-secret-key-2026
AWS_ACCESS_KEY_ID=your-key
AWS_SECRET_ACCESS_KEY=your-secret
S3_BUCKET=your-test-bucket
PORT=3000.gitignore`.
Protégez ce fichier avec
Serveur principal CASB
import express from 'express';
import { createProxyMiddleware } from 'http-proxy-middleware';
import { S3Client, PutObjectCommand, GetObjectCommand } from '@aws-sdk/client-s3';
import { log, LogEntry } from './logger';
import { authenticate, applyPolicies, AuthUser } from './policies';
const app = express();
app.use(express.json({ limit: '10mb' }));
app.use(express.raw({ type: '*/*', limit: '10mb' }));
const s3 = new S3Client({ region: 'us-east-1' });
const TARGET_BUCKET = process.env.S3_BUCKET!;
const PORT = (process.env.PORT || 3000) as number;
// Générer JWT pour tests
app.post('/auth/login', (req, res) => {
const token = require('jsonwebtoken').sign({ userId: 'user123', roles: ['admin'] }, process.env.JWT_SECRET!);
res.json({ token });
});
// Proxy S3 PUT (upload)
app.put('/s3/*', authenticate, applyPolicies, async (req, res) => {
const key = req.url.replace('/s3/', '');
const logEntry: LogEntry = { userId: (req.user as AuthUser).userId, action: 'UPLOAD', target: key, status: 'ALLOW' };
try {
await s3.send(new PutObjectCommand({ Bucket: TARGET_BUCKET, Key: key, Body: req.body }));
log(logEntry);
res.status(200).json({ success: true });
} catch (err) {
log({ ...logEntry, status: 'ERROR', details: (err as Error).message });
res.status(500).json({ error: 'Upload échoué' });
}
});
// Proxy S3 GET (download)
app.get('/s3/*', authenticate, (req, res) => {
const key = req.url.replace('/s3/', '');
log({ userId: (req.user as AuthUser).userId, action: 'DOWNLOAD', target: key, status: 'ALLOW' });
// Forward proxy pour GET complet
createProxyMiddleware({
target: `https://${TARGET_BUCKET}.s3.amazonaws.com`,
changeOrigin: true,
pathRewrite: { '^/s3': '' },
onProxyReq: (proxyReq) => proxyReq.setHeader('Authorization', req.headers.authorization || ''),
})(req, res);
});
app.listen(PORT, () => {
log({ action: 'LISTEN', target: `port ${PORT}`, status: 'ALLOW' });
console.log(`CASB Proxy sur http://localhost:${PORT}`);
});Ce serveur Express proxy les PUT/GET S3 via AWS SDK. Auth + DLP avant forward. /auth/login pour tester JWT. Limite body à 10MB anti-DoS. Piège : sans region AWS, les calls échouent ; toujours logger avant/after pour audit.
Test et déploiement
npm run dev- Obtenez token :
curl -X POST http://localhost:3000/auth/login - Test upload safe :
curl -H "Authorization: Bearer" -X PUT http://localhost:3000/s3/test.txt -d "contenu normal" - Test block DLP :
-d "SSN: 123-45-6789"→ 403
casb-logs.jsonl. Pour prod, build + Docker.Dockerfile pour production
FROM node:20-alpine
WORKDIR /app
COPY package*.json tsconfig.json ./
RUN npm ci --only=production && npm run build
COPY --from=build /app/dist ./dist
COPY .env ./
EXPOSE 3000
CMD ["node", "dist/server.js"]
# Multi-stage pour sécurité : exclut dev depsDockerfile multi-stage minimise l'image ( <200MB). Copie .env à runtime. Lance en prod sans nodemon/ts-node. Piège : oubliez npm ci pour deps lockées ; scannez l'image avec Trivy pour vulns.
Bonnes pratiques
- Rotation JWT : Implémentez refresh tokens et expire <15min.
- DLP avancé : Intégrez ML comme Google DLP API pour regex limités.
- Scalabilité : Utilisez Redis pour cache policies, PM2/K8s pour cluster.
- Observabilité : Forwardez logs vers ELK ou Datadog.
- Compliance : Ajoutez consentement RGPD pour scans payloads.
Erreurs courantes à éviter
- Pas de rate limiting : Ajoutez
express-rate-limitcontre brute-force auth. - Secrets hardcodés : Toujours .env + Vault en prod.
- Ignorez CORS : Pour frontend, ajoutez middleware CORS restreint aux domaines autorisés.
- Pas de HTTPS : Forcez via Nginx reverse proxy ou LetsEncrypt.
Pour aller plus loin
- Docs AWS SDK : aws.amazon.com/sdk
- CASB open-source : Explorez Envoy Proxy pour WASM policies.
- Avancé : Intégrez avec Okta pour SSO.