본문 바로가기

D.S/Project

210916목 - encrypted NoSql 사용하기 #2 (MongoDB)

728x90

 

몽고DB에서 encryption하는 방법은 클라이언트 측에서 데이터를 암호화해버리는 것이다.  

이를 CSFLE(client-side field level encryption) 이라고 부른다.  

사용자측에서 이미 암호화하길 원하는 필드를 선택해서 암호화를 함으로써 데이터는 비암호화인체로 서버로 전송될 일이 없고 DB서버에서 비암호화 된 채로 원데이터를 직접적으로 볼 수 없게 된다.  

따라서 민감한 정보를 서버에서 얻는 것도 블가능하고, DBA나 DB의 root 증명(credentials)로 디스크에서 데이터를 바로 읽어도 내용을 알 수 없게 된다.  

몽고DB는 mongoDB atlas나 엔터프라이즈에서만 implicit(비명시적으로)데이터를 암호화할 수 있으므로 나는 atlas에서 무료 db를 하나 만들었다. 이곳에서 db를 생성하면 몽고 DB측에서 AWS같은 클라우드서버에 몽고DB를 자동세팅해서 DB를 사용할 수 있게 해준다. 로컬로 잠깐 봤다가 도커제공도 따로 없고..환경세팅이 젤 귀찮아서;; Free 버전도 있다. 로컬에다 설치하면 explict(명시적으로) 내가 데이터를 암호화해야 한다고 나와있다.  

이 내용은 참조1의 내용을 정리한 것이다. 기본세팅은  

  - python 3.6+  

  - mongoDB Atlas 클러스터 (ver 4.2+)  

  - automatic field level encryption 이용 에는 엔터프라이즈 서버 패키지 설치. 위의 비명시적으로 데이터 암호화할 때는 말하는 것으로 atlas나 엔터프라이즈 이용시에 사용가능.  

  - 인스톨: python -m pip install "pymongo[encryption,srv]~=3.11"  

 


  • [참고1] 우클릭을 막아두어서 대략 어떻게 돌아가는지 확인하고, 아래 페이지의 코드를 다시 살펴보며 실행하는 걸 추천함.

 

데이터 encryption하는 방법 순서:

  1. 96 bytes의 토큰 생성 (마스터키. 클라이언트측에 보관)  

  2. 필드를 암호화할 데이터 키(key_id) 생성( 마스터키로 암호화함. DB __keystore 컬렉션에 저장)  

  3. 암호화할 필드에 대한 scheme json 생성 (key_id 이용함)  

  4. collection(=테이블)을 JSON scheme 적용해서 생성.  

 

 

 

 

728x90

 

결과부터 먼저:  

  - __keystore 컬렉션에는 data key가 암호화되어 저장되어 있다.  

  - keys 컬렉션에는 keys field가 암호화되어 저장되어 있다. 밑의 이미지는 keys가 서버DB에 암호화되어 보이지 않는 상태로 저장되어 있는 것을 보여줌.  

 

 

import

# ref: https://www.mongodb.com/developer/quickstart/python-quickstart-fle/
# python -m pip install "pymongo[encryption,srv]~=3.11"


import datetime
import json
import os
from pathlib import Path
from pprint import pprint

from bson import json_util
from bson.codec_options import CodecOptions
from bson.binary import STANDARD

from pymongo import MongoClient
from pymongo.encryption import (Algorithm,
                                ClientEncryption)
from pymongo.encryption_options import AutoEncryptionOpts
from pymongo.write_concern import WriteConcern
from pymongo.errors import OperationFailure

 

main함수에 위의 1~4를 실행하게 하고, 마지막으로 데이터를 넣어보고 확인하는 것까지 넣었음.  


if __name__ == "__main__":
    main()
    
#

 

main 함수의 part1.

1. 마스터키 생성  

2. 마스터키 적용해서 data key 생성 → test DB의 __keystore 컬렉션(테이블)에 저장  

3. 이 data key를 이용해서 ssn이라는 field에 암호화적용한 json scheme 생성  

