Naver Cloud Platform

네이버 클라우드의 Key Management Service로 개인 정보 암호화

앙뚱이 2023. 7. 6. 16:43

암호화 관련 개인정보보호법 출처는 인터넷 뉴스 부분 발췌

ㅇ일단 데이터 암호화를 왜 하고 뭘 해야하는지

1. 개인정보보호법에서 정의하는 암호화 대상 데이터들은 시스템에 암호화 되어 저장해야한다. -- 정보 유출 방지

2. 암호화 대상들로는 고유식별정보, 비밀번호, 생체인식정보가 있다.

3. 암호화 대상 데이터 타입은 여러가지가 있다. DB가 될수도 있고 비정형 데이터(이미지, 녹취, 엑셀, PDF 파일, ..........)도 해야할수도 있고 일단 뭐 고유식별정보가 들어가면 해야된다.

 

 

그러므로

지금은 NCP의 Key Management Service ( 이하 KMS )로 DB 암호화를 해보려합니다

 

  •  시나리오는 아래 그림과 같고 

1. 웹 이용자가 웹 페이지에 개인정보를 적는다

2. 그러면 KMS에서 API를 이용해 키를 불러와 암호화하고 DB에 저장한다.

3. Read 쿼리를 할땐 반대로 복호화할 수 있는 API URL을 가져와서 Read한다.

4. KMS에서 키를 호출하여 암/복호화에 관한 로그는 NCP 콘솔에서 확인 가능하다.

- 결론 : DB에 저장되는 암호화 대상 데이터는 암호화가 되어 서버에 보관된다.

 

시나리오

 

 

  • 제가 구성한 환경은

파이썬 ( KMS API 이용 ), 플라스크 ( 웹페이지 구현 ), KMS ( NCP 서비스 ), CentOS ( OS ), PostgreDB ( DB )를 이용했지만 이 포스팅에선 간단하게 KMS 기능과 부분 설정, 그리고 API 코드(예시) 정도만 공유하려 합니다.

 

 

  • 일단 그 전에 KMS의 기능 좀 살펴볼게요!

- 키 라이프 사이클 ( 키 생성, 키 삭제, 키 보관, 키 활성화/비활성화 )

- 암호화 키를 KMS에서 제공하는 여러개의 키로 암호화하여 안전하게 보관도 가능합니다. 

- KMS에서 콘솔 통해 생성한 암호화 키 값은 육안으로 확인 불가합니다. ( API의 cutomkey 생성을 이용하면 base64로 인코딩된 평문 키를 확인할 순 있습니다.)

- KMS의 암호화 키를 사용자 계정 별로 권한 관리도 가능합니다.

키 권환 관리

 

- KMS의 암호화 키 사용 이력에 대해 로그를 남겨 놓습니다. ( 암호화 키를 요청한 서버 혹은 pc의 IP나 API를 요청한 계정 / 작업(암호화/복호화)에 대한 로그를 제공합니다.)

키 사용 이력

- 아래는 요금표입니다.

요금표

 

***** 중간 생각 *****

  • 비용이 중요하기 때문에 KMS를 이용하려면 요금표에 따라 한달에 평균적으로 실행되는 키 호출횟수가 사실 엄청 중요하고 20,000개까지는 무료고 그 이후 추가되는 호출 건수에 따라 X30하면 된다고 하드라구요. 그리고 키 보유 한개당 1000원씩 드네요. 사용하고 있는 DB를 잘 파악해서 잘 이용하면 암호화 솔루션 이용하는 것보다 저렴할 것 같아요. 물론 개발 공수도 살짝 들어가고... 제한적인 게 많긴 하지만... 아 그리고 키에 대한 호출값은 사실 DB 쿼리수에 비례합니다.(Read 쿼리-> 복호화 , write 쿼리 -> 암호화)
  • 성능은 네트워크를 한번 더 타고 코드도 추가해야하는 게 있으니 미미한 영향이 있을 수 있을 것 같긴해요. 고민을 잘 해봐야될 것 같아요.
  • 왠만한 암호화 요건은 잘 준수하는 것 같아요. 암호화 키도 암호화 대상 서버에 저장하지 않고 클라우드에 저장하고 또 키 값을 확인 못하니  키에 대한 유출을 방지하니 잘 써먹으면 좋을 것 같아요.
  • 암/복호화나 그런 암호화 키 사용 이력에 대한 히스토리는 확인할 수 있는데 이 이력을 다운받아서 오브젝에 저장하거나 그런 로그 파이프라인? 구성이 좀 어렵네요 이건... 그런 기능이 없는 것 같은데 제가 모르는건가.. 혹시 알고 계시다면 알려주세요.
  • 일단 이 KMS는 사실 DB나 그런 정형 데이터를 겨냥해서 나온 서비스는 아닌 것 같고 custom된 암호화 키를 KMS에서 생성한 Master key로 암호화 하여 별도의 서버나 저장 장치에 저장하기 위한 용도가 아닐까 생각은 드네요 하지만 그래도 DB 암호화 해보겠음요.

