Skip to content

Commit 67c0043

Browse files
committed
feat: add hostnameExceptionList for ssrf
1 parent b4d8baa commit 67c0043

File tree

7 files changed

+64
-2
lines changed

7 files changed

+64
-2
lines changed

README.md

+5
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,7 @@ In a [Server-Side Request Forgery (SSRF)](https://www.owasp.org/index.php/Server
523523

524524
- ipBlackList(Array) - specific which IP addresses are illegal when requested with `safeCurl`.
525525
- ipExceptionList(Array) - specific which IP addresses are legal within ipBlackList.
526+
- hostnameExceptionList(Array) - specific which hostname are legal within ipBlackList.
526527
- checkAddress(Function) - determine the ip by the function's return value, `false` means illegal ip.
527528

528529
```js
@@ -540,6 +541,10 @@ exports.security = {
540541
'10.1.1.1',
541542
'10.10.0.1/24',
542543
],
544+
// legal hostname
545+
hostnameExceptionList: [
546+
'example.com',
547+
],
543548
// checkAddress has higher priority than ipBlackList
544549
checkAddress(ip) {
545550
return ip !== '127.0.0.1';

app/extend/context.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ module.exports = {
209209
},
210210

211211
[CSRF_REFERER_CHECK]() {
212-
const { refererWhiteList } = this.app.config.security.csrf;
212+
const { refererWhiteList } = this.app.config.security.csrf.refererWhiteList;
213213
const referer = (this.headers.referer || '').toLowerCase();
214214
if (!referer) {
215215
debug('missing csrf referer');

config/config.default.js

+1
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ module.exports = () => {
103103
ssrf: {
104104
ipBlackList: null,
105105
ipExceptionList: null,
106+
hostnameExceptionList: null,
106107
checkAddress: null,
107108
},
108109
};

lib/utils.js

+8-1
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,14 @@ exports.preprocessConfig = function(config) {
111111
if (ssrf && ssrf.ipBlackList && !ssrf.checkAddress) {
112112
const containsList = ssrf.ipBlackList.map(getContains);
113113
const exceptionList = (ssrf.ipExceptionList || []).map(getContains);
114-
ssrf.checkAddress = ipAddresses => {
114+
const hostnameExceptionList = ssrf.hostnameExceptionList;
115+
ssrf.checkAddress = (ipAddresses, family, hostname) => {
116+
// Check hostname first
117+
if (hostname && hostnameExceptionList) {
118+
if (hostnameExceptionList.includes(hostname)) {
119+
return true;
120+
}
121+
}
115122
// ipAddresses will be array address on Node.js >= 20
116123
// [
117124
// { address: '220.181.125.241', family: 4 },
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
'use strict';
2+
3+
exports.security = {
4+
ssrf: {
5+
ipBlackList: [
6+
'10.0.0.0/8',
7+
'127.0.0.1',
8+
'0.0.0.0/32',
9+
],
10+
hostnameExceptionList: [
11+
'registry.npmjs.org',
12+
'registry.npmmirror.com',
13+
],
14+
},
15+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"name": "ssrf-ip-black-list"
3+
}

test/ssrf.test.js

+31
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,37 @@ describe('test/ssrf.test.js', () => {
171171
assert(count === 3);
172172
});
173173
});
174+
175+
describe('hostnameExceptionList', () => {
176+
before(() => {
177+
app = mm.app({ baseDir: 'apps/ssrf-hostname-exception-list' });
178+
return app.ready();
179+
});
180+
181+
it('should safeCurl work', async () => {
182+
const ctx = app.createAnonymousContext();
183+
const host = process.env.CI ? 'registry.npmjs.org' : 'registry.npmmirror.com';
184+
const url = `https://${host}`;
185+
let count = 0;
186+
187+
mm(app, 'curl', async (url, options) => {
188+
options.checkAddress('10.0.0.1', 4, host) && count++;
189+
return 'response';
190+
});
191+
mm(app.agent, 'curl', async (url, options) => {
192+
options.checkAddress('10.0.0.1', 4, host) && count++;
193+
return 'response';
194+
});
195+
mm(ctx, 'curl', async (url, options) => {
196+
options.checkAddress('10.0.0.1', 4, host) && count++;
197+
return 'response';
198+
});
199+
200+
await app.safeCurl(url, { dataType: 'json' });
201+
await app.agent.safeCurl(url, { dataType: 'json' });
202+
await ctx.safeCurl(url, { dataType: 'json' });
203+
});
204+
});
174205
});
175206

176207
async function checkIllegalAddressError(instance, url) {

0 commit comments

Comments
 (0)