4. 이 json scheme 로컬에 파일로 저장.  

 

keyAltNames = data 키의 별명 이라고 생각하면 됨. 키 저장한 컬렉션(테이블)에서 저 별명으로 data key를 불러올 수 있음.  

1.

def main():
    password = your passwd
    # atlas의 DB 접속.
    MDB_URL = f"mongodb+srv://{account}:{password}@{프로젝트db이름}.lyfew.mongodb.net"
    
   
    # This must be the same master key that was used to create
    # the encryption key. 96바이트의 마스터키 생성
    local_master_key = os.urandom(96)
		
    # 마스터키 로컬 저장
    Path("key_bytes.bin").write_bytes(local_master_key)
    kms_providers = {"local": {"key": local_master_key}}

#

 

2.

   
   # 2. -------------------------- 
    # 내가 원하는 field를 암호화할 data key를 생성하고 
    # test DB의 __keystore 컬렉션(테이블)에 저장하자.
    key_vault_namespace = "test.__keystore"
    key_vault_db_name, key_vault_coll_name = key_vault_namespace.split(".", 1)

    csfle_opts = AutoEncryptionOpts(
        kms_providers=kms_providers,
        key_vault_namespace=key_vault_namespace
    )
    
    # The MongoClient used to access the key vault (key_vault_namespace).
    with MongoClient(MDB_URL, auto_encryption_opts=csfle_opts, ssl=True, ssl_cert_reqs='CERT_NONE') as client:

        print("Resetting demo database & keystore ...")
        client.drop_database(key_vault_db_name)

        # key_vault = __keystore 테이블임
        key_vault = client[key_vault_db_name][key_vault_coll_name]


        # # 테이블에 인덱스 생성. keyaltname(키별명) 유니크하게 유지 설정.
        # key_vault.create_index(
        #     "keyAltNames",
        #     unique=True,
        #     partialFilterExpression={"keyAltNames": {"$exists": True}})


        # 여기가 마스터키로 데이터키 만드는 부분
        client_encryption = ClientEncryption(
            kms_providers,
            key_vault_namespace,
            client,
            # The CodecOptions class used for encrypting and decrypting.
            # This should be the same CodecOptions instance you have configured
            # on MongoClient, Database, or Collection. We will not be calling
            # encrypt() or decrypt() in this example so we can use any
            # CodecOptions.
            CodecOptions(uuid_representation=STANDARD))


        # 위에서 만든 키 저장할 콜렉션(__keystore)에 생성한 data 키를 저장한다.
        # Create a new data key and json schema for the encryptedField.
        
        print("Creating key in MongoDB ...")
        data_key_id = client_encryption.create_data_key(kms_provider='local', key_alt_names=['keys'])
        
        # db 컬렉션에서 altname으로 데이터키 가져오기
        # key_id = db.__keystore.find_one({ "keyAltNames": "example" })["_id"]
   
 #

 

3.

        # 3. -------------------------- 
        print('scheme.......')
        schema = {
            "bsonType": "object",
            "properties": {
                "ssn": {
                    "encrypt": {
                        "keyId": [data_key_id],
                        "bsonType": "string",
                        "algorithm": Algorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random
                    }
                }
            }
        }

    
#

 

4. 


       # 4. -----------------------
       json_schema = json_util.dumps(schema,
                                      json_options=json_util.CANONICAL_JSON_OPTIONS,
                                      indent=2)
        Path("json_schema.json").write_text(json_schema)


    # 키랑 스키마 다시 받아오는 건데 참조용으로 달아둠
    # Load the master key from 'key_bytes.bin':
    # key_bin = Path("key_bytes.bin").read_bytes()


    # Load the 'person' schema from "json_schema.json":
    collection_schema = json_util.loads(Path("json_schema.json").read_text())
    
    #

 

main 함수의 part2.

1. test db에서 people이라는 컬렉션에 접속해서 이전 데이터를 날림 → 앞서 만든 json scheme을 validator로 적용한 컬렉션을 새로 만들기  

2. 데이터넣고 확인.  

