Skip to content

Commit

Permalink
Implement IP address blacklist, and validate hostnames on redirect.
Browse files Browse the repository at this point in the history
  • Loading branch information
kring committed Aug 17, 2018
1 parent 80444d3 commit 3cbc484
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 2 deletions.
60 changes: 59 additions & 1 deletion lib/controllers/proxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ var express = require('express');
var defaultRequest = require('request');
var url = require('url');
var bodyParser = require('body-parser');
var rangeCheck = require('range_check');

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;
var PROTOCOL_REGEX = /^\w+:\//;
Expand Down Expand Up @@ -46,12 +47,49 @@ module.exports = function(options) {
var proxyDomains = options.proxyableDomains || [];
var proxyAuth = options.proxyAuth || {};
var proxyPostSizeLimit = options.proxyPostSizeLimit || '102400';

// If you change this, also change the same list in serverconfig.json.example.
// This page is helpful: https://en.wikipedia.org/wiki/Reserved_IP_addresses
var blacklistedAddresses = options.blacklistedAddresses || [
// loopback addresses
'127.0.0.0/8',
'::1/128',
// link local addresses
'169.254.0.0/16',
'fe80::/10',
// private network addresses
'10.0.0.0/8',
'172.16.0.0/12',
'192.168.0.0/16',
'fc00::/7',
// other
'0.0.0.0/8',
'100.64.0.0/10',
'192.0.0.0/24',
'192.0.2.0/24',
'198.18.0.0/15',
'192.88.99.0/24',
'198.51.100.0/24',
'203.0.113.0/24',
'224.0.0.0/4',
'240.0.0.0/4',
'255.255.255.255/32',
'::/128',
'2001:db8::/32',
'ff00::/8'
];

//Non CORS hosts and domains we proxy to
function proxyAllowedHost(host) {
// Exclude hosts that are really IP addresses and are in our blacklist.
if (rangeCheck.inRange(host, blacklistedAddresses)) {
return false;
}

if (proxyAllDomains) {
return true;
}

host = host.toLowerCase();
//check that host is from one of these domains
for (var i = 0; i < proxyDomains.length; i++) {
Expand Down Expand Up @@ -190,7 +228,27 @@ module.exports = function(options) {
headers: filteredRequestHeaders,
encoding: null,
proxy: proxy,
body: req.body
body: req.body,
followRedirect: function(response) {
var location = response.headers.location;
if (location && location.length > 0) {
var parsed = url.parse(location);
if (proxyAllowedHost(parsed.host)) {
// redirect is ok
return true;
}
}
// redirect is forbidden
return false;
}
}).on('socket', function(socket) {
socket.once('lookup', function(err, address, family, host) {
if (rangeCheck.inRange(address, blacklistedAddresses)) {
res.status(403).send('IP address is not allowed: ' + address);
res.end();
proxiedRequest.abort();
}
});
}).on('error', function(err) {
console.error(err);

Expand Down
3 changes: 2 additions & 1 deletion lib/makeserver.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ module.exports = function(options) {
proxyPostSizeLimit: options.settings.proxyPostSizeLimit,
upstreamProxy: options.settings.upstreamProxy,
bypassUpstreamProxyHosts: bypassUpstreamProxyHostsMap,
basicAuthentication: options.settings.basicAuthentication
basicAuthentication: options.settings.basicAuthentication,
blacklistedAddresses: options.settings.blacklistedAddresses
}));

var esriTokenAuth = require('./controllers/esri-token-auth')(options.settings.esriTokenAuth);
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"ogr2ogr": "^1.0.0",
"proj4": "^2.3.12",
"proj4js-defs": "0.0.1",
"range_check": "^1.4.0",
"request": "^2.67.0",
"request-promise": "^4.0.1",
"when": "^3.7.7",
Expand Down
32 changes: 32 additions & 0 deletions serverconfig.json.example
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,38 @@
// separately from the main codebase.
initPaths: [ "../randominits" ],

// Optional: IP addresses to refuse to proxy for, even if they're resolved from a hostname that we would ordinarily allow.
// 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
// 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.
blacklistedAddresses: [
// loopback addresses
"127.0.0.0/8",
"::1/128",
// link local addresses
"169.254.0.0/16",
"fe80::/10",
// private network addresses
"10.0.0.0/8",
"172.16.0.0/12",
"192.168.0.0/16",
"fc00::/7",
// other
"0.0.0.0/8",
"100.64.0.0/10",
"192.0.0.0/24",
"192.0.2.0/24",
"198.18.0.0/15",
"192.88.99.0/24",
"198.51.100.0/24",
"203.0.113.0/24",
"224.0.0.0/4",
"240.0.0.0/4",
"255.255.255.255/32",
"::/128",
"2001:db8::/32",
"ff00::/8"
],

// Pass requests through to another proxy upstream.
upstreamProxy: "proxy.example.com",

Expand Down

0 comments on commit 3cbc484

Please sign in to comment.