Skip to content

Commit 3cbc484

Browse files
committed
Implement IP address blacklist, and validate hostnames on redirect.
1 parent 80444d3 commit 3cbc484

File tree

4 files changed

+94
-2
lines changed

4 files changed

+94
-2
lines changed

lib/controllers/proxy.js

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ var express = require('express');
66
var defaultRequest = require('request');
77
var url = require('url');
88
var bodyParser = require('body-parser');
9+
var rangeCheck = require('range_check');
910

1011
var DO_NOT_PROXY_REGEX = /^(?:Host|X-Forwarded-Host|Proxy-Connection|Connection|Keep-Alive|Transfer-Encoding|TE|Trailer|Proxy-Authorization|Proxy-Authenticate|Upgrade|Expires|pragma|Strict-Transport-Security)$/i;
1112
var PROTOCOL_REGEX = /^\w+:\//;
@@ -46,12 +47,49 @@ module.exports = function(options) {
4647
var proxyDomains = options.proxyableDomains || [];
4748
var proxyAuth = options.proxyAuth || {};
4849
var proxyPostSizeLimit = options.proxyPostSizeLimit || '102400';
50+
51+
// If you change this, also change the same list in serverconfig.json.example.
52+
// This page is helpful: https://en.wikipedia.org/wiki/Reserved_IP_addresses
53+
var blacklistedAddresses = options.blacklistedAddresses || [
54+
// loopback addresses
55+
'127.0.0.0/8',
56+
'::1/128',
57+
// link local addresses
58+
'169.254.0.0/16',
59+
'fe80::/10',
60+
// private network addresses
61+
'10.0.0.0/8',
62+
'172.16.0.0/12',
63+
'192.168.0.0/16',
64+
'fc00::/7',
65+
// other
66+
'0.0.0.0/8',
67+
'100.64.0.0/10',
68+
'192.0.0.0/24',
69+
'192.0.2.0/24',
70+
'198.18.0.0/15',
71+
'192.88.99.0/24',
72+
'198.51.100.0/24',
73+
'203.0.113.0/24',
74+
'224.0.0.0/4',
75+
'240.0.0.0/4',
76+
'255.255.255.255/32',
77+
'::/128',
78+
'2001:db8::/32',
79+
'ff00::/8'
80+
];
4981

5082
//Non CORS hosts and domains we proxy to
5183
function proxyAllowedHost(host) {
84+
// Exclude hosts that are really IP addresses and are in our blacklist.
85+
if (rangeCheck.inRange(host, blacklistedAddresses)) {
86+
return false;
87+
}
88+
5289
if (proxyAllDomains) {
5390
return true;
5491
}
92+
5593
host = host.toLowerCase();
5694
//check that host is from one of these domains
5795
for (var i = 0; i < proxyDomains.length; i++) {
@@ -190,7 +228,27 @@ module.exports = function(options) {
190228
headers: filteredRequestHeaders,
191229
encoding: null,
192230
proxy: proxy,
193-
body: req.body
231+
body: req.body,
232+
followRedirect: function(response) {
233+
var location = response.headers.location;
234+
if (location && location.length > 0) {
235+
var parsed = url.parse(location);
236+
if (proxyAllowedHost(parsed.host)) {
237+
// redirect is ok
238+
return true;
239+
}
240+
}
241+
// redirect is forbidden
242+
return false;
243+
}
244+
}).on('socket', function(socket) {
245+
socket.once('lookup', function(err, address, family, host) {
246+
if (rangeCheck.inRange(address, blacklistedAddresses)) {
247+
res.status(403).send('IP address is not allowed: ' + address);
248+
res.end();
249+
proxiedRequest.abort();
250+
}
251+
});
194252
}).on('error', function(err) {
195253
console.error(err);
196254

lib/makeserver.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,8 @@ module.exports = function(options) {
9696
proxyPostSizeLimit: options.settings.proxyPostSizeLimit,
9797
upstreamProxy: options.settings.upstreamProxy,
9898
bypassUpstreamProxyHosts: bypassUpstreamProxyHostsMap,
99-
basicAuthentication: options.settings.basicAuthentication
99+
basicAuthentication: options.settings.basicAuthentication,
100+
blacklistedAddresses: options.settings.blacklistedAddresses
100101
}));
101102

102103
var esriTokenAuth = require('./controllers/esri-token-auth')(options.settings.esriTokenAuth);

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
"ogr2ogr": "^1.0.0",
4141
"proj4": "^2.3.12",
4242
"proj4js-defs": "0.0.1",
43+
"range_check": "^1.4.0",
4344
"request": "^2.67.0",
4445
"request-promise": "^4.0.1",
4546
"when": "^3.7.7",

serverconfig.json.example

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,38 @@
2222
// separately from the main codebase.
2323
initPaths: [ "../randominits" ],
2424

25+
// Optional: IP addresses to refuse to proxy for, even if they're resolved from a hostname that we would ordinarily allow.
26+
// The default is as shown here. If your server has access to an IP range that is not accessible to clients of the proxy, and you
27+
// want to ensure that the client can't get access to it through the proxy, it is vital that you add that IP range to this list.
28+
blacklistedAddresses: [
29+
// loopback addresses
30+
"127.0.0.0/8",
31+
"::1/128",
32+
// link local addresses
33+
"169.254.0.0/16",
34+
"fe80::/10",
35+
// private network addresses
36+
"10.0.0.0/8",
37+
"172.16.0.0/12",
38+
"192.168.0.0/16",
39+
"fc00::/7",
40+
// other
41+
"0.0.0.0/8",
42+
"100.64.0.0/10",
43+
"192.0.0.0/24",
44+
"192.0.2.0/24",
45+
"198.18.0.0/15",
46+
"192.88.99.0/24",
47+
"198.51.100.0/24",
48+
"203.0.113.0/24",
49+
"224.0.0.0/4",
50+
"240.0.0.0/4",
51+
"255.255.255.255/32",
52+
"::/128",
53+
"2001:db8::/32",
54+
"ff00::/8"
55+
],
56+
2557
// Pass requests through to another proxy upstream.
2658
upstreamProxy: "proxy.example.com",
2759

0 commit comments

Comments
 (0)