diff --git a/config.js b/config.js index c5c8e50..a8ffa56 100644 --- a/config.js +++ b/config.js @@ -1,15 +1,26 @@ +const MATCH_EVERYTHING_STRING = '.*'; const env = process.env.APP__ENV_NAME || "local"; const isLocal = env === "local" || env === "test"; const strippedPath = process.env.APP__STRIPPED_PATH || ''; +const hostsWhitelistRegex = (() => { + try { + return new RegExp(process.env.APP__HOSTS_WHITELIST_REGEX || MATCH_EVERYTHING_STRING); + } catch (e) { + console.error(`APP__HOSTS_WHITELIST_REGEX=${process.env.APP__HOSTS_WHITELIST_REGEX} cannot be converted to RegExp:`, e, '\nUsing the default.'); + return new RegExp(MATCH_EVERYTHING_STRING); + } +})(); console.log("Environment variables:"); console.log(`APP__STRIPPED_PATH=${strippedPath} (path added to original host in analytics scripts)`); console.log(`APP__ENV_NAME=${env} (should not be local nor test in production)`); +console.log(`APP__HOSTS_WHITELIST_REGEX=${hostsWhitelistRegex}${hostsWhitelistRegex.toString() === `/${MATCH_EVERYTHING_STRING}/` ? ' (YAY!! Anyone can use your proxy!)' : ''}`); export default { isLocalEnv: isLocal, httpPort: process.env.PORT || 80, strippedPath, + hostsWhitelistRegex: hostsWhitelistRegex, proxyDomain: "", // Domain to proxy calls through. Leave it empty to use the requested domain as a proxy domain proxy: { // Proxy configuration is here domains: [ // These domains are replaced in any proxied response (including scripts, URLs and redirects) diff --git a/readme.md b/readme.md index 35a7cac..59a8821 100644 --- a/readme.md +++ b/readme.md @@ -95,8 +95,17 @@ APP__STRIPPED_PATH=/gtm-proxy # reaching analytics-saviour so that next front end requests land to the same prefixed path # on your domain e.g. example.com/gtm-proxy/*(d3d3Lmdvb2dsZS1hbmFseXRpY3MuY29t)*/collect?.. # Because of this, she path you strip must be explicitly provided. + APP__ENV_NAME=local # APP__ENV_NAME=local or APP__ENV_NAME=test (default) will display static content from `static-test`. + +APP__HOSTS_WHITELIST_REGEX="^(example\\.com|mysecondwebsite\\.com)$" +# A JavaScript regular expression that the host must match. By default, it matches ANY HOST, MAKING +# YOUR PROXY AVAILABLE TO ANYONE. Make sure you screen all special regexp characters here. Examples: +# APP__HOSTS_WHITELIST_REGEX="^example\\.com$" (only the domain example.com is allowed to access the proxy) +# APP__HOSTS_WHITELIST_REGEX="^.*\\.example\\.com$" (only subdomains of example.com are allowed) +# APP__HOSTS_WHITELIST_REGEX="^.*\\.?example\\.com$" (example.com and all its subdomains are allowed) +# APP__HOSTS_WHITELIST_REGEX="^(example\\.com|mysecondwebsite\\.com)$" (multiple specified domains are allowed) ``` ### NodeJS Application diff --git a/src/proxy/configured-domains.js b/src/proxy/configured-domains.js index 5300ff4..69c324e 100644 --- a/src/proxy/configured-domains.js +++ b/src/proxy/configured-domains.js @@ -1,6 +1,7 @@ import { createDefaultProxy } from "../modules/proxy"; import config from "../../config"; import { unmask } from "../modules/mask"; +import { info } from "../logger"; const domains = new Set(config.proxy.domains); const proxies = new Map(Array.from(domains).map((domain) => { @@ -11,6 +12,12 @@ const proxies = new Map(Array.from(domains).map((domain) => { export function enableDefaultProxy (expressApp) { expressApp.use("/", (req, res, next) => { + if (!req.headers || !req.headers.host || !config.hostsWhitelistRegex.test(req.headers.host)) { + info(`FORBIDDEN: proxy request from host "${req.headers.host}" was canceled as it doesn't match with ${config.hostsWhitelistRegex}.`); + return res.status(403).send({ + error: `Requests from host ${req.headers.host} are restricted.` + }); + } const domain = unmask(req.url.split(/\//g)[1]); return domains.has(domain) ? proxies.get(domain)(req, res, next) // Use proxy for configured domains