Skip to content

Commit beb35b7

Browse files
committed
fix: critical security hardening for license routes, SSRF, and encryption
CRITICAL fixes: - Add admin::isAuthenticatedAdmin to all 5 license routes (were policies:[]) - Validate IP address format before geolocation fetch (prevents SSRF) - Remove hardcoded 'default-insecure-key' fallback, throw error if no key These vulnerabilities allowed unauthenticated access to license data, SSRF via crafted IP addresses, and trivial decryption of stored tokens.
1 parent 37bedf9 commit beb35b7

File tree

3 files changed

+22
-15
lines changed

3 files changed

+22
-15
lines changed

server/src/controllers/session.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,13 @@ module.exports = {
594594
return ctx.badRequest('IP address is required');
595595
}
596596

597+
// Validate IP address format to prevent SSRF
598+
const IPV4_REGEX = /^(\d{1,3}\.){3}\d{1,3}$/;
599+
const IPV6_REGEX = /^[0-9a-fA-F:]+$/;
600+
if (!IPV4_REGEX.test(ipAddress) && !IPV6_REGEX.test(ipAddress)) {
601+
return ctx.badRequest('Invalid IP address format');
602+
}
603+
597604
// Check if user has premium license
598605
const licenseGuard = strapi.plugin('magic-sessionmanager').service('license-guard');
599606
const pluginStore = strapi.store({

server/src/routes/admin.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,39 +90,39 @@ module.exports = {
9090
path: '/license/status',
9191
handler: 'license.getStatus',
9292
config: {
93-
policies: [],
93+
policies: ['admin::isAuthenticatedAdmin'],
9494
},
9595
},
9696
{
9797
method: 'POST',
9898
path: '/license/auto-create',
9999
handler: 'license.autoCreate',
100100
config: {
101-
policies: [],
101+
policies: ['admin::isAuthenticatedAdmin'],
102102
},
103103
},
104104
{
105105
method: 'POST',
106106
path: '/license/create',
107107
handler: 'license.createAndActivate',
108108
config: {
109-
policies: [],
109+
policies: ['admin::isAuthenticatedAdmin'],
110110
},
111111
},
112112
{
113113
method: 'POST',
114114
path: '/license/ping',
115115
handler: 'license.ping',
116116
config: {
117-
policies: [],
117+
policies: ['admin::isAuthenticatedAdmin'],
118118
},
119119
},
120120
{
121121
method: 'POST',
122122
path: '/license/store-key',
123123
handler: 'license.storeKey',
124124
config: {
125-
policies: [],
125+
policies: ['admin::isAuthenticatedAdmin'],
126126
},
127127
},
128128
// Geolocation (Premium Feature)

server/src/utils/encryption.js

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,19 @@ function getEncryptionKey() {
2222
const envKey = process.env.SESSION_ENCRYPTION_KEY;
2323

2424
if (envKey) {
25-
// Use provided key (must be 32 bytes for AES-256)
26-
const key = crypto.createHash('sha256').update(envKey).digest();
27-
return key;
25+
return crypto.createHash('sha256').update(envKey).digest();
2826
}
2927

30-
// Fallback: Use Strapi's app keys (not recommended for production)
31-
const strapiKeys = process.env.APP_KEYS || process.env.API_TOKEN_SALT || 'default-insecure-key';
32-
const key = crypto.createHash('sha256').update(strapiKeys).digest();
33-
34-
console.warn('[magic-sessionmanager/encryption] [WARNING] No SESSION_ENCRYPTION_KEY found. Using fallback (not recommended for production).');
35-
console.warn('[magic-sessionmanager/encryption] Set SESSION_ENCRYPTION_KEY in .env for better security.');
28+
// Fallback: Use Strapi's app keys (always present in running Strapi)
29+
const strapiKeys = process.env.APP_KEYS || process.env.API_TOKEN_SALT;
30+
if (!strapiKeys) {
31+
throw new Error(
32+
'[magic-sessionmanager] No encryption key available. ' +
33+
'Set SESSION_ENCRYPTION_KEY in your .env file, or ensure APP_KEYS is configured.'
34+
);
35+
}
3636

37-
return key;
37+
return crypto.createHash('sha256').update(strapiKeys).digest();
3838
}
3939

4040
/**

0 commit comments

Comments
 (0)