[MongoDB] 보안

2020. 12. 20. 00:59Database/MongoDB

1. 보안

  • 인증
  • 권한
  • 암호화
  • 감사
  • 데이터 관리

 

 

 

2.  인증

MongoDB 인증은 크게 자체 인증 방식과 외부 인증 방식으로 나눌 수 있다. 외부 인증은 MongoDB 서버 외부의 LDAP나 액티브 디렉토리를 이용해 사용자 인증을 받는 방식을 의미한다. 물론 외부 인증 방식은 별도의 솔루션이 필요하고 장애의 범위가 넓어지기 때문에 자주 사용되지는 않는다. MongoDB의 외부 인증 방식은 엔터프라이즈 버전에서만 제공되며, 사용자 인증이 반드시 필요하다면 Percona에서 배포되는 Percona Server for MongoDB를 이용하여 SASL을 이용한 OpenLDAP 또는 액티브 디렉토리 서비스를 구축할 수 있다.

 

내부 인증은 Sharding된 Cluster나 레플리카 셋의 각 멤버들끼리의 통신에서 이루어지는 서로간의 인증 방식이다. MongoDB 서버에서 내부 인증은 keyFile과 X.509 인증서 두 가지 방식을 선택해서 사용할 수 있다. keyFile은 암호화되지 않은 평문의 단순 문자열로 구성된 비밀번호 파일을 MongoDB 서버가 내부 인증으로 사용하도록 하는 방식이며, X.509는 CA의 인증을 받는 인증서를 이용해서 Cluster 멤버 간 통신을 인증하는 방식이다. 인증을 활성화하려면 서버의 설정파일에서 인증과 관련된 옵션을 활성화해야한다.

security:
  authorization: enabled
  clusterAuthMode: keyFile # 혹은 x.509
  keyFile: /etc/mongod.key

※ 3.4 버전부터는 sendKeyFile, sendX509 옵션도 사용할 수 있는데, 이는 keyFile의 내부 인증 방식을 x.509로 변경하고자 할 때 필요한 옵션이다.

 

keyFile의 생성시 제약 사항은 다음과 같다.

  • keyFile은 MongoDB 서버 프로세스가 읽을 수 있어야 한다.
  • keyFile의 접근 구너한은 반드시 600 혹은 700으로 파일의 소유자만 접근할 수 있어야 한다.
  • keyFile의 내용에서 공백 문자는 자동으로 무시된다.
  • keyFile은 6개 이상 1024개 이하의 문자로 구성해야하며 BASE-64 셋에 포함되는 문자만 사용할 수 있다.

마지막으로 MongoDB 서버 자체적으로 지원하는 사용자 인증은 다른 DBMS와 같이 아이디/패스워드 기반의 인증을 사용한다. MongoDB 서버는 인증 데이터베이스 정보를 추가로 더 필요로 한다. MongoDB 서버에서는 사용자를 생성할 때 반드시 특정 데이터베이스 정보를 추가로 더 필요로 한다. 이 때 데이터베이스를 '인증 데이터베이스'라고 한다. 물론 하나의 계정이 여러 다른 데이터베이스에 대해서 권한을 가질 수 있지만 인증 데이터 베이스는 사용자 계정으로 로그인 ㅎ라 때 인증을 위한 데이터베이스이므로 하나만 가질 수 있다. 사용자 인증을 활성화하는 방법은 다음과 같다.

security:
  authorization: enabled

설정 파일의 인증 옵션이 enabled일 경우, MongoDB 서버를 시작하면 MongoDB 서버는 아무런 관리자나 사용자 계정을 가지지 않은 상태로 시작된다. 이렇게 사용자 계정 정보가 비어있는 상태에서는 서버에 익명으로 로그인해서 단 하나의 사용자 계정을 생성할 수 있도록 구현되어있다. 따라서 MongoDB 서버가 처음 시작되면 제일 먼저 익명으로 로그인한 후 모든 권한을 가지는 관리자 계정을 생성해야한다.

use admin
db.createUser({ user: "dba", pwd: "dbapassword", roles: ["root"] })

다음은 특정 데이터베이스에 user 계정이 읽고 쓰기 권한을 부여하는 예제이다. 

use mysns
db.createUser({ user: "user", pwd: "mypassword", roles: ["readWrite"] })

만약 하나의 사용자 계정이 여러 데이터베이스에 대해서 권한을 가지도록 한다면 createUser() 메서드에 다른 데이터베이스에 대한 권한을 같이 설정하거나 grantRolesToUser() 메서드를 이용하여 권한을 추가해주면 된다.

// 추가 권한 설정
use mysns
db.createUser({ user: "user", pwd: "mypassword",
                roles: [ "readWrite", { role: "readWrite", db: "myblog" } ] })
                