말이 너무 길어졌네요 일단 해보죠.

 

 

1. 키 생성

- 키 이름을 지정해주고 저희는 DB를 암호화 할꺼니 대칭키인 AES256을 선택해줍니다. 자동회전은 선택안할게요. 왜냐면 자동 회전할 경우 암호화된 데이터를 복호화 하지 못하기 때문이죠. 

 

2. 키 Tag값 확인

- 생성된 키를 클릭하면 아래와 같이 키 Tag값을 확인할 수 있어요 이 값을 이용하여 API를 통해 암/복호화를 위한 암호화 키를 호출할 수 있습니다.

 

3. 웹 페이지에서 개인정보를 넣어보기

- 이름은 홍길동이고 주민등록번호가 1111인 친구를 웹페이지에 입력을 해볼게요. 웹페이지에 입력된 정보는 자동으로 DB에 저장되게끔 설정하였습니다.

 

4. DB 데이터 확인

- 주민번호가 1111111이었는데 암호화 되어 저장된 것을 확인할 수 있네요. 금방 write하네요 추후에 암호화된 데이터를 읽을땐 복호화 API 코드를 추가하여 read 쿼리에 넣어주면 됩니다.

 

5. 암호화 코드는  이렇게 썼어요   웹에서 입력한 코드를 KMS API 이용하여 암호화 후 DB에게 전달

- flask라는 걸 처음 써보는 지라 gpt의 도움을 많이 받았네요

- 제가 CentOS에서 이 코드를 사용했는데 혹시 저와 같은 환경구성이 필요한 사람은 댓글로 남기면 알려드림요

 

[root@s188f69e37cc www]# cat encryption.py
from flask import Flask, request, render_template
import sys
import os
import hashlib
import hmac
import base64
import requests
import time
import json
import psycopg2

app = Flask(__name__)

access_key = "1111111" ## 이 값은 제가 NCP sens 관련해서 쓴 포스팅글이 있는데 거기다 확인하는 법 적어놨어요
secret_key = "111111"  ## 이 값은 제가 NCP sens 관련해서 쓴 포스팅글이 있는데 거기다 확인하는 법 적어놨어요
timestamp = str(int(time.time() * 1000))
url = "https://kms.apigw.ntruss.com"
uri = "/keys/v2/21c607401b58104ee394568cf267bdc6f46c00106c7a53cd4b29f626b0bfe2dc/encrypt"

@app.route('/', methods=['GET', 'POST'])
def encrypt_number():
    if request.method == 'POST':
        name = request.form['name']
        number = request.form['number']

        # PostgreSQL database connection
        connection = psycopg2.connect(
            database="token",
            user="postgres",
            password="Admin!123#",
            host="111.111.111.111",
            port="5432"
        )
        cursor = connection.cursor()   #### DB 접속 설정 및 커넥션 객체 생성

        def make_signature():
            global secret_key
            global access_key
            global timestamp
            global url
            global uri
            secret_key_bytes = bytes(secret_key, 'UTF-8')
            method = "POST"
            message = method + " " + uri + "\n" + timestamp + "\n" + access_key
            message = bytes(message, 'UTF-8')
            signingKey = base64.b64encode(hmac.new(secret_key_bytes, message, digestmod=hashlib.sha256).digest())
            return signingKey

        header = {
            "Content-Type": "application/json; charset=utf-8",
            "x-ncp-apigw-timestamp": timestamp,
            "x-ncp-iam-access-key": access_key,
            "x-ncp-apigw-signature-v2": make_signature()
        }

        encoded_number = base64.b64encode(number.encode('utf-8')).decode('utf-8')

        body = {
            "plaintext": encoded_number
        }

        res = requests.post(url + uri, headers=header, data=json.dumps(body))
        data = json.loads(res.text)
        encrypted_number = data['data']['ciphertext']

        cursor.execute("INSERT INTO info (name, identifier_number) VALUES (%s, %s)", (name, encrypted_number))
        connection.commit()
        cursor.close()
        connection.close()

        return f"암호화된 번호: {encrypted_number}"
    
    return render_template('index.html')

if __name__ == '__main__':
    app.run(host='0.0.0.0')

 

 

6. 결론

- KMS가 기능이 많은건 아니지만 고유식별 정보 관련해서 많은 쿼리가 호출되는 DB가 아니라면 이와 같이 DB 암호화를 구성해도 되지 않을까라는 생각 

- 키 감사, 암/복호화 키 별도 보관, 키 라이프 사이클 관리 가능, 키 접근제어 가능 ( NCP 계정으로 ) 이런 기본적인 보안 요건을 준수하는 기능들을 가지고 있다 생각.

- 아쉬운 건.. 음.. Key Vault 같이 Key Insert 기능이 없어서 아쉽긴 하다. 이미 운영중인 환경에서 암호화된 데이터를 KMS에서 생성한 새로운 키로 재암호화 하기에는 너무 공수가 많이 들어가니 기존에 사용하던 키를 보관하는 기능이 있다면 좋을 듯 싶은데..