Skip to content

Commit e61de87

Browse files
RubenVerborghdmitrizagidulin
authored andcommitted
Ensure the host is not a local IP.
1 parent 1afb00a commit e61de87

File tree

2 files changed

+52
-28
lines changed

2 files changed

+52
-28
lines changed

lib/handlers/proxy.js

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const proxy = require('http-proxy-middleware')
44
const cors = require('cors')
55
const debug = require('../debug')
66
const url = require('url')
7+
const dns = require('dns')
78
const isIp = require('is-ip')
89
const ipRange = require('ip-range-check')
910
const validUrl = require('valid-url')
@@ -20,7 +21,12 @@ const PROXY_SETTINGS = {
2021
router: req => req.destination.target,
2122
pathRewrite: (path, req) => req.destination.path
2223
}
23-
const LOCAL_IP_RANGES = ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16']
24+
const LOCAL_IP_RANGES = [
25+
'10.0.0.0/8',
26+
'127.0.0.0/8',
27+
'172.16.0.0/12',
28+
'192.168.0.0/16'
29+
]
2430

2531
// Adds a CORS proxy handler to the application on the given path
2632
function addCorsProxyHandler (app, path) {
@@ -40,14 +46,21 @@ function extractProxyConfig (req, res, next) {
4046
return res.status(400).send(`Invalid URL passed: ${uri || '(none)'}`)
4147
}
4248

43-
// Ensure the host is not a local IP
44-
// TODO: guard against hostnames such as 'localhost' as well
49+
// Parse the URL and retrieve its host's IP address
4550
const { protocol, host, hostname, path } = url.parse(uri)
46-
if (isIp(hostname) && LOCAL_IP_RANGES.some(r => ipRange(hostname, r))) {
47-
return res.status(400).send(`Cannot proxy ${uri}`)
51+
if (isIp(hostname)) {
52+
addProxyConfig(null, hostname)
53+
} else {
54+
dns.lookup(hostname, addProxyConfig)
4855
}
4956

50-
// Add the proxy configuration to the request
51-
req.destination = { path, target: `${protocol}//${host}` }
52-
next()
57+
// Verifies and adds the proxy configuration to the request
58+
function addProxyConfig (error, hostAddress) {
59+
// Ensure the host is not a local IP
60+
if (error || LOCAL_IP_RANGES.some(r => ipRange(hostAddress, r))) {
61+
return res.status(400).send(`Cannot proxy ${uri}`)
62+
}
63+
req.destination = { path, target: `${protocol}//${host}` }
64+
next()
65+
}
5366
}

test/integration/proxy.js

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ describe('proxy', () => {
1515
var server = supertest(ldp)
1616

1717
it('should return the website in /proxy?uri', (done) => {
18-
nock('https://amazingwebsite.tld').get('/').reply(200)
19-
server.get('/proxy?uri=https://amazingwebsite.tld/')
18+
nock('https://example.org').get('/').reply(200)
19+
server.get('/proxy?uri=https://example.org/')
2020
.expect(200, done)
2121
})
2222

@@ -28,10 +28,21 @@ describe('proxy', () => {
2828
.end(done)
2929
})
3030

31-
it('should return 400 on local network requests', (done) => {
32-
nock('https://192.168.0.0').get('/').reply(200)
33-
server.get('/proxy?uri=https://192.168.0.0/')
34-
.expect('Cannot proxy https://192.168.0.0/')
31+
const LOCAL_IPS = ['127.0.0.0', '10.0.0.0', '172.16.0.0', '192.168.0.0']
32+
LOCAL_IPS.forEach(ip => {
33+
it(`should return 400 for a ${ip} address`, (done) => {
34+
nock(`https://${ip}`).get('/').reply(200)
35+
server.get(`/proxy?uri=https://${ip}/`)
36+
.expect(`Cannot proxy https://${ip}/`)
37+
.expect(400)
38+
.end(done)
39+
})
40+
})
41+
42+
it('should return 400 with a local hostname', (done) => {
43+
nock('https://nic.localhost').get('/').reply(200)
44+
server.get('/proxy?uri=https://nic.localhost/')
45+
.expect('Cannot proxy https://nic.localhost/')
3546
.expect(400)
3647
.end(done)
3748
})
@@ -51,7 +62,7 @@ describe('proxy', () => {
5162
})
5263

5364
it('should return the same headers of proxied request', (done) => {
54-
nock('https://amazingwebsite.tld')
65+
nock('https://example.org')
5566
.get('/')
5667
.reply(function (uri, req) {
5768
if (this.req.headers['accept'] !== 'text/turtle') {
@@ -64,7 +75,7 @@ describe('proxy', () => {
6475
}
6576
})
6677

67-
server.get('/proxy?uri=https://amazingwebsite.tld/')
78+
server.get('/proxy?uri=https://example.org/')
6879
.set('test', 'test1')
6980
.set('accept', 'text/turtle')
7081
.expect(200)
@@ -75,8 +86,8 @@ describe('proxy', () => {
7586
})
7687

7788
it('should also work on /proxy/ ?uri', (done) => {
78-
nock('https://amazingwebsite.tld').get('/').reply(200)
79-
server.get('/proxy/?uri=https://amazingwebsite.tld/')
89+
nock('https://example.org').get('/').reply(200)
90+
server.get('/proxy/?uri=https://example.org/')
8091
.expect((a) => {
8192
assert.equal(a.header['link'], null)
8293
})
@@ -87,31 +98,31 @@ describe('proxy', () => {
8798
async.parallel([
8899
// 500
89100
(next) => {
90-
nock('https://amazingwebsite.tld').get('/404').reply(404)
91-
server.get('/proxy/?uri=https://amazingwebsite.tld/404')
101+
nock('https://example.org').get('/404').reply(404)
102+
server.get('/proxy/?uri=https://example.org/404')
92103
.expect(404, next)
93104
},
94105
(next) => {
95-
nock('https://amazingwebsite.tld').get('/401').reply(401)
96-
server.get('/proxy/?uri=https://amazingwebsite.tld/401')
106+
nock('https://example.org').get('/401').reply(401)
107+
server.get('/proxy/?uri=https://example.org/401')
97108
.expect(401, next)
98109
},
99110
(next) => {
100-
nock('https://amazingwebsite.tld').get('/500').reply(500)
101-
server.get('/proxy/?uri=https://amazingwebsite.tld/500')
111+
nock('https://example.org').get('/500').reply(500)
112+
server.get('/proxy/?uri=https://example.org/500')
102113
.expect(500, next)
103114
},
104115
(next) => {
105-
nock('https://amazingwebsite.tld').get('/').reply(200)
106-
server.get('/proxy/?uri=https://amazingwebsite.tld/')
116+
nock('https://example.org').get('/').reply(200)
117+
server.get('/proxy/?uri=https://example.org/')
107118
.expect(200, next)
108119
}
109120
], done)
110121
})
111122

112123
it('should work with cors', (done) => {
113-
nock('https://amazingwebsite.tld').get('/').reply(200)
114-
server.get('/proxy/?uri=https://amazingwebsite.tld/')
124+
nock('https://example.org').get('/').reply(200)
125+
server.get('/proxy/?uri=https://example.org/')
115126
.set('Origin', 'http://example.com')
116127
.expect('Access-Control-Allow-Origin', 'http://example.com')
117128
.expect(200, done)

0 commit comments

Comments
 (0)