// grantRoleToUser() 방법
use mysns
db.createUser({ user: "user", pwd: "mypassword", roles: ["readWrite" ] })
db.grantRolesToUser("user", [{ role: "readWrite", db:"myblog" }] )            

생성된 사용자 계정의 비밀번호와 권한 정보는 인증 데이터베이스와 무관하게 admin 데이터 베이스에 저장된다. 확인하는 방법은 다음과 같다.

use admin
db.system.user.find().pretty()

 

 

 

3. 권한

기존 RDBMS에는 실행 명령어 단위(SELECT, INSERT, UPDATE, DELETE, ...)로 권한이 부여되는 경우가 많다. 반면 MongoDB는 역할 기반으로 권한 부여 방식을 사용하는데, 역할은 특정 리소스에 대해서 액션을 미치 매핑해 둔 것이다. MongoDB 서버에는 매우 다양한 액션이 이미 정의(메뉴얼 참고)되어 있다.

MongoDB 액션

MongoDB 서버의 역할은 이렇게 정의된 액션을 묶어서 하나의 그룹으로 만든 것으로 볼 수 있다. 위의 예시처럼 특정 사용자에게 'readWrite' 권한을 부여해준 것 처럼, MongoDB 서버에서 사용자 계정을 생성하고 사용자별로 권한을 할당할 때는 액션의 모음인 역할로 부여한다.

 

MongoDB 서버에는 이미 많은 역할이 미리 정의되어 있지만, 사용자가 자신의 서비스나 요건에 맞게 새로운 역할을 정의해서 사용할 수 있다. 역할은 createRole() 메서드를 이요하여 생성하며, 생성된 사용자ㅏ 역할에 새로운 액션을 추가하고 제거하는 작업은 grantPrivilegesToRole() 메서드와 revokePrivilegesFromRole() 메서드를 사용한다.

 

예를 들어 mysns 데이터베이스에 find와 update 그리고 insert와 delete 액션이 가능하고 myblog 데이터베이서에 대해서는 find 액션이 가능한 dev_mysns 역할 생성 방법은 다음과 같다.

use admin
db.createRole({
  role: "dev_mysns",
  privileges: [
    { resource: { db: "mysns", collection: "" }, actions: [ "find", "update", "insert", "remove" ] },
    { resource: { db: "myblog", collection: "" }, actions: [ "find" ] }
    ],
    roles: []
})

MongoDB에서 이미 내장된 역할을 이용하여 mysns 데이터베이스에 대해 읽고 쓰기가 가능하며, myblog 데이터베이스에 대해 읽기 권한을 가지는 dev_mysns 역할 생성하는 방법은 다음과 같다.

use admin
db.createRole({
  role: "dev_mysns",
  privileges: [],
    roles: [
      { role: "readWrite", db: "mysns" },
      { role: "read", db: "myblog" }
    ]
})

생성한 사용자 정의 역할은 admin 데이터베이스의 role 컬렉션을 통해 확인할 수 있다.

use admin
db.system.roles.find().pretty()

생성한 역할은 createUser() 메서드를 통해 사용자 계정을 생성하면서 부여해주면 된다.

 

 

 

4. 암호화

데이터의 보안에서 상당히 중요한 부분을 차지하는 것이 DBMS 서버의 데이터 암호화 기능이다. 데이터 암호화는 크게 응용 프로그램 수준의 암호화와 데이터베이스 서버 수준의 암호화로 나눠 볼 수 있다.

 

응용 프로그램 수준의 암호화는 응용 프로그램을 개발하는 개발자가 직접 프로그램의 코드상에서 데이터를 암호화하고 복호화하는 작업을 수행하기에 암호화와 관련된 지식이 필요할 수 있다. 하지만 한번 익숙해지면 데이터베이스에 의존하지 않아도 암호화와 복호활르 할 수 있기 때문에 개발 생산성이 높아질 수 있다. 따라서 DBMS 수준에서 복호화가 필요한 것이 아니라면 응용 프로그램에서 암호화흔 ㄴ것이 훨씬 손쉬운 방법이다. 하지만 이럴 경우 암호화된 데이터는 인덱스를 이용한 범위 검색을 수행할 수 없다는 문제가 존재한다.

 

암호화하기 전의 평문에 대해서 범위 검색을 수행해야 한다면 DBMS 서버가 암호화된 데이터를 다시 복호화해서 정렬해야한다. 인덱스로 검색이나 정렬 작업을 필요로하는 경우를 위해서 많은 DBMS 서버들이 TDE 기능을 제공하고 있다. TDE(Transparent Data Encryption) 기능은 응용 프로그램 코드로부터 투명하게 작동할 뿐만 아닌 실제 DBMS 서버의 내부적인 처리에서도 투명하게 동작한다.

 

