9.5 Node.js에서 비밀 설정 정보(Secrets) 관리

어플리케이션을 개발할 때, 외부에 알려지면 않되는 민감한 정보들를 사용할 때가 있다.
여기서 말하는 민감한 정보들이란 예를 들면 아래와 같은 것들이다.

  • 세션 키
  • 데이터베이스 접속을 위한 정보 (host, user, password…)

이런 정보들을 어떤 형태이든 코드내에 포함되어야 하는데 일반적으로 다음과 같은 방법이 사용된다.

  • 코드 내에 하드 코딩하기
  • 설정파일 사용하기
  • command-line에서 argument 요구하기
  • 환경변수 사용하기

그럼 각각의 장단점을 알아보자.

코드 내에 하드 코딩하기

데이터베이스에 접속하기 위해서는 host, user, password, database명 등의 정보가 필요하다. 물론 이 정보들은 외부에 알려지면 곤란한 정보들이다.

이하는 mysql 연동 모듈인 mysql의 Introduction에 소개된 예제이다.

// https://github.com/mysqljs/mysql#introduction

var mysql      = require('mysql');
var connection = mysql.createConnection({
  host     : 'localhost',
  user     : 'me',
  password : 'secret',
  database : 'my_db'
});

connection.connect();

connection.query('SELECT 1 + 1 AS solution', function(err, rows, fields) {
  if (err) throw err;

  console.log('The solution is: ', rows[0].solution);
});

connection.end();

이것은 모듈의 사용법을 설명하기 위한 단순한 예제이다.

실무에서 코드내에 설정 정보를 그대로 하드 코딩하는 것은 절대로 해서는 안된다. 그 이유는 아래와 같다.

  • 설정 정보가 변경되면 코드를 수정해야 한다. 물론 배포도 다시 해야 한다. 관심사의 분리(Separation of Concerns) 원칙을 적용하여 설정 정보와 코드는 분리되어야 한다.
  • github과 같은 SCM(source code management)를 사용하는 경우, 비밀 정보가 노출되므로 별도의 행위가 필요하다.

설정파일 사용하기

다음은 설정 정보를 JSON file에 저장해 두고 require()를 사용하여 필요 정보를 get하는 예제이다.

var mysql      = require('mysql');
var db_config  = require('./config/db-config.json');

var connection = mysql.createConnection({
  host     : db_config.host,
  user     : db_config.user,
  password : db_config.password,
  database : db_config.database
});
{
  "host": "localhost",
  "user": "me",
  "password": "secret",
  "database": "my_db"
}

코드 내에 하드 코딩하는 경우의 단점 중 관심사의 분리 문제는 해결되었다. 설정 정보가 코드에서 분리되었을 뿐이므로 SCM을 이용한 배포시에 주의해야 한다.

  • .gitignore file에 설정 파일(db-config.json. 해당폴더를 통째로 등록해 두는 것이 더 낮겠다.)을 추가하여 SCM에 저장하지 않는다.
  • 대신 db-config-sample.json과 같은 예제 파일을 등록하고 사용법을 명시한다.

그러나 설정 파일 내부에 비밀 정보가 존재하는 것은 여전히 문제로 남아있고 SCM 배포 시 특정 파일의 배포를 회피해야 하는 번거로움은 그대로이다.

command-line에서 argument 요구하기

비밀 정보를 보호하는 가장 좋은 방법은 설정 파일에 저장하지 않는 것이다.

nopt package를 사용하여 command-line에서 설정 정보를 argument로 요구하면 비밀 정보는 파일로 존재하지 않아도 된다.

아래는 nopt package를 사용하여 argument로 세션 키를 요구하는 예제이다.

// secret-arg.js

var nopt = require("nopt")

var longOpts = {
  "sessionSecret": String,
}

var shortOpts = {
  "s": ["--sessionSecret"],
}

var parsed = nopt(longOpts, shortOpts, process.argv, 2)

console.log("session secret is:", parsed.sessionSecret)
node secret-arg.js --sessionSecret "keyboard cat"
node secret-arg.js -s "keyboard cat"

설정 정보를 하드코딩하거나 설정 파일로 가지고 있는 것보다 비밀정보 노출에 보다 안전해졌다. 그러나 앱을 실행할 때마다 입력해야 할 정보의 양이 많아진 것은 번거로울 수 있다. script를 만들어 해결하려 한다면 설정 정보를 입력해 두면 매번 입력하는 수고를 덜 수 있겠지만 여전히 비밀정보가 script내에 존재하는 문제는 남게 된다.

환경변수(environment variable) 사용하기

OSX, Linux에서 환경변수를 설정할 때는 export를 사용하면 된다. 예를 들어 SESSION_SECRET key에 값을 설정해 보자

export SESSION_SECRET="keyboard cat"

Windows에서는 set을 사용하면 된다.

set SESSION_SECRET="keyboard cat"

실행할 때마다 환경변수를 설정할 수도 있다.

SESSION_SECRET="keyboard cat" node secret-env.js

코드 내에서 환경변수에 접근할 때에는 node.js의 process.env를 사용한다.

// https://github.com/expressjs/session 참고

var express = require('express')
var session = require('express-session')

var app = express()

app.use(session({secret: process.env.SESSION_SECRET}))

앞에서 살펴본 command-line에서 argument 요구하기와 비교하면 nopt 패키지를 별도로 사용하지 않아도 된다는 점과 매번 설정 정보를 입력하지 않아도 된다는 것은 좀더 개선된 방법으로 보인다. 여러분의 선택은 무엇인가?

Reference

Keeping secrets

Back to top
Close