초기화 함수 init(6)

10418 단어 JavaScriptJavaScript

trust proxy

  • express앱 앞에 프록시 서버를 두는경우와 관련되어 있음
  • express의 경우 req.ip, req.hostname, req.protocol등을 통하여 클라이언트의 정보를 받아올 수 있음
  • 프록시 서버를 설정한 경우 express입장에서는 해당 프록시 서버또한 클라이언트가 됨
  • trust proxy를 설정함으로써 프록시 서버의 정보(ip, hostname, protocol)를 가져오는것이 아니라, 실제 클라이언트의 정보를 가져올수 있음

테스트

테스트 조건

  • 80포트로 nginx를 동작시키고, 오는 모든 요청을 5000포트로 전달하였다.
brew install nginx
/usr/local/etc/nginx/nginx.conf 설정 파일 변경
brew services start nginx      

nginx.conf 설정 파일

worker_processes  1;

events {
    worker_connections 1024;
}

http {
    server {
        listen 8080;
        server_name localhost;

        location / {
            proxy_pass http://YOUR_DOMAIN:YOUR_PORT;
        }
    }
}

https://kirillplatonov.com/2017/11/12/simple_reverse_proxy_on_mac_with_nginx/ 참고

  • 5000포트에서 express를 작동시키고 req.ip, req.protocol, req.hostname, req.headers를 확인하였다.

테스트 결과

  • trust proxy를 설정하지 않은경우
const express = require("express");

const app = express();

app.get("/", (req, res) => {
  console.log(req.ip);
  console.log(req.protocol);
  console.log(req.hostname);
  console.log(req.headers);
  res.send("hello");
});

app.listen(5000);
  • nginx(80포트)를 통한 접근
::ffff:127.0.0.1 (req.ip)
http (req.protocol)
0.0.0.0 (req.hostname)
{ (req.headers)
  host: '0.0.0.0:5000',
  connection: 'close',
  'upgrade-insecure-requests': '1',
  accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
  'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Mobile/15E148 Safari/604.1',
  'accept-language': 'ko-kr',
  'accept-encoding': 'gzip, deflate'
}
  • express(5000포트)를 통한 접근
::ffff:192.168.206.153 (req.ip)
http (req.protocol)
192.168.206.141 (req.hostname)
{ (req.headers)
  host: '192.168.206.141:5000',
  'upgrade-insecure-requests': '1',
  accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
  'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Mobile/15E148 Safari/604.1',
  'accept-language': 'ko-kr',
  'accept-encoding': 'gzip, deflate',
  connection: 'keep-alive'
}
  • trust proxy를 설정한 경우
const express = require("express");

const app = express();

app.set("trust proxy", true);

app.get("/", (req, res) => {
  console.log(req.ip);
  console.log(req.protocol);
  console.log(req.hostname);
  console.log(req.headers);
  res.send("hello");
});

app.listen(5000);
  • nginx(80포트)를 통한 접근
::ffff:127.0.0.1 (req.ip)
http (req.protocol)
0.0.0.0 (req.hostname)
{ (req.headers)
  host: '0.0.0.0:5000',
  connection: 'close',
  'upgrade-insecure-requests': '1',
  accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
  'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Mobile/15E148 Safari/604.1',
  'accept-language': 'ko-kr',
  'accept-encoding': 'gzip, deflate'
}
  • express(5000포트)를 통한 접근
::ffff:192.168.206.153 (req.ip)
http (req.protocol)
192.168.206.141 (req.hostname)
{ (req.headers)
  host: '192.168.206.141:5000',
  'upgrade-insecure-requests': '1',
  accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
  'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Mobile/15E148 Safari/604.1',
  'accept-language': 'ko-kr',
  'accept-encoding': 'gzip, deflate',
  connection: 'keep-alive'
}

문제점

예측

  • nginx가 X-Forwarded-For등의 헤더를 통하여 실제 클라이언트의 정보를 전달
  • express가 trust proxy설정 여부에 따라 X-Forwarded-For등의 헤더있는 정보를 이용하여 실제 클라이언트의 정보를 설정

실제

  • X-Forwarded-For등의 헤더를 nginx가 express에 전달하지 않음
  • X-Forwarded-For등의 헤더가 설정되어 있지 않으므로 trust proxy설정은 무의미함

해결

  • nginx.conf에 설정 추가
 proxy_set_header        X-Real-IP       $remote_addr;
 proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
 proxy_set_header        X-Forwarded-Proto $scheme;
 proxy_set_header        X-Forwarded-Host  $host;
 proxy_set_header        X-Forwarded-Port  $server_port;
  • nginx(80포트)를 통한 접근
192.168.206.153 (req.ip)
http (req.protocol)
192.168.206.141 (req.hostname)
{ (req.headers)
  'x-real-ip': '192.168.206.153',
  'x-forwarded-for': '192.168.206.153',
  'x-forwarded-proto': 'http',
  'x-forwarded-host': '192.168.206.141',
  'x-forwarded-port': '80',
  host: '0.0.0.0:5000',
  connection: 'close',
  'upgrade-insecure-requests': '1',
  accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
  'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Mobile/15E148 Safari/604.1',
  'accept-language': 'ko-kr',
  'accept-encoding': 'gzip, deflate'
}
  • express(5000포트)를 통한 접근
