TL;DR
MongoDB에서 기본기능(무상)으로 제공하는 컬럼 암호화(Queryable Encryption)에 대한 내용입니다. 초기 설정, 데이터 입력, Equality, Range 검색을 하는 예제로 구성되어 있습니다.
IT Journeyman
DB 컬럼 암호화는 주민번호같은 민감정보를 위한 암호화로 많이 사용됩니다. 그러나 MongoDB를 제외한 대부분 DB는 암호화된 컬럼에는 인덱스 설정에 안되거나 제한적이어서 다른 방법을 사용해야 했습니다. Equality만 되어도 훌륭한데 Range 검색까지 되니 이게 왠 떡이냐 싶기도 합니다.(그러나 모든 것에는 대가가 있죠, 그러나 이 경우는 효용이 비용을 크게 초과한다는 생각이 드네요)
Queryable Encryption w/ AWS KMS
Table of Contents
Executive Summary
1. Node.js Application으로 데이터 암호화 하기
2. 암호화 여부 확인하기
2-1 Atlas UI에서 확인하기
2-2 Compass에서 확인하기
2-3 별도의 인증을 통하여 Compass에서 데이터 확인하기
3. Node.js Application으로 Equality 검색 하기
3-1 암호화되어 있지 않은 필드 조건 검색
3-2 암호화된 필드 조건 검색
4. Node.js Application으로 Range 검색 하기
4-1 암호화되어 있는 필드 Range 검색(Multi Row)
4-2 암호화되어 있는 필드 Range 검색(Single Row)
4-3 암호화되어 있는 필드 Composite 검색(Equality+Range)
4-4 암호화되어 있는 필드 Mapping Range를 Equality로 검색
5. DEK 삭제 후 검색
0. Test Environment
0-1 : Test Server@AWS(Ubuntu 24.04.1 LTS @Amazon EC2 t3.micro)
0-2 : MongoDB Atlas M10(8.0.5, ap-northeast-2)
0-3 Install mongosh
0-4 DB Connection test w/ mongosh
0-5 Git에서 Sample 가져오기
0-6 AWS Key Management Service (KMS) 생성 및 ARN 확인
0-7 credentials 확인 및 편집
0-7 node.js, npm, MongoDB Node Driver 설치
Executive Summary
- 별도의 3rd Tool 없이 암호화가 가능하며, 암호화된 필드의 조건 검색도 가능합니다.
- 조건 검색은 Equality, Range 그리고 Composite도 가능합니다.
- 전체 데이터를 조회하거나 비암호화 필드를 검색할 경우,암호화된 데이터는 보이지 않습니다.
- KMS는 필수이며, 아래를 지원합니다.
- AWS Key Management Service (AWS KMS), Google Cloud KMS, Azure Key Vault, Any KMIP-compliant key provider.
- 암호화에 사용되는 DEK(Data Encryption Key)는 CMK(Customer Master Key)로부터 생성되어 MongoDB에 저장되어 관리 됩니다. 만약 DEK를 삭제하면 영원히 데이터를 복호화할 수 없습니다.(백업이 강력히 권장됩니다)

www.mongodb.com/docs/upcoming/core/csfle/fundamentals/manage-keys/#delete-a-data-encryption-key
- 주기적인 CMK Rotation도 가능합니다.
- 첫 번째가 이번 테스트를 수행한 코드이며, 다른 샘플 코드는 두 번째 링크에 있습니다.
- https://github.com/KyleLeeKorea/QE_DEMO
- https://github.com/mongodb/docs/tree/master/source/includes/qe-tutorials
1. Node.js Application으로 데이터 암호화 하기
npm install을 한 후, data key를 생성하고, 초기 데이터 10건을 암호화를 수행하여 입력합니다.
ubuntu@ip-10-0-0-107:~/QE_DEMO$ npm install
up to date, audited 137 packages in 918ms
12 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
ubuntu@ip-10-0-0-107:~/QE_DEMO$ node make_data_key.js
Created encrypted collection!
ubuntu@ip-10-0-0-107:~/QE_DEMO$ node load_data.js
10개의 문서가 성공적으로 삽입되었습니다.
2. 암호화 여부 확인하기
2-1 Atlas UI에서 확인하기
암호화한 필드에 대한 내용은 아래처럼 내용을 확인할 수 없습니다.

