fix: Mindestlaenge fuer JWT_ACCESS_SECRET beim Start erzwungen
JWT_ACCESS_SECRET war zwar als Pflicht-Env-Var geprueft, aber ohne Mindestlaenge - ein kurzer/erratbarer Wert haette nicht nur das Faelschen von Access-/MFA-/SSO-Tokens fuer jeden Nutzer erlaubt, sondern auch (ueber mfa-crypto.util.ts, das den AES-Schluessel fuer gespeicherte TOTP-Secrets aus genau diesem Wert ableitet) das Entschluesseln aller gespeicherten MFA-Secrets. main.ts bricht den Start jetzt mit klarer Fehlermeldung ab, wenn JWT_ACCESS_SECRET kuerzer als 32 Zeichen ist. .env.example und das K8s-Secret-Template zeigen jetzt einen ausreichend langen Platzhalter mit Erzeugungshinweis (openssl rand -hex 32). Betrifft nur main.ts's bootstrap() - e2e-Tests bootstrappen ueber test/utils/create-test-app.ts direkt via configureApp() und durchlaufen diese Pruefung nicht, daher keine Testanpassung noetig. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
1 parent
4bd7e9791a
commit
33fa768062
3 files changed
+23
-2
No files matched your search
@@ -2,7 +2,10 @@
|
||||
|
||||
DATABASE_URL="postgresql://user:password@localhost:5432/apicore?schema=public"
|
||||
|
||||
JWT_ACCESS_SECRET="change-me-access-secret"
|
||||
# Mindestens 32 Zeichen (server-seitig erzwungen, siehe main.ts) - signiert
|
||||
# jedes Access-/MFA-/SSO-Token UND verschluesselt gespeicherte TOTP-Secrets.
|
||||
# Erzeugen z.B. mit: openssl rand -hex 32
|
||||
JWT_ACCESS_SECRET="change-me-to-a-random-value-at-least-32-characters-long"
|
||||
JWT_REFRESH_SECRET="change-me-refresh-secret"
|
||||
JWT_ACCESS_EXPIRES_IN="15m"
|
||||
JWT_REFRESH_EXPIRES_IN="7d"
|
||||
|
||||
@@ -5,6 +5,15 @@ import { AppModule } from './app.module';
|
||||
import { configureApp } from './app.config';
|
||||
|
||||
const REQUIRED_ENV_VARS = ['DATABASE_URL', 'JWT_ACCESS_SECRET'] as const;
|
||||
// JWT_ACCESS_SECRET is the root of trust for both authentication (every
|
||||
// access/MFA-challenge/SSO-completion token is signed with it) and MFA
|
||||
// secret confidentiality (mfa-crypto.util.ts derives the AES key that
|
||||
// encrypts stored TOTP secrets from it) - a short or guessable value here
|
||||
// would let an attacker forge tokens for any user AND decrypt everyone's
|
||||
// stored TOTP secret, not just guess a password. 32 chars matches the
|
||||
// output of "openssl rand -hex 32" / "-base64 32", the standard way to
|
||||
// generate one.
|
||||
const MIN_JWT_SECRET_LENGTH = 32;
|
||||
|
||||
function assertRequiredEnvVars(): void {
|
||||
const missing = REQUIRED_ENV_VARS.filter((name) => !process.env[name]);
|
||||
@@ -13,6 +22,12 @@ function assertRequiredEnvVars(): void {
|
||||
`Missing required environment variable(s): ${missing.join(', ')}`,
|
||||
);
|
||||
}
|
||||
const jwtSecret = process.env.JWT_ACCESS_SECRET as string;
|
||||
if (jwtSecret.length < MIN_JWT_SECRET_LENGTH) {
|
||||
throw new Error(
|
||||
`JWT_ACCESS_SECRET must be at least ${MIN_JWT_SECRET_LENGTH} characters (got ${jwtSecret.length}) - generate one with e.g. "openssl rand -hex 32"`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function bootstrap() {
|
||||
|
||||
@@ -10,7 +10,10 @@ stringData:
|
||||
# Anderer Namespace als das Backend? Dann FQDN nutzen:
|
||||
# apicore-postgres.<namespace>.svc.cluster.local
|
||||
DATABASE_URL: "postgresql://apicore:change-me-postgres-password@apicore-postgres:5432/apicore?schema=public"
|
||||
JWT_ACCESS_SECRET: "change-me-access-secret"
|
||||
# Mindestens 32 Zeichen (server-seitig erzwungen, Start bricht sonst ab) -
|
||||
# signiert jedes Access-/MFA-/SSO-Token UND verschluesselt gespeicherte
|
||||
# TOTP-Secrets. Erzeugen z.B. mit: openssl rand -hex 32
|
||||
JWT_ACCESS_SECRET: "change-me-to-a-random-value-at-least-32-characters-long"
|
||||
JWT_REFRESH_SECRET: "change-me-refresh-secret"
|
||||
# Entra ID App-Registrierung (SSO). Siehe configmap.example.yaml
|
||||
# fuer OIDC_ISSUER_URL/OIDC_REDIRECT_URI.
|
||||
|
||||
Reference in new issue
Block a user