개요

SSL 요청 중 발생할 수 있는 self signed certificate 에러의 발생 원인과 Node.js의 Axios를 통한 해결방법을 공유해보려 합니다. 

self signed certificate(자체 서명 인증서) 에러 발생 원인에 대해서는 python이든, 타언어에서도 동일한 원인으로 발생을 하기 기본 베이스 지식으로 알아가면 좋지 않을까 합니다.

Self-signed certificate 에러 발생 원인

일반적인 SSL/TLS 통신은 기본적으로 비대칭키(공개키) 기반의 암호화 방법을 이용하여 SSL handshaking을 합니다. 

큰 맥락의 절차는 아래와 같습니다.

  1. 브라우저(사용자)가 서비스(서버)로 요청을 보냄.
  2. 서비스(서버)는 공개키를 브라우저(사용자)에게 내려주고, 
  3. 브라우저(사용자)는 공개키로 pre master secret key(대칭키에 이용)를 암호화해서 서비스(서버)로 전송
  4. 서비스(서버)는 개인키로 복호화하여 pre master secret key 추출
  5. 최종적으로 공유된 대칭키로 실데이터 암호화 통신

이 과정에서, 브라우저는 공개키가 실제 서비스(서버)에서 온 것이 맞는지 보증을 할 수 있어야 합니다. 보증을 할 수 없으면 해커의 공개키에 의해 MITM 공격을 당하고 있는 것일 수도 있으니까요.

이것을 방지하기 위해서 브라우저는 신뢰할 수 있는 인증기관(CA)에서 발급된 서비스(서버)의 인증서(공개키포함)임을 보증하기 위해 내부적으로 일련의 검증 과정을 거칩니다. 브라우저가 알고 있는 신뢰할 수 있는 인증기관(CA)에서 발급된 인증서인지? / 인증서 내용을 통해 내가 안전하게 통신하려는 서비스(서버)가 실 소유주가 맞는지? 를요.

일반적인 브라우저를 통한 SSL/TLS 통신에서 위와 같은 과정이 이루어 지며, 이 외에 nodejs 나 python등 백엔드 프로그램에서의 https request 에서도 동일하게 인증서 검증을 하게 됩니다.

그러나, 만약 서버에서 그냥 별도로 테스트를 위해서 자체 서명 인증서를 만들어서 사용하거나, 인증서가 만료되었거나하는 이유로 인증서 검증이 되지않을 경우 self signed certificate 에러를 발생 시키게 됩니다.

Nodejs 에서 발생되는 예제 코드와 에러 구문입니다.

const axios = require("axios");

// https request 요청(SSL/TLS)
// 10.10.10.100 서버는 CA에 의해 검증되지 않은 자체 서명 인증서 사용 중일 경우
const host = "https://10.10.10.100";

const getInfo = () => {
  const url = `${host}/info`;
  const options = {
    method: "get",
    headers: {
      "User-Agent": "test",
    },
  };
  axios(url, options)
    .then((res) => console.log(res.data));
};

getInfo();

https 요청을 받는 host 서버가 자체 서명 인증서 사용하고 있는 경우, 아래와 같은 에러가 발생합니다.

(node:77462) UnhandledPromiseRejectionWarning: Error: unable to verify the first certificate
    at TLSSocket.onConnectSecure (_tls_wrap.js:1497:34)
    at TLSSocket.emit (events.js:315:20)
    at TLSSocket._finishInit (_tls_wrap.js:932:8)
    at TLSWrap.ssl.onhandshakedone (_tls_wrap.js:706:12)

해결 방법

Nodejs에서 axios를 사용할 때, 해결 방법은 2가지 입니다.

첫번째 방법은 rejectUnauthorized 를 false로 설정 하는 방법입니다.

const axios = require("axios");
const https = require("https");

const host = "https://10.10.10.100";

const getInfo = () => {
  const url = `${host}/info`;
  const options = {
    method: "get",
    headers: {
      "User-Agent": "test",
    },
    httpsAgent: new https.Agent({
      rejectUnauthorized: false, //허가되지 않은 인증을 reject하지 않겠다!
    }),
  };
  axios(url, options)
    .then((res) => console.log(res.data));
};

getInfo();

두번째 방법은 process.env로 환경변수를 설정하는 방법입니다.

const axios = require("axios");
const https = require("https");

// 환경변수로 node에서 허가되지 않은 인증TLS통신을 거부하지 않겠다고 설정
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';

const host = "https://10.10.10.100";

const getInfo = () => {
  const url = `${host}/info`;
  const options = {
    method: "get",
    headers: {
      "User-Agent": "test",
    },
  };
  axios(url, options)
    .then((res) => console.log(res.data));
};

getInfo();

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

Back To Top