소스 검색

improve private ip check (#3403)

https://huntr.dev/bounties/c1c03ef6-3f18-4976-a9ad-08c251279122/
Mikael Finstad 3 년 전
부모
커밋
fc137e30a2
3개의 변경된 파일14개의 추가작업 그리고 128개의 파일을 삭제
  1. 1 1
      packages/@uppy/companion/package.json
  2. 10 74
      packages/@uppy/companion/src/server/helpers/request.js
  3. 3 53
      yarn.lock

+ 1 - 1
packages/@uppy/companion/package.json

@@ -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",

+ 10 - 74
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
 
@@ -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
       }
@@ -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
     }
@@ -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
     }

+ 3 - 53
yarn.lock

@@ -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
@@ -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"
@@ -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"
@@ -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"
@@ -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"
@@ -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
@@ -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"
@@ -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"
@@ -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