Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve private ip check #3403

Merged
merged 1 commit into from Jan 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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