::ffff:192.168.206.153 (req.ip)
http (req.protocol)
192.168.206.141 (req.hostname)
{ (req.headers)
  host: '192.168.206.141:5000',
  'upgrade-insecure-requests': '1',
  accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
  'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Mobile/15E148 Safari/604.1',
  'accept-language': 'ko-kr',
  'accept-encoding': 'gzip, deflate',
  connection: 'keep-alive'
}
  • trust proxy를 문자열(ip주소), 숫자(hopcount)등으로도 설정이 가능함
    -> app.set("trust proxy", "127.0.0.1");
    -> app.set("trust proxy", 0);

테스트를 통하여 배운점

  • 검색을 통해 확인한 정보를 검증하였다.
  • 실제 어떤식으로 클라이언트의 정보를 가져오는지 확인할 수 있었다.

X-Forwarded-For

  • 사실상의 표준 헤더
  • HTTP 프록시 또는 로드 밸런서 사용시 실제 클라이언트의 ip주소를 확인하기 위하여 사용한다.
X-Forwarded-For: <client>, <proxy1>, <proxy2>

https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For 참고

코드 분석

lib/application

app.defaultConfiguration = function defaultConfiguration() {
  var env = process.env.NODE_ENV || "development";

  // default settings
  this.enable("x-powered-by");
  this.set("etag", "weak");
  this.set("env", env);
  this.set("query parser", "extended");
  this.set("subdomain offset", 2);
  this.set("trust proxy", false);

  // trust proxy inherit back-compat
  Object.defineProperty(this.settings, trustProxyDefaultSymbol, {
    configurable: true,
    value: true,
  });

  /**
   * this.set("trust proxy", false)설정시 @@symbol:trust_proxy_default : false로 설정이 되는데 다시 값을 설정하는 이유
   *
   * 기본 값이 세팅이 된 경우(app.init()에 의해 설정된 값)와 사용자가 임의로 설정(app.set("trust proxy", true))한 경우를 구분하기 위한것으로 추정됨
   *
   * 기본 값으로 설정된 경우  @@symbol:trust_proxy_default : true 이며
   * 그 이외의 경우에서 "trust proxy"를 설정한 경우
   * 굳이 "trust proxy"값을 바꿀때 @@symbol:trust_proxy_default의 값을 바꾸지 않는 한 @@symbol:trust_proxy_default : false가 설정이 됨
   *
   */

  debug("booting in %s mode", env);

  this.on("mount", function onmount(parent) {
    // inherit trust proxy
    if (
      this.settings[trustProxyDefaultSymbol] === true &&
      typeof parent.settings["trust proxy fn"] === "function"
    ) {
      delete this.settings["trust proxy"];
      delete this.settings["trust proxy fn"];
    }

    // inherit protos
    setPrototypeOf(this.request, parent.request);
    setPrototypeOf(this.response, parent.response);
    setPrototypeOf(this.engines, parent.engines);
    setPrototypeOf(this.settings, parent.settings);
  });

  // setup locals
  this.locals = Object.create(null);

  // top-most app is mounted at /
  this.mountpath = "/";

  // default locals
  this.locals.settings = this.settings;

  // default configuration
  this.set("view", View);
  this.set("views", resolve("views"));
  this.set("jsonp callback name", "callback");

  if (env === "production") {
    this.enable("view cache");
  }

  Object.defineProperty(this, "router", {
    get: function () {
      throw new Error(
        "'app.router' is deprecated!\nPlease see the 3.x to 4.x migration guide for details on how to update your app."
      );
    },
  });
};

lib/utils/compeilTrust

/**
 * val이 true인 경우 항상 true를 리턴하는 함수를 리턴하고
 * 숫자인 경우 설정된 hopcount보다 낮은경우 true를 리턴하는 함수를
 * 문자열이고 "127.0.0.1, 192.168.0.1"형식의 여러 ip주소가 들어오는 경우
 * 문자열을 나눈다.
 */

exports.compileTrust = function (val) {
  if (typeof val === "function") return val;

  if (val === true) {
    // Support plain true/false
    return function () {
      return true;
    };
  }

  if (typeof val === "number") {
    // Support trusting hop count
    return function (a, i) {
      return i < val;
    };
  }

  if (typeof val === "string") {
    // Support comma-separated values
    val = val.split(/ *, */);
  }

  return proxyaddr.compile(val || []);
  /**
   * val이 undefined, null, false인 경우 빈 배열을 넘기고
   * 그 이외의 경우 val을 넘겨준다.
   *
   * val: undefined || false || null
   * -> return () => false;
   * 그 이외의 경우 입력돤 ip주소가 val을 통하여 입력된 ip주소와 비교하여 그 결과를 리턴하는 함수를 리턴한다.
   */
};

좋은 웹페이지 즐겨찾기