2-2 Compass에서 확인하기
MongoDB가 제공하는 툴인 Compass에서나 다른 일반적인 환경에서도 데이터 확인 안 됩니다.

2-3 별도의 인증을 통하여 Compass에서 데이터 확인하기
Compass의 접속 설정화면에서 KMS 관련 설정을 하면 데이터 확인이 가능합니다. 또한 MongoDB CLI인 Mongosh에서 KMS설정 후 데이터를 확인할 수 있습니다.


3. Node.js Application으로 Equality 검색 하기
3-1 암호화되어 있지 않은 필드 조건 검색
아래 처럼 코드 한 줄로 암호되어 있지 않은 firstName으로 검색을 합니다.
console.log(await unencryptedColl.findOne({ firstName: /철수/ }));
아래 Example1-1에서 볼 수 있듯이 암호화된 필드는 내용 확인이 되지 않습니다.
ubuntu@ip-10-0-0-107:~/QE_DEMO$ node equal_query.js
=================================================================================
Example1-1 : Finding a document with regular (non-encrypted) client.
=================================================================================
{
_id: new ObjectId("67be9d33d3437096bcbff034"),
firstName: '철수',
lastName: '김',
patientId: new Binary(Buffer.from("0e25cbbe22261d4314b79e6040458f958b10e54c2d71dba71c5d06e02f791d83f3f21e6ec102f5c460a2404f9c0c569c33aced17793a2e0c5cd2946a89357dc27116b05b6b75d578d528726c69283fad5e45778f4b4b0ac8076b92e17f3835ff8a87343a4deb345e9358a5f406d2547bf185d8e131f5bac0d687f70d019983527c4900e109e3b4e2c11c78911195f99db4e2ba51882ef4a7e654f5929b948ce6ebb178dee12f08263796fbebfb831f80cba79211c177a7ace0c92f400c249d9ca000d7a2cf72bb58d6f3ba9b6ab45412caf0", "hex"), 6),
address: '서울특별시 강남구 테헤란로 123',
patientRecord: {
ssn: new Binary(Buffer.from("0e03d5d953e1b546fe84d1b9fdf8ee356402d35397b5b69868b4a02378b63577aa478101591c4c3042cf1f3d2262c1442c4a53eb2b60c18b064c10f32852f687f86e855d2156b828837aab2e30e8ff8f1a494a3658e44663b4ec728144fbf4fea981da0b7082820d2e54e502e48e9c6f3a2ff9f0d20bfddfa470ad49c33e01c2326e361f00994786220dfa9747cd8696b4f08f0a80f977e0fbed706b124fc794f748881423992a4c3459dcb9c64ba04286d0ef1634d923919475cf6ffff93cdccdc18ddeab455f5f0b34caf22b894abbc3dab0388d08173fd79b54d6179c3bc6b63e", "hex"), 6),
billing: new Binary(Buffer.from("10e4e8224b30ea45c8966ce7c13e90ab6f037202856218bfcbef7fcf4e48a86b004024af9e7c50303e3d8ab006a9ec87ac10ee86d433b5b662f89139a43c5c4a707b80ab4f65fd531ecaf5b99af1b0ebcd6d778467d32b17b7a41f2d8a70bcc3c089e581a6fd155e1edf09c5707152da93a4cf06fa08ab8bb40fc1ce7473a4199ad8", "hex"), 6)
},이하 생략
3-2 암호화된 필드 조건 검색
아래 Example1-2처럼 환자의 암호화되어 있는 사회보장번호를 입력값으로 Equality 검색합니다.
console.log(await encryptedColl.findOne({ "patientRecord.ssn": "987-65-4320" }));
환자번호, 카드회사, 카드번호, 처방내용, 청구비용 등 암호화된 데이터가 정상 조회
=================================================================================
Example1-2 : Finding a document with encrypted client, searching on an encrypted field
=================================================================================
{
_id: new ObjectId("67be9d33d3437096bcbff034"),
firstName: '철수',
lastName: '김',
patientId: 12345678,
address: '서울특별시 강남구 테헤란로 123',
patientRecord: {
ssn: '987-65-4320',
billing: { type: 'Visa', number: '4111111111111111' }
},
medications: [ '아토르바스타틴', '레보티록신' ],
billAmount: 1000,
4. Node.js Application으로 Range 검색 하기
4-1 암호화되어 있는 필드 Range 검색(Multi Row)
상방(Upper Limit)이 열려있는 Range Query를 수행합니다.
const amountQuery = await encryptedColl.find({ billAmount: { $gt: 1000} }).toArray();
Example2-1처럼 복호화된 검색 결과를 확인할 수 있습니다.
=================================================================================
Example2-1 : billAmount가 1000 이상인 환자 찾기
=================================================================================
billAmount가 1000 이상인 환자 수: 6명
환자 목록:
1. 지수 최: $1500
2. 지윤 한: $1400
3. 하준 배: $1300
4. 영희 박: $1200
5. 서연 윤: $1100
6. 은서 강: $1100
4-2 암호화되어 있는 필드 Range 검색(Single Row)
billAmount가 1100인 조건으로 한 건을 읽어 오는 Range 검색을 합니다.
const findResult1 = await encryptedColl.findOne({ "billAmount": { $gt: 1000, $lt: 1200 }, });
결과는 두 건 중 먼저 읽은 데이터를 반환합니다.
=================================================================================
Example2-2 : billAmount가 1000 이상 1200 이하 환자 찾기
=================================================================================
{
_id: new ObjectId("67be9d33d3437096bcbff039"),
firstName: '서연',
lastName: '윤',
patientId: 67890123,
address: '인천광역시 남동구 구월로 78',
patientRecord: {
ssn: '432-10-9876',
billing: { type: 'Mastercard', number: '5105105105105100' }
},
medications: [ '하이드로클로로티아지드', '심바스타틴' ],
billAmount: 1100,
이하 생략
4-3 암호화되어 있는 필드 Composite 검색(Equality+Range)
아래처럼 하나 이상의 암호화된 필드에 조건 검색하여 원하는 결과를 검색할 수 있습니다.
console.log(
await encryptedColl.findOne({
"patientRecord.ssn": "987-65-4321",
"billAmount": { $gt: 1000, $lt: 1200 }
})
);
Example2-2(서연 윤)와 다른 Example2-3(은서 강)을 확인할 수 있습니다.
=================================================================================
Example2-3 : Range + Equality
=================================================================================
{
_id: new ObjectId("67be9d33d3437096bcbff03d"),
firstName: '은서',
lastName: '강',
patientId: 12123459,
address: '제주특별자치도 제주시 일주서로 200',
patientRecord: {
ssn: '987-65-4321',
billing: { type: 'Mastercard', number: '5200123456789012' }
},
medications: [ '텔미사르탄', '아스피린' ],
billAmount: 1100,
4-4 암호화되어 있는 필드 Mapping이 Range인 필드를 Equality로 검색
아래 billAmount는 Range로 매핑을 하였지만, Example2-4처럼 Equality도 가능합니다. 반대의 경우는 에러가 납니다.
const findResult2= await encryptedColl.findOne({ "billAmount": 1500 , });
=================================================================================
Example2-4 : Range로 Map하고 Equality로 검색
=================================================================================
{
_id: new ObjectId("67be9d33d3437096bcbff037"),
firstName: '지수',
lastName: '최',
patientId: 45678901,
address: '대구광역시 수성구 범어로 56',
patientRecord: {
ssn: '654-32-1098',
billing: { type: 'Visa', number: '4012888888881881' }
},
medications: [ '메토프롤롤', '가바펜틴' ],
billAmount: 1500,
5. DEK 삭제 후 검색
아래처럼 DEK가 저장되어 있는 컬렉션을 삭제 시 Application에서는 DEK를 찾을 수 없다는 에러를 발생하고, GUI(Compass)나 Web UI(Atlas UI)에서도 암호화 되지 않은 데이터만 조회할 수 있습니다.

0. Test Environment
0-1 : Test Server@AWS(Ubuntu 24.04.1 LTS @Amazon EC2 t3.micro)
ubuntu@ip-10-0-0-107:~$ hostnamectl
Static hostname: ip-10-0-0-107
Icon name: computer-vm
Chassis: vm
Machine ID: ec201d1924d2fc44a4ef43bb4a384d14
Boot ID: 4d8857c634a8489797b553d688e4e5b0
Virtualization: amazon
Operating System: Ubuntu 24.04.1 LTS
Kernel: Linux 6.8.0-1021-aws
Architecture: x86-64
Hardware Vendor: Amazon EC2
Hardware Model: t3.micro
Firmware Version: 1.0
Firmware Date: Mon 2017-10-16
Firmware Age: 7y 4month 1w 3d
0-2 : MongoDB Atlas M10(8.0.5, ap-northeast-2)

0-3 Install mongosh
Install mongosh - MongoDB Shell
wget -qO- https://www.mongodb.org/static/pgp/server-8.0.asc | sudo tee /etc/apt/trusted.gpg.d/server-8.0.asc
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu noble/mongodb-org/8.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-8.0.list
sudo apt-get update
sudo apt-get install -y mongodb-mongosh
0-4 DB Connection test w/ mongosh
ubuntu@ip-10-0-0-107:~$ mongosh "mongodb+srv://prod01.npgjj.mongodb.net/" --apiVersion 1 --username it_admin
Enter password: *******
Current Mongosh Log ID: 67bd8c57500d03ed1651e943
Connecting to: mongodb+srv://<credentials>@dev01.npgjj.mongodb.net/?appName=mongosh+2.4.0
Using MongoDB: 8.0.4 (API Version 1)
Using Mongosh: 2.4.0
For mongosh info see: https://www.mongodb.com/docs/mongodb-shell/
0-5 Git에서 Sample 가져오기
ubuntu@ip-10-0-0-107:~/$ git clone https://github.com/KyleLeeKorea/QE_DEMO
Cloning into 'QE_DEMO'...
remote: Enumerating objects: 37, done.
remote: Counting objects: 100% (37/37), done.
remote: Compressing objects: 100% (34/34), done.
remote: Total 37 (delta 10), reused 0 (delta 0), pack-reused 0 (from 0)
Receiving objects: 100% (37/37), 15.97 KiB | 3.99 MiB/s, done.
Resolving deltas: 100% (10/10), done.
0-6 AWS Key Management Service (KMS) 생성 및 ARN 확인

0-7 credentials 확인 및 편집
SHARED_LIB_PATH는 아래에서 라이브러리를 다운로드 받아 압축을 푼 후 위치한 경로를 입력
Install and Configure a Query Analysis Component - MongoDB Manual v8.0
const credentials = {
// Mongo Paths + URI
MONGODB_URI: "<your MongoDB URI here>",
SHARED_LIB_PATH: "<path to crypt_shared library>",
// AWS Credentials
AWS_ACCESS_KEY_ID: "<your AWS access key ID here>",
AWS_SECRET_ACCESS_KEY: "<your AWS secret access key here>",
AWS_KEY_REGION: "<your AWS key region>",
AWS_KEY_ARN: "<your AWS key ARN>",
};
이하 생략
0-7 node.js, npm, MongoDB Node Driver 설치
Download and Install - Node.js Driver v6.13
sudo apt update
sudo apt install nodejs
ubuntu@ip-10-0-0-107:~/QE_DEMO$ node -v
v18.19.1
sudo apt install npm
npm install mongodb@6.13
'T. > MongoDB' 카테고리의 다른 글
| MongoDB Certifications Tips (1) | 2025.02.19 |
|---|---|
| [AI]Enable Natural Language Querying (1) | 2024.09.25 |
| [DB Internel]killSessions, 누가 이거 돌렸어 ! (0) | 2024.08.16 |
| [DB Internal]Deadlock (0) | 2024.08.07 |
| [Data Lake]Atlas Data Federation (0) | 2024.07.30 |