-
Notifications
You must be signed in to change notification settings - Fork 123
Fix hardcoded JWT secrets in .env.example and constants.js (CWE-798) #178
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,14 @@ | ||
| MONGO_URL_DEV=mongodb://localhost/nodejs-api-boilerplate-dev | ||
| JWT_SECRET_DEV=ewtijwebgiuweg9w98u9283982t!!u1h28h1t1h89u9h@$$ | ||
|
|
||
| MONGO_URL_TEST=mongodb://localhost/nodejs-api-boilerplate-test | ||
| JWT_SECRET_TEST=ewtijwebgiuweg9w98u9283982t!!u1h28h1t1h89u9h@$$ | ||
|
|
||
| MONGO_URL_PROD=mongodb://localhost/nodejs-api-boilerplate-prod | ||
| JWT_SECRET_PROD=ewtijwebgiuweg9w98u9283982t!!u1h28h1t1h89u9h@$$ | ||
|
|
||
| RAVEN_ID=yourapikey | ||
| DOCS_WEBSITE=yourwebsiteurl | ||
| MONGO_URL_DEV=mongodb://localhost/nodejs-api-boilerplate-dev | ||
| # Generate: node -e "console.log(require('crypto').randomBytes(64).toString('hex'))" | ||
| JWT_SECRET_DEV= | ||
|
|
||
| MONGO_URL_TEST=mongodb://localhost/nodejs-api-boilerplate-test | ||
| # Generate: node -e "console.log(require('crypto').randomBytes(64).toString('hex'))" | ||
| JWT_SECRET_TEST= | ||
|
|
||
| MONGO_URL_PROD=mongodb://localhost/nodejs-api-boilerplate-prod | ||
| # Generate: node -e "console.log(require('crypto').randomBytes(64).toString('hex'))" | ||
| JWT_SECRET_PROD= | ||
|
|
||
| RAVEN_ID=yourapikey | ||
| DOCS_WEBSITE=yourwebsiteurl |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,48 +1,68 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| require('dotenv').config(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const WHITELIST = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| posts: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| create: ['title', 'text'], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| update: ['title', 'text'], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| users: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| create: ['email', 'username', 'password'], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const devConfig = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| JWT_SECRET: process.env.JWT_SECRET_DEV, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MONGO_URL: process.env.MONGO_URL_DEV, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const testConfig = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| JWT_SECRET: 'ewtijwebgiuweg9w98u9283982t!!u1h28h1t1h89u9h@$$', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MONGO_URL: 'mongodb://localhost/nodejs-api-boilerplate-test', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const prodConfig = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| JWT_SECRET: process.env.JWT_SECRET_PROD, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MONGO_URL: process.env.MONGO_URL_PROD, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const defaultConfig = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PORT: process.env.PORT || 3000, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| RAVEN_ID: process.env.RAVEN_ID, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| WHITELIST, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function envConfig(env) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| switch (env) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case 'development': | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return devConfig; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case 'test': | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return testConfig; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| default: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return prodConfig; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export default { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ...defaultConfig, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ...envConfig(process.env.NODE_ENV), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| require('dotenv').config(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const WHITELIST = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| posts: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| create: ['title', 'text'], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| update: ['title', 'text'], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| users: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| create: ['email', 'username', 'password'], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const devConfig = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| JWT_SECRET: process.env.JWT_SECRET_DEV, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MONGO_URL: process.env.MONGO_URL_DEV, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const testConfig = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| JWT_SECRET: process.env.JWT_SECRET_TEST, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MONGO_URL: process.env.MONGO_URL_TEST || 'mongodb://localhost/nodejs-api-boilerplate-test', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const prodConfig = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| JWT_SECRET: process.env.JWT_SECRET_PROD, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MONGO_URL: process.env.MONGO_URL_PROD, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const defaultConfig = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PORT: process.env.PORT || 3000, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| RAVEN_ID: process.env.RAVEN_ID, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| WHITELIST, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function envConfig(env) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| switch (env) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case 'development': | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return devConfig; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case 'test': | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return testConfig; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| default: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return prodConfig; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const config = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ...defaultConfig, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ...envConfig(process.env.NODE_ENV), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Validate JWT_SECRET at startup — reject empty/weak defaults | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const KNOWN_WEAK_SECRETS = [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| '', 'ewtijwebgiuweg9w98u9283982t!!u1h28h1t1h89u9h@$$', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'secret', 'changeme', 'jwt_secret', 'your-secret-key', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!config.JWT_SECRET || KNOWN_WEAK_SECRETS.includes(config.JWT_SECRET)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new Error( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'JWT_SECRET is not set or uses a known default value. ' + | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'Set JWT_SECRET_DEV, JWT_SECRET_TEST, or JWT_SECRET_PROD in your .env file. ' + | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'Generate a secure key: node -e "console.log(require(\'crypto\').randomBytes(64).toString(\'hex\'))"' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (config.JWT_SECRET.length < 32) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new Error( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `JWT_SECRET must be at least 32 characters. Current length: ${config.JWT_SECRET.length}.` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+51
to
+66
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The JWT secret validation can be improved by handling potential whitespace and performing case-insensitive checks for known weak secrets. This prevents accidental bypasses with leading/trailing spaces or variations like 'Secret'. Also, the empty string in the KNOWN_WEAK_SECRETS array is redundant as the !config.JWT_SECRET check already handles empty strings, null, and undefined values. The length check is also updated to use the trimmed secret for accuracy.
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export default config; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix denylist mismatch and normalize JWT secret before validation.
Line 52 appears to have a typo (
...h@$$) versus the compromised value described in this PR (...h@$), so the intended block may not trigger. Also, rawincludeschecks can be bypassed with whitespace/case variants (for example," Secret ").Suggested patch
Also applies to: 62-64
🤖 Prompt for AI Agents