-
Notifications
You must be signed in to change notification settings - Fork 0
Security
Kengela est un socle de sécurité : sa propre posture doit être prouvée, pas affirmée. Cette page résume la doctrine Zero Trust, les contrôles vérifiés par l'audit adverse red/blue, et explique comment relancer cet audit.
| Invariant | Où il est appliqué |
|---|---|
| Deny-by-default | PDP (RbacDecisionPoint / LayeredDecisionPoint), guard NestJS (route non annotée = 403) |
| Fail-closed | condition CEL inévaluable → deny ; session expirée → null ; tenant non résoluble → refus ; narrowing d'union illisible → écarté |
| Isolation multi-tenant au cœur |
tenantScopedRelation() : cross-tenant → relation none ; seul un grant global franchit |
| Anti-staleness | grants rechargés à chaque check (un droit révoqué cesse d'agir immédiatement) |
| Timing-safe |
verify toujours effectué (hash leurre), pas de court-circuit cross-tenant (anti-énumération) |
| Crypto authentifié | AES-256-GCM, clé par tenant (HKDF) et par sujet ; toute altération → rejet |
| Anti-ReDoS |
matches interdit dans CEL ; regex bornées dans iam-mapping ; filtres SCIM bornés |
| Observabilité |
DecisionLogSink (autorisation), AuditSink (métier/sécurité), PiiAccessLogSink (RGPD art. 30) |
Le rapport complet est dans docs/SECURITY-AUDIT-REPORT.md. L'audit a
attaqué les 12 paquets de façon adverse (fakes en mémoire, aucun réseau/DB réel), puis prouvé les
contrôles et cartographié la conformité.
| Sévérité | Trouvées | Corrigées | Documentées (dette) |
|---|---|---|---|
| Critical | 0 | 0 | 0 |
| High | 4 | 4 | 0 |
| Medium | 1 | 0 | 1 |
| Low | 1 | 0 | 1 |
83 cas de test adverses (security-*.test.ts), tous verts. Aucune API de port publique modifiée.
-
Isolation multi-tenant défendue au cœur. Le PDP déléguait toute l'isolation au
RelationResolverde l'app. Correctif :tenantScopedRelation()ramène la relation ànoneen cross-tenant (fail-closed), câblé danspdp.tsetpolicy-pdp.ts, avec un signalcrossTenantau decision log. -
Guard NestJS fail-open. Un
@PublicRoutede classe neutralisait un@RequirePermissionde handler. Correctif : précédence explicite handler > classe (voir 05-nestjs-integration.md). -
ReDoS via CEL
matches. cel-js compilait uneRegExpnon bornée (DoS du PDP). Correctif :matchesinterdit à la compilation (fail-closed) ; les conditions s'écrivent via==,in,startsWith,contains. -
Sessions expirées servies.
get()restituait une ligne expirée non encore purgée. Correctif :get()renvoienulldèsexpiresAt <= now, indépendamment du cron (Prisma + connector-translog).
-
MEDIUM-1 — anti-rejeu du code TOTP (NIST 800-63B §5.1.4.2) : le défi est one-shot, mais un
code déjà consommé pourrait être rejoué via un nouveau
challengeIddans la fenêtre (~30 s). Cible : cache anti-rejeu (adapter-authn-native/DEBT.md#3). -
LOW-1 — helper
escapeLdapFilterValue()absent : l'adapter n'introduit aucune injection (filtre verbatim), mais n'outille pas l'appelant qui composerait un filtre depuis une entrée utilisateur (adapter-directory-ldap/DEBT.md#5).
| Scénario attaqué | Contrôle prouvé |
|---|---|
| Escalade par portée |
subtree ne couvre pas tenant ; grant expiré inopérant ; seul global franchit un tenant |
| Fail-open / deny-wins | relation none sans grant global = deny ; deny explicite gagne ; condition qui lève = deny |
| Sandbox CEL | accès process/globalThis/__proto__ → throw ; non-booléen → throw ; variable absente → throw |
| Énumération par timing | compare leurre toujours effectué (email inconnu, compte inactif) ; cross-tenant sans court-circuit |
| AES-256-GCM | iv/tag/ciphertext altéré → rejet ; tronqué → rejet ; mauvaise clé tenant → rejet ; nonce unique |
| Crypto-shredding | après eraseSubject, PII illisible ; clé d'un autre sujet ne déchiffre pas |
| Rejeu MFA |
challengeId one-shot ; verify sans secret = false ; challengeId forgé = false |
| SCIM | filtres bornés ; injection non interprétée ; unicité 409 ; delete = désactivation ; 404 cross-tenant |
| Mapping IdP | safe-regex anti-ReDoS ; règle vide ne matche jamais ; SAML sans groupe ne fabrique aucun rôle |
| better-auth | session absente → null ; tenant non résoluble → null ; roles/mfa jamais hérités du payload |
| LDAP | filtre verbatim ; unbind garanti même en erreur ; plafond max appliqué ; secret non journalisé |
| Sessions | rotation invalide l'ancien token ; revokeAllForUser effectif ; token 256 bits sans collision |
| Guard NestJS | deny-by-default (403) ; principal absent = 401 ; précédence handler > classe |
| Cadre | Couverture |
|---|---|
| OWASP ASVS v4 | V2 authn (argon2id, anti-énumération), V3 session (expiration fail-closed, rotation, 256 bits), V4 access control (deny-by-default, tenantScopedRelation, deny-wins), V6 crypto (AES-256-GCM par tenant/sujet), V7 logging (decision log), V9 data protection (pii, FieldCipherPort) |
| NIST SP 800-63B | hashing argon2id memory-hard + needsRehash ; verifier temps constant ; MFA TOTP (RFC 6238) secret chiffré + défi one-shot ; anti-rejeu OTP = dette #3 |
| RGPD | minimisation, effacement art. 17 (crypto-shredding), journal d'accès art. 30, rétention, chiffrement at-rest, isolation cryptographique inter-tenant |
| SCIM | RFC 7643/7644 + validateur Microsoft Entra (schéma, filtre eq borné, PATCH, unicité 409, déprovisionnement = désactivation, isolation tenant) |
Le prompt d'audit reproductible est dans
docs/SECURITY-AUDIT-PROMPT.md. Il confie à un agent (ou un pentester)
un accès lecture + écriture de tests au dépôt, avec pour mission de casser la lib (RED) puis
de prouver les contrôles (BLUE).
Cadre imposé :
- Vérifier d'abord que tout est vert :
pnpm install && pnpm -r build && pnpm -r test && pnpm lint:arch
- Lire les ports (
packages/contracts/src/index.ts), les implémentations (packages/*/src) et tous lesDEBT.md(chaque dette = une hypothèse de faiblesse à confirmer/infirmer). - Écrire des tests adverses
packages/*/test/security-*.test.ts(hermétiques, fakes en mémoire). - Corriger les Critical/High ; documenter les Medium/Low dans les
DEBT.md(une dette résolue est supprimée). - Re-vérifier build + test +
lint:archverts, sans régression, sans casser l'API publique des ports.
Livrables : le rapport docs/SECURITY-AUDIT-REPORT.md (findings par sévérité + mapping de conformité
- correctifs appliqués vs recommandés) et les tests adverses ajoutés.
Kengela est sous licence Apache-2.0. Pour un signalement responsable, ouvrez un contact privé avec le mainteneur plutôt qu'une issue publique tant que le correctif n'est pas disponible.
🇬🇧 English
- Home
- Getting started
- Architecture
- Authorization
- Authentication
- Identity federation
- Compliance & PII
- NestJS integration
- Developing an adapter
- Security
Recipes
- NestJS + native + Prisma
- better-auth
- SCIM / Entra
- LDAP / AD
- RBAC / ABAC
- PII / GDPR
- Combo: better-auth + PII
- Combo: SCIM/Entra + authz
- Combo: full stack
🇫🇷 Français
- Accueil
- Prise en main
- Architecture
- Autorisation
- Authentification
- Fédération d'identité
- Conformité & PII
- Intégration NestJS
- Développer un adapter
- Sécurité
Recettes