MongoClient 생성시에 파라미터로 auto_encryption_opts = csfle_opts 를 해주면 find할 때 알아서 데이터를 복호화해서 보여준다. 옵션을 안 넣으면 암호화한 상태로 보여줌.  

   

    # auto_encryption_opts=csfle_opts 이게 없으면 find_one할 때 암호화된 상태로 보여줌
    with MongoClient(MDB_URL, auto_encryption_opts=csfle_opts, ssl=True, ssl_cert_reqs='CERT_NONE') as client:
        
        db_namespace = 'test.people'
        db_name, coll_name = db_namespace.split(".", 1)

        db = client[db_name]
        # Clear old data
        db.drop_collection(coll_name)


        # Create the collection with the encryption JSON Schema.
        db.create_collection(
            coll_name,
            # uuid_representation=STANDARD is required to ensure that any
            # UUIDs in the $jsonSchema document are encoded to BSON Binary
            # with the standard UUID subtype 4. This is only needed when
            # running the "create" collection command with an encryption
            # JSON Schema.
            codec_options=CodecOptions(uuid_representation=STANDARD),
            write_concern=WriteConcern(w="majority"),
						# 암호화하는 field에 대한 validator
            validator={"$jsonSchema": collection_schema})
        coll = client[db_name][coll_name]



        tmp_data= {
            "full_name": "Sophia Duleep Singh",
            "ssn": "123-12-1234",
        }
        
        id_ = coll.insert_one(tmp_data).inserted_id
        print('id_:', id_)
        pprint(coll.find_one())

 

 

 

 

 

 

 

에러해결

 

1. RuntimeWarning: Failed to start mongocryptd: is it on your $PATH?

이건 또 뭔 에러인가 싶었는데, 찾아보니 automatic field level encryption 이용 할 때 mongocryptd 가 필요하고, 이 파일은 MongoDB Enterprise Server package 에 포함되어 있다. 그 말인 즉슨 엔터프라이즈 서버 패키지를 설치해야한다는 것.  

  - 다운로드 페이지: https://www.mongodb.com/try/download/enterprise?tck=docs_server  

 

mongocryptd is required for automatic field level encryption and is included as a component in the MongoDB Enterprise Server package, or separately as the mongodb-enterprise-cryptd package.

  - 참조: https://docs.mongodb.com/manual/reference/security-client-side-encryption-appendix/  

 

설치하고 뭘 해야한다? mongocryptd 를 사용하기 위해 PATH에 mongocryptd 가 들어가 있는 폴더를 추가해줘야 한다. 나는 pycharm 사용하는 os가 윈도우10이고, atlas의 몽고DB 버전은 4.4.8로 엔터프라이즈 서버 패키지도 4.4.8로 다운받고, 설치한 후 Path에 C:\Program Files\MongoDB\Server\4.4\bin 추가해줌.  

윈도우10 환경설정은 어찌하는줄 알쥬? 내 PC>속성>고오급 시스템 설정>환경 변수>Path에 추가  

바로 적용 안 되면(안 될거임..) 재부팅ㄱㄱ  

참조:  

  - https://www.mongodb.com/community/forums/t/is-client-side-field-level-encryption-supported-with-atlas/5712  

  - https://docs.mongodb.com/manual/reference/security-client-side-encryption-appendix/  

 

2. getpass()함수는 파이참에서 실행이 안 됨.

  - 좀 딴 소리이지만, 해보다 알았음. 명령 프롬프트에선 된다고 함. 끗.  

 


참조

  - 가장 많이 참고함: https://www.mongodb.com/developer/quickstart/python-quickstart-fle/  

  - https://www.mongodb.com/developer/quickstart/python-quickstart-fle/  

  - 기본사용법: https://www.mongodb.com/developer/quickstart/python-quickstart-crud/  

  - pymongo 공식 doc: https://pymongo.readthedocs.io/en/stable/installation.html  

  - 엔터프라이즈 서버 패키지 다운로드 페이지: https://www.mongodb.com/try/download/enterprise?tck=docs_server  

 

 

 

 

 

반응형