Skip to content

Commit

Permalink
improve private ip check (#3403)
Browse files Browse the repository at this point in the history
  • Loading branch information
mifi committed Jan 4, 2022
1 parent 6aa7553 commit fc137e3
Show file tree
Hide file tree
Showing 3 changed files with 14 additions and 128 deletions.
2 changes: 1 addition & 1 deletion packages/@uppy/companion/package.json
Expand Up @@ -45,7 +45,7 @@
"express-session": "1.17.1",
"grant": "4.7.0",
"helmet": "^4.6.0",
"ip-address": "6.2.0",
"ipaddr.js": "^2.0.1",
"isobject": "3.0.1",
"jsonwebtoken": "8.5.1",
"lodash.merge": "^4.6.2",
Expand Down
84 changes: 10 additions & 74 deletions packages/@uppy/companion/src/server/helpers/request.js
@@ -1,83 +1,19 @@
// eslint-disable-next-line max-classes-per-file
const http = require('http')
const https = require('https')
const { URL } = require('url')
const dns = require('dns')
const ipAddress = require('ip-address')
const request = require('request')
const ipaddr = require('ipaddr.js')

const logger = require('../logger')

const FORBIDDEN_IP_ADDRESS = 'Forbidden IP address'

function isIPAddress (address) {
const addressAsV6 = new ipAddress.Address6(address)
const addressAsV4 = new ipAddress.Address4(address)
return addressAsV6.isValid() || addressAsV4.isValid()
}

/* eslint-disable max-len */
/**
* Determine if a IP address provided is a private one.
* Return TRUE if it's the case, FALSE otherwise.
* Excerpt from:
* https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html#case-2---application-can-send-requests-to-any-external-ip-address-or-domain-name
*
* @param {string} ipAddress the ip address to validate
* @returns {boolean}
*/
/* eslint-enable max-len */
function isPrivateIP (ipAddress) {
let isPrivate = false
// Build the list of IP prefix for V4 and V6 addresses
const ipPrefix = []
// Add prefix for loopback addresses
ipPrefix.push('127.')
ipPrefix.push('0.')
// Add IP V4 prefix for private addresses
// See https://en.wikipedia.org/wiki/Private_network
ipPrefix.push('10.')
ipPrefix.push('172.16.')
ipPrefix.push('172.17.')
ipPrefix.push('172.18.')
ipPrefix.push('172.19.')
ipPrefix.push('172.20.')
ipPrefix.push('172.21.')
ipPrefix.push('172.22.')
ipPrefix.push('172.23.')
ipPrefix.push('172.24.')
ipPrefix.push('172.25.')
ipPrefix.push('172.26.')
ipPrefix.push('172.27.')
ipPrefix.push('172.28.')
ipPrefix.push('172.29.')
ipPrefix.push('172.30.')
ipPrefix.push('172.31.')
ipPrefix.push('192.168.')
ipPrefix.push('169.254.')
// Add IP V6 prefix for private addresses
// See https://en.wikipedia.org/wiki/Unique_local_address
// See https://en.wikipedia.org/wiki/Private_network
// See https://simpledns.com/private-ipv6
ipPrefix.push('fc')
ipPrefix.push('fd')
ipPrefix.push('fe')
ipPrefix.push('ff')
ipPrefix.push('::1')
// Verify the provided IP address
// Remove whitespace characters from the beginning/end of the string
// and convert it to lower case
// Lower case is for preventing any IPV6 case bypass using mixed case
// depending on the source used to get the IP address
const ipToVerify = ipAddress.trim().toLowerCase()
// Perform the check against the list of prefix
for (const prefix of ipPrefix) {
if (ipToVerify.startsWith(prefix)) {
isPrivate = true
break
}
}

return isPrivate
}
// Example scary IPs that should return false (ipv6-to-ipv4 mapped):
// ::FFFF:127.0.0.1
// ::ffff:7f00:1
const isDisallowedIP = (ipAddress) => ipaddr.parse(ipAddress).range() !== 'unicast'

module.exports.FORBIDDEN_IP_ADDRESS = FORBIDDEN_IP_ADDRESS

Expand Down Expand Up @@ -115,7 +51,7 @@ function dnsLookup (hostname, options, callback) {

const toValidate = Array.isArray(addresses) ? addresses : [{ address: addresses }]
for (const record of toValidate) {
if (isPrivateIP(record.address)) {
if (isDisallowedIP(record.address)) {
callback(new Error(FORBIDDEN_IP_ADDRESS), addresses, maybeFamily)
return
}
Expand All @@ -127,7 +63,7 @@ function dnsLookup (hostname, options, callback) {

class HttpAgent extends http.Agent {
createConnection (options, callback) {
if (isIPAddress(options.host) && isPrivateIP(options.host)) {
if (ipaddr.isValid(options.host) && isDisallowedIP(options.host)) {
callback(new Error(FORBIDDEN_IP_ADDRESS))
return undefined
}
Expand All @@ -138,7 +74,7 @@ class HttpAgent extends http.Agent {

class HttpsAgent extends https.Agent {
createConnection (options, callback) {
if (isIPAddress(options.host) && isPrivateIP(options.host)) {
if (ipaddr.isValid(options.host) && isDisallowedIP(options.host)) {
callback(new Error(FORBIDDEN_IP_ADDRESS))
return undefined
}
Expand Down
56 changes: 3 additions & 53 deletions yarn.lock
Expand Up @@ -8100,7 +8100,7 @@ __metadata:
grant: 4.7.0
helmet: ^4.6.0
into-stream: ^6.0.0
ip-address: 6.2.0
ipaddr.js: ^2.0.1
isobject: 3.0.1
jsonwebtoken: 8.5.1
lodash.merge: ^4.6.2
Expand Down Expand Up @@ -23125,21 +23125,6 @@ hexo-filter-github-emojis@arturi/hexo-filter-github-emojis:
languageName: node
linkType: hard

"ip-address@npm:6.2.0":
version: 6.2.0
resolution: "ip-address@npm:6.2.0"
dependencies:
jsbn: 1.1.0
lodash.find: 4.6.0
lodash.max: 4.0.1
lodash.merge: 4.6.2
lodash.padstart: 4.6.1
lodash.repeat: 4.1.0
sprintf-js: 1.1.2
checksum: bcb93b4b13b3bf17d033fafce45850bf14c56366659ab627d3adbcd7a87ec64e219e7c6dc5cf990dddff5ac59db76520f2b1c7b5b05ea8d069162ca8a5112e44
languageName: node
linkType: hard

"ip-regex@npm:^2.1.0":
version: 2.1.0
resolution: "ip-regex@npm:2.1.0"
Expand Down Expand Up @@ -25047,13 +25032,6 @@ hexo-filter-github-emojis@arturi/hexo-filter-github-emojis:
languageName: node
linkType: hard

"jsbn@npm:1.1.0":
version: 1.1.0
resolution: "jsbn@npm:1.1.0"
checksum: 944f924f2bd67ad533b3850eee47603eed0f6ae425fd1ee8c760f477e8c34a05f144c1bd4f5a5dd1963141dc79a2c55f89ccc5ab77d039e7077f3ad196b64965
languageName: node
linkType: hard

"jsbn@npm:~0.1.0":
version: 0.1.1
resolution: "jsbn@npm:0.1.1"
Expand Down Expand Up @@ -26431,13 +26409,6 @@ hexo-filter-github-emojis@arturi/hexo-filter-github-emojis:
languageName: node
linkType: hard

"lodash.find@npm:4.6.0":
version: 4.6.0
resolution: "lodash.find@npm:4.6.0"
checksum: b737f849a4fe36f5c3664ea636780dda2fde18335021faf80cdfdcb300ed75441da6f55cfd6de119092d8bb2ddbc4433f4a8de4b99c0b9c8640465b0901c717c
languageName: node
linkType: hard

"lodash.flatten@npm:^4.4.0":
version: 4.4.0
resolution: "lodash.flatten@npm:4.4.0"
Expand Down Expand Up @@ -26543,13 +26514,6 @@ hexo-filter-github-emojis@arturi/hexo-filter-github-emojis:
languageName: node
linkType: hard

"lodash.max@npm:4.0.1":
version: 4.0.1
resolution: "lodash.max@npm:4.0.1"
checksum: f887b68db054edabe3a4f4877a7b7d0bef6e4a29e9371ea4d711a6ed0ca2393659a879158c609649aabd0337528ec58ed5b1e9581fed536ec0b8ae45a6e5f278
languageName: node
linkType: hard

"lodash.memoize@npm:4.1.2, lodash.memoize@npm:^4.1.2":
version: 4.1.2
resolution: "lodash.memoize@npm:4.1.2"
Expand All @@ -26564,7 +26528,7 @@ hexo-filter-github-emojis@arturi/hexo-filter-github-emojis:
languageName: node
linkType: hard

"lodash.merge@npm:4.6.2, lodash.merge@npm:^4.6.1, lodash.merge@npm:^4.6.2":
"lodash.merge@npm:^4.6.1, lodash.merge@npm:^4.6.2":
version: 4.6.2
resolution: "lodash.merge@npm:4.6.2"
checksum: ad580b4bdbb7ca1f7abf7e1bce63a9a0b98e370cf40194b03380a46b4ed799c9573029599caebc1b14e3f24b111aef72b96674a56cfa105e0f5ac70546cdc005
Expand All @@ -26585,13 +26549,6 @@ hexo-filter-github-emojis@arturi/hexo-filter-github-emojis:
languageName: node
linkType: hard

"lodash.padstart@npm:4.6.1":
version: 4.6.1
resolution: "lodash.padstart@npm:4.6.1"
checksum: 0d6ad92c626d351db85de539e41df3238d7d36c5fbfc5f57c4f060c90c73ad9f1db566463487795fdf0bf290a8f133189a0bd91d051032f6eb2d15b7e1863b5e
languageName: node
linkType: hard

"lodash.pick@npm:^4.4.0":
version: 4.4.0
resolution: "lodash.pick@npm:4.4.0"
Expand All @@ -26606,13 +26563,6 @@ hexo-filter-github-emojis@arturi/hexo-filter-github-emojis:
languageName: node
linkType: hard

"lodash.repeat@npm:4.1.0":
version: 4.1.0
resolution: "lodash.repeat@npm:4.1.0"
checksum: dac15fc59ed783678e1a9f986fefa180bfdbf95280852165965ecc8e15b871c6f0eaf7b325768a176014594d5186f1d6558fb72a18527bddd82539fb3ef8a4d3
languageName: node
linkType: hard

"lodash.set@npm:^4.3.2":
version: 4.3.2
resolution: "lodash.set@npm:4.3.2"
Expand Down Expand Up @@ -37701,7 +37651,7 @@ hexo-filter-github-emojis@arturi/hexo-filter-github-emojis:
languageName: node
linkType: hard

"sprintf-js@npm:1.1.2, sprintf-js@npm:^1.0.3, sprintf-js@npm:^1.1.2":
"sprintf-js@npm:^1.0.3, sprintf-js@npm:^1.1.2":
version: 1.1.2
resolution: "sprintf-js@npm:1.1.2"
checksum: d4bb46464632b335e5faed381bd331157e0af64915a98ede833452663bc672823db49d7531c32d58798e85236581fb7342fd0270531ffc8f914e186187bf1c90
Expand Down

0 comments on commit fc137e3

Please sign in to comment.