Skip to content

Commit

Permalink
feat: add hostnameExceptionList for ssrf
Browse files Browse the repository at this point in the history
  • Loading branch information
killagu committed Jul 8, 2024
1 parent b4d8baa commit 67c0043
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 2 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,7 @@ In a [Server-Side Request Forgery (SSRF)](https://www.owasp.org/index.php/Server

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

```js
Expand All @@ -540,6 +541,10 @@ exports.security = {
'10.1.1.1',
'10.10.0.1/24',
],
// legal hostname
hostnameExceptionList: [
'example.com',
],
// checkAddress has higher priority than ipBlackList
checkAddress(ip) {
return ip !== '127.0.0.1';
Expand Down
2 changes: 1 addition & 1 deletion app/extend/context.js
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ module.exports = {
},

[CSRF_REFERER_CHECK]() {
const { refererWhiteList } = this.app.config.security.csrf;
const { refererWhiteList } = this.app.config.security.csrf.refererWhiteList;
const referer = (this.headers.referer || '').toLowerCase();
if (!referer) {
debug('missing csrf referer');
Expand Down
1 change: 1 addition & 0 deletions config/config.default.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ module.exports = () => {
ssrf: {
ipBlackList: null,
ipExceptionList: null,
hostnameExceptionList: null,
checkAddress: null,
},
};
Expand Down
9 changes: 8 additions & 1 deletion lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,14 @@ exports.preprocessConfig = function(config) {
if (ssrf && ssrf.ipBlackList && !ssrf.checkAddress) {
const containsList = ssrf.ipBlackList.map(getContains);
const exceptionList = (ssrf.ipExceptionList || []).map(getContains);
ssrf.checkAddress = ipAddresses => {
const hostnameExceptionList = ssrf.hostnameExceptionList;
ssrf.checkAddress = (ipAddresses, family, hostname) => {
// Check hostname first
if (hostname && hostnameExceptionList) {
if (hostnameExceptionList.includes(hostname)) {
return true;
}
}
// ipAddresses will be array address on Node.js >= 20
// [
// { address: '220.181.125.241', family: 4 },
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
'use strict';

exports.security = {
ssrf: {
ipBlackList: [
'10.0.0.0/8',
'127.0.0.1',
'0.0.0.0/32',
],
hostnameExceptionList: [
'registry.npmjs.org',
'registry.npmmirror.com',
],
},
};
3 changes: 3 additions & 0 deletions test/fixtures/apps/ssrf-hostname-exception-list/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"name": "ssrf-ip-black-list"
}
31 changes: 31 additions & 0 deletions test/ssrf.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,37 @@ describe('test/ssrf.test.js', () => {
assert(count === 3);
});
});

describe('hostnameExceptionList', () => {
before(() => {
app = mm.app({ baseDir: 'apps/ssrf-hostname-exception-list' });
return app.ready();
});

it('should safeCurl work', async () => {
const ctx = app.createAnonymousContext();
const host = process.env.CI ? 'registry.npmjs.org' : 'registry.npmmirror.com';
const url = `https://${host}`;
let count = 0;

mm(app, 'curl', async (url, options) => {
options.checkAddress('10.0.0.1', 4, host) && count++;
return 'response';
});
mm(app.agent, 'curl', async (url, options) => {
options.checkAddress('10.0.0.1', 4, host) && count++;
return 'response';
});
mm(ctx, 'curl', async (url, options) => {
options.checkAddress('10.0.0.1', 4, host) && count++;
return 'response';
});

await app.safeCurl(url, { dataType: 'json' });
await app.agent.safeCurl(url, { dataType: 'json' });
await ctx.safeCurl(url, { dataType: 'json' });
});
});
});

async function checkIllegalAddressError(instance, url) {
Expand Down

0 comments on commit 67c0043

Please sign in to comment.