2.1 KiB
2.1 KiB
Security
Private key on the server
Design B generates the user private key in the portal:
- Created in RAM on POST
/download - Exported into an identity PKCS#12 (still RAM)
- Embedded in the plist, which is then CMS-signed
- Returned in the HTTP response
- Not written to disk; no cache of keys or P12 passwords
Anyone who can download a profile for a user obtains that user’s private key. Protect the app like an RA:
- OIDC on every download
- Optional
LDAP_REQUIRED_GROUP - HTTPS only in production (
SESSION_HTTPS_ONLY, reverse-proxy TLS) - Restrict who can reach the portal (VPN, SSO group, IP allow list)
Do not use this design for keys that must be device-bound.
Logging
The portal logs UPN and SID at info when a profile is issued. It does not log:
- Identity P12 bytes or passwords
- Profile-signing P12 passwords
- OIDC client secrets, LDAP bind passwords, certsrv passwords
- Raw CSR PEM at info (avoid debug logging in production)
Session cookie: HttpOnly, SameSite=lax, Secure when SESSION_HTTPS_ONLY is true.
Who can enroll
- Must complete OIDC (valid session).
- Must match exactly one LDAP user.
- If
LDAP_REQUIRED_GROUPis set, must be a member (nested groups allowed). - The service account, not the user, must have Enroll on the template. A signed-in user cannot enroll a template the portal account cannot enroll.
There is no Enrollment Agent. A caller cannot request a cert for a different SID than LDAP returned for their username.
CA trust
Prefer CERTSRV_VERIFY_TLS=true and LDAP_VERIFY_TLS=true. Disabling TLS verification allows credential theft on the path to certsrv (NTLM) and LDAP.
OpenAPI
Leave DOCS_ENABLED=false in production so /docs is not public.
Threat notes
| Issue | Mitigation |
|---|---|
| Stolen session cookie | Short IdP session, HTTPS, Secure cookies |
| Stolen signing P12 | Filesystem permissions / secret store; rotate |
| Template too powerful | Dedicated Wi-Fi user template, not a smartcard/admin template |
| certsrv HTML change | Parser fails closed (unknown page = error), fixtures in tests |
| SSRF-ish CA URL | CERTSRV_URL is config, not user input |