TDE 기능은 엔터프라이즈 버전에서 사용이 가능하다. 하지만 커뮤니티 버전의 MongoDB 서버에 내장된 WiredTiger 스토리지 엔진은 데이터 파일 암호화를 위한 인터페이스가 제공되고 있어 사용할 수 있다. 깃헙 'TDE for WiredTiger storage engine'에 커밋된 코드는 MongoDB 서버에서 빌드할 때 같이 컴파일 되지 않는다. 따라서 소스를 내려받고 TDE 플러그인을 컴파일하고 모든 프로그램이 참조할 수 있는 공용 라이브러리 디렉토리로 복사한다.

cd real-mongodb/src/third_party/tde
make

cp tde_encrypt.so /usr/lib64

tde_encrypt.so 플러그인은 데이터 파일의 암복호화를 위해서 2개의 암호화 키가 있어야하는데, 하나는 암복호화를 위한 초기화 벡터이고 나머지 하나는 암호화 키다. 이 2개의 키는 별도의 설정 파일에 기록해두면 tde_encrypt.so 파일이 초기화되면서 메모리로 읽어들인다.

[tde]
## Do not use these sample key as production key
init_vector = 1D1D1D1D1D1D1D1D1D1D...
encrypt_key = 1D1D1D1D1D1D1D1D1D1D
log_file = /var/log/mongod-enc.log

이 후에는 MongoDB 설정 파일 옵션을 변경하여 tde_encrypt.so 공유 라이브러리가 로딩될 수 있도록 해주면 된다. 이 때 tde_encrypt.so 파일은 MongoDB 서버가 접근할 수 있는 라이브러리 디렉토리에 존재해야한다. 만약 해당 파일이 공용 라이브러리 디렉토리 이외의 디렉토리에 있다면 리눅스의 LD_LIBRARY_PATH 환경 변수를 먼저 설정하고 MongoDB 서버를 시작해야한다.

 

extensions는 WiredTiger의 확장 기능을 활성화하는 옵션이다. 확장 기능을 구현한 공유 라이브러리 이름인 tde_encrypt.so 파일을 명시하고 공유 라이브러리의 시작 함수의 이름을 설정한다. 이 후 암호화 키와 초기화 벡터가 저장된 설정파일의 경로를 keyid 필드에 설정한다. 단, TDE 플러그인을 위한 암호화 키가 저장된 설정 파일과 로그 파일은 MongoDB 서버를 실행하는 운영 체제 유저가 읽고 쓸 수 있는 권한을 가져야한다. configString 옵션에서는 keyid 필드의 값 이외에는 변경해서는 안되며, keyid 필드의 이름 또한 예약된 키워드이고 단순히 keyid 필드에 암호화 관련 설정 파일의 경로만 명시해야한다. name 필드는 플러그인의 고유한 이름을 설정하는데, 이 이름도 플러그인의 실제 이름과 일치해야하므로 변경해서는 안된다. secretkey는 별도로 사용하지 않기에 비워둔다.

...
storage:
  dbPath: /data
  journal:
    enabled: true
  
  engine: wiredTiger
  
  wiredTiger:
    engineConfig:
      cacheSizeGB: 20
      journalCompressor: none
      configString: "extensions=[tde_encrypt.so=(entry=register_tde_encryptors)], encryption=(name=kencrypt,keyid=/etc/encryption.key,secretkey=)"
    collectionConfig:
      block Compressor: none
    indexConfig:
      prefixCompression: false
... 

tde_encrypt.so 공유 라이브러리가 정상적으로 초기화가 되면 설정된 로그 파일이 생성되고 'TDE for WiredTiger engine is initialized' 로그 기록을 확인할 수 있다. TDE가 활성화된 상태에서 MongoDB 서버가 시작되면 그 순간부터 저장되는 모든 데이터는 암호화되어 저장이 된다.

 

 

 

5. 감사

감사는 어떤 사용자가 어떤 쿼리를 언제 실행했는지 모두 로그로 기록하여 DBMS의 데이터에 문제가 발생했을 때, 언제 누가 실행한 쿼리 때문에 문제가 발생했는지 확인할 수 있는 기능을 의미한다. (MongoDB 서버에서 감사 기능은 엔터프라이즈 버전에서만 지원하지만, Percona MongoDB 서버에는 감사 기능이 지원되고 있음)

 

 

 

6. 데이터 관리

데이터 관리는 보안적인 관점보다는 데이터의 일관성을 유지하기 위해 사용하는 Document 체크 기능을 의미한다.


[참고] Real MongoDB

728x90

'Database > MongoDB' 카테고리의 다른 글

[MongoDB] 백업 및 복구  (0) 2020.12.21
[MongoDB] 잠금, 트랜잭션  (0) 2020.12.19
[MongoDB] Index  (0) 2020.12.19
[MongoDB] Aggregation Pipeline  (0) 2020.12.18