L U N I V E R S E
안전한 암호 및 키 관리를 위한 조건과 볼트(Vault)

암호화 알고리즘은 결국 공개된 표준이기 때문에 해당 표준의 상세 여부를 따지기보다 암호화 열쇠를 어떻게 만들고 저장하고 관리할 것인지가 실제 가장 중요하다. 한국인터넷진흥원(KISA)에서는 암호화된 데이터와 암호화 키는 물리적으로 안전하게 분리된 장소에 보관해야 한다고 권고하고 있다. 데이터는 암호화하여 DB에 보관한다고 하면 키는 어디에 어떻게 보관해야 할까?

일반적으로 암호화 키, 인증서 등 민감한 데이터를 안전하게 보호하는 서비스가 볼트(Vault)이다. 볼트 서비스는 민감 데이터를 DB에 전달하기 전에 암호화를 하며, DB에 저장된 암호화된 민감 데이터는 Vault 없이는 해독할 수 없게 한다. 이러한 과정을 통해 민감 데이터를 안전하게 보호한다. 본 글에서는 해시코프(HashiCorp)의 크로스플랫폼 패스워드 및 인증 관리 시스템인 볼트서비스와 루니버스 볼트 서비스를 통해 안전한 키 및 데이터 관리 방법에 대해 살펴본다.

해시코프의 볼트(Vault) 서비스

사용 사례

Secrets Management

조직의 도메인에 대한 SSL 인증서 및 키, 회사 데이터베이스 서버에 연결하기 위한 자격 증명 등을 안전한 방식으로 저장할 수 있다.

Static Secrets: Key/Value Secrets Engine 예제
  • request payload에 API key 저장
				
					$ tee payload.json <<EOF
{
  "key": "AAaaBBccDDeeOTXzSMT1234BB_Z8JzG7JkSVxI"
}
EOF
				
			
  • secret path 설정과 함께 key를 vault로 전송
				
					$ curl --header "X-Vault-Token: <client_token>" \
       --request POST \
       --data @payload.json \
       http://127.0.0.1:8200/v1/kv-v1/eng/apikey/Google
				
			
  • 잘 저장되었는지 secret path로 확인
				
					$ curl --header "X-Vault-Token: <client_token>" \
       http://127.0.0.1:8200/v1/kv-v1/eng/apikey/Google | jq

// 리턴된 json
{
  "request_id": "dc0f0baf-16cb-af3f-db11-2c986df02e60",
  "lease_id": "",
  "renewable": false,
  "lease_duration": 2764800,
  "data": {
    "key": "AAaaBBccDDeeOTXzSMT1234BB_Z8JzG7JkSVxI"
  },
  "wrap_info": null,
  "warnings": null,
  "auth": null
}
				
			
  • 하지만 위와 같이 secret engine으로 K/V를 사용하면 key 값이 평문으로 암호화 없이 저장되어 문제가 될 수 있다.
Data Encryption
Vault는 EaaS (Encryption as a Service)를 제공한다. 키, 데이터 암/복호화를 Vault 관리자가 책임지므로 앱개발자의 부담을 줄여준다.
- 예제 1
앱에서 data(이미지 포함)를 base64로 인코딩하여 전송하면 암호화된 data를 얻을 수 있다.
  • 문자열 “4111 1111 1111 1111” 을 암/복호화 해보자.
				
					// base64 인코딩 방법
const fs = require('fs');

let buff = '4111 1111 1111 1111';
let base64data = buff.toString('base64');
or
$ base64 <<< "4111 1111 1111 1111"
NDExMSAxMTExIDExMTEgMTExMQo=

// Root token 또는 권한 있는 토큰과 함께 encryption API 호출
$ curl --header "X-Vault-Token: <client_token>" \
       --request POST \
       --data '{"plaintext": "NDExMSAxMTExIDExMTEgMTExMQo="}' \
       http://127.0.0.1:8200/v1/transit/encrypt/orders | jq

// 리턴된 데이터
{
  "request_id": "f92362bd-8f06-d847-7275-5c2124003479",
  "lease_id": "",
  "renewable": false,
  "lease_duration": 0,
  "data": {
    "ciphertext": "vault:v1:haARDbBl/dry0pNSpztCHZ8K+ZKFZyn6z7z2cRfESZzfWdPD+GoSYxwJaRuiBtwC",
    "key_version": 1
  },
  "wrap_info": null,
  "warnings": null,
  "auth": null
}

// Root token 또는 권한 있는 토큰과 함께 decryption API 호출
$ curl --header "X-Vault-Token: <client_token>" \
   --request POST \
   --data '{"ciphertext": "vault:v1:haARDbBl/dry0pNSpztCHZ8K+ZKFZyn6z7z2cRfESZzfWdPD+GoSYxwJaRuiBtwC"}' \
   http://127.0.0.1:8200/v1/transit/decrypt/orders | jq

// 리턴된 데이터
{
  "request_id": "57d8b84f-4e81-42c6-3829-ecb8241072bb",
  "lease_id": "",
  "renewable": false,
  "lease_duration": 0,
  "data": {
    "plaintext": "NDExMSAxMTExIDExMTEgMTExMQo="
  },
  "wrap_info": null,
  "warnings": null,
  "auth": null
}

// base64 decode
$ base64 --decode <<< "NDExMSAxMTExIDExMTEgMTExMQo="
4111 1111 1111 1111
				
			

해커가 DB 액세스 계정을 탈취하여 접근하여도 암호화된 데이터만 획득하기 때문에 안전하게 데이터를 지킬 수 있다.

- 예제 2
현재 운영되고 있는 DB에서 Vault를 적용하려고 하면 DB 스키마 변경이 필요하지 않을까?
Vault에서는 형식 보존 암호화(FPE)를 통해 데이터 형식과 길이를 유지하면서 인코딩이 가능하여 아래와 같이 해결할 수 있다.

High-Level Overview

  • 인증을 통해 policy와 token을 리턴받아 사용한다.
Barrier 영역

Barrier는 장벽이라는 의미 그대로 데이터의 안전한 사용을 위한 강철벽이라고 생각하면 된다. 스토리지 백엔드 간에 흐르는 모든 데이터는 Barrier를 통과한다. Barrier는 암호화된 데이터만을 기록하며, 데이터가 필요한 시점에 복호화(암호해독)가 되도록 한다.

Barrier는 시작하면 “sealed” 상태가 되고 모든 데이터를 보호하는데 사용되는 암호화 키가 생성된다.

  • 암호화 키는 마스터 키로 보호된다.
  • 기본적으로 Sharmir’s secret sharing algorithm을 사용하여 마스터 키를 5개로 분할하여 공유되고, 마스터 키를 재구성 하는데 이 중 3개가 필요하다.

Barrier가 “unsealed” 되어야만 내부 데이터에 접근 할 수 있다.

Core
시스템을 통한 요청 흐름을 관리하고 ACL을 적용하며 감사 로깅이 수행되는지 확인하는 데 사용된다.
Secret Engine

데이터를 저장, 생성, 암호화하는 구성요소 이다. “kv(key/value) secret engine” 같은 간단한 secret engine들은 쿼리할 때 단순하게 key&value 형식으로 데이터를 저장하고 읽을 수 있으며, 좀 더 복잡한 secret engine들은 서비스에 연결하고 요청을 하는 시점에서 동적으로 자격증명을 생성할 수도 있다.

  • 다양한 secret engine 목록 : Active Directory, AliCloud, AWS, Azure, Consul, Cubbyhole, Database, Google Cloud, Google Cloud KMS, KMIP, Key/Value, Identity, Nomad, PKI, RabbitMQ, SSH, TOTP, Transit
Policy

정책이란 vault에 로그인한 유저가 어떤 기능을 사용할 수 있느냐를 정의하는 것이다. 정책은 hcl 문법으로 작성한다.

- 작성 예
				
					# /my-policy.hcl

# Normal servers have version 1 of KV mounted by default, so will need these
# paths:
path "secret/*" {
  capabilities = ["create"]
}
path "secret/foo" {
  capabilities = ["read"]
}
 
# Dev servers have version 2 of KV mounted by default, so will need these
# paths:
path "secret/data/*" {
  capabilities = ["create"]
}
path "secret/data/foo" {
  capabilities = ["read"]
}
				
			
  • “secret/ 의 모든 경로에 쓰기가 가능하지만 secret/foo 는 읽기만 가능하다” 라는 정책이다.
  •  vault는 경로 기반 secrets engine 사용 패턴이므로 경로에 대해서 정책을 정하여 엄격하게 관리할 수 있다.
Storage Backend
Barrier 영역 밖에 있으므로 신뢰할 수 없지만, 암호화된 데이터를 지속적으로 저장하는 데 사용된다.
Auth & Secret engine

Auth 모듈과 Secret engine은 원하는 대로 선택하여 구성할 수 있다.

  • Vault v1.6.2에 기본 등록된 auth/secret 플러그인 목록
				
					"data": {
    "auth": [
      "alicloud",
      "app-id",
      "approle",
      "aws",
      "azure",
      "centrify",
      "cert",
      "cf",
      "gcp",
      "github",
      "jwt",
      "kerberos",
      "kubernetes",
      "ldap",
      "oci",
      "oidc",
      "okta",
      "pcf",
      "radius",
      "userpass"
    ],
    "database": [
      "cassandra-database-plugin",
      "couchbase-database-plugin",
      "elasticsearch-database-plugin",
      "hana-database-plugin",
      "influxdb-database-plugin",
      "mongodb-database-plugin",
      "mongodbatlas-database-plugin",
      "mssql-database-plugin",
      "mysql-aurora-database-plugin",
      "mysql-database-plugin",
      "mysql-legacy-database-plugin",
      "mysql-rds-database-plugin",
      "postgresql-database-plugin",
      "redshift-database-plugin"
    ],
    "secret": [
      "ad",
      "alicloud",
      "aws",
      "azure",
      "cassandra",
      "consul",
      "gcp",
      "gcpkms",
      "kv",
      "mongodb",
      "mongodbatlas",
      "mssql",
      "mysql",
      "nomad",
      "openldap",
      "pki",
      "postgresql",
      "rabbitmq",
      "ssh",
      "totp",
      "transit"
    ]
  },
				
			
Vault auth/secret engine 설정 방법
1. auth 설정 방법
  • $ vault auth enable -path=<mount_path> <auth plugin_name>
  • GitHub 인증 예
				
					$ vault auth enable -path=github github
Success! Enabled github auth method at: github/
				
			
    • GitHub 인증 설정
      • 인증 범위는 github에서 구성한 organization(justin-vault-test) 내의 유저로 설정한다.
      • 그 다음 명령어는 조직의 모든 사용자에게 default와 test-policy 정책을 매핑하게 한다.
				
					$ vault write auth/github/config organization=justin-vault-test
Success! Data written to: auth/github/config
 
$ vault write auth/github/map/teams/my-team value=default,test-policy
Success! Data written to: auth/github/map/teams/my-team
				
			
    • GitHub 인증을 통한 토큰 얻기
      • organization 구성 시 생성된 GitHub 토큰을 이용해서 로그인한다.
				
					// 위 github token을 복사하여 사용
$ vault login -method=github token=0c9c83b96e1c3b8f8f0997801f27e8fa85cb58ab
Success! You are now authenticated. The token information displayed below
is already stored in the token helper. You do NOT need to run "vault login"
again. Future Vault requests will automatically use this token.

Key                    Value
---                    -----
token                  s.jjZzLQMJprfYstAmc7DoQvzT
token_accessor         NL5ua6DUE6E2AfSsPb37sJLH
token_duration         768h
token_renewable        true
token_policies         ["default"]
identity_policies      []
policies               ["default"]
token_meta_org         justin-vault-test
token_meta_username    SungMin-Ha
				
			
2. secret engine 설정 방법
  • $ vault secrets enable -path=<mount_path> <secret engine plugin_name>
  • AWS secret engine 활성화 예
				
					$ vault secrets enable -path=aws aws
Success! Enabled the aws secrets engine at: aws/
				
			

블록체인에 데이터 기록, 얼마나 쉽게 할 수 있을까요?

블록체인 기반 이벤트 추적 서비스 Trace의 데모를 확인해보세요.

Vault server 맛보기

CLI
				
					$ vault operator init
Unseal Key 1: 6VoUsGk140OnDi2Z3OcXBJyr/cS3Zv+gnAGDOteiOeFO
Unseal Key 2: 4wrk+3AX7Z39BKSZcAsGNO+5Zsn1s4MjWK/2N5o0Tc7U
Unseal Key 3: HIqhJ9p5rRaYjN8Rjhm6bDMO+zVlyy3Tc2HD+0MeY6dJ
Unseal Key 4: hkmXS/d6Wdmg4xkyJVLlWJP8L8uEKhmKbOH0nOm1PNfE
Unseal Key 5: /GJp/G+7aoMjTqarPqmxYtN+oSEw38C4kWZNQNuw6zsf
  
Initial Root Token: s.eRWvoMPfKdxk2ja7kqIlSuR6
  
Vault initialized with 5 key shares and a key threshold of 3. Please securely
distribute the key shares printed above. When the Vault is re-sealed,
restarted, or stopped, you must supply at least 3 of these keys to unseal it
before it can start servicing requests.
  
Vault does not store the generated master key. Without at least 3 key to
reconstruct the master key, Vault will remain permanently sealed!
  
It is possible to generate new unseal keys, provided you have a quorum of
existing unseal keys shares. See "vault operator rekey" for more information.
				
			
실행됨과 동시에 Unseal Key 5개와 Root token이 생성된다.
  • unseal을 하려면 임계값 개수(최소)의 unseal key 3개를 소유하고 있어야 한다.
  • unseal api를 세번 호출하여 성공해야 unseal이 된다.
    • Unseal 예
				
					$ vault operator unseal 6VoUsGk140OnDi2Z3OcXBJyr/cS3Zv+gnAGDOteiOeFO
$ vault status
Key                Value
---                -----
Seal Type          shamir
Initialized        true
Sealed             true   # <-- true 로 해제되지 않음을 확인 가능
Total Shares       5
Threshold          3
Unseal Progress    1/5
Unseal Nonce       n/a
Version            1.6.2
Storage Type       inmem
HA Enabled         false
$ vault operator unseal hkmXS/d6Wdmg4xkyJVLlWJP8L8uEKhmKbOH0nOm1PNfE
$ vault operator unseal 4wrk+3AX7Z39BKSZcAsGNO+5Zsn1s4MjWK/2N5o0Tc7U
$ vault status
Key                    Value
---                    -----
Seal Type              shamir
Initialized            true
Sealed                 false # <-- false 로 해제됨을 확인 가능
Total Shares           5
Threshold              3
Version                1.6.2
Cluster Name           vault-cluster-6075fd5f
Cluster ID             c08caafe-09e6-c546-3216-b30a5d289ece
HA Enabled             falseVault에는 기본 해지 메커니즘이 있기 때문에 동적 비밀은 사용 후 즉시 해지 될 수 있으므로 비밀이 존재하는 시간을 최소화 할 수 있습니다.
				
			
Initial Root token
Node.js 에서 vault client 사용 예
  • node-vault
    • install 및 vault init & unseal
				
					// install
// make sure to use node.js version >= 6
$ npm install node-vault


// init and unseal
var options = {
  apiVersion: 'v1', // default
  endpoint: 'http://127.0.0.1:8200', // default
  token: '1234' // optional client token; can be fetched after valid initialization of the server
};

// get new instance of the client
var vault = require("node-vault")(options);

// init vault server
vault.init({ secret_shares: 1, secret_threshold: 1 })
.then( (result) => {
  var keys = result.keys;
  // set token for all following requests
  vault.token = result.root_token;
  // unseal vault server
  return vault.unseal({ secret_shares: 1, key: keys[0] })
})
.catch(console.error);
				
			

Vault 의 Master Key 관리 방식

Shamir Secret Sharing을 사용한다.
  • 비밀(정보)을 여러 조각으로 쪼개서 일정 개수 이상의 조각이 모였을 때만 비밀(정보)을 다시 복원할 수 있도록 하는 방법이다.
    1. 하나의 secret을 여러 조각으로 나누어 분산시켜 저장한다. (shares)
    2. 원래의 secret을 복원하기 위해서는 반드시 일정한 수 이상의 share가 필요하다. (threshold)
  • Vault는 초기화 단계에 5개의 키가 생성되고 이 중 최소 3개가 있어야 Master Key로 복원된다. 때문에 DB나 Vault 서버가 통째로 털려도 이 키 3개가 없다면 공격자는 정보를 볼 수 없게 된다.
그렇다면 Shamir Secret Sharing 방식은 완벽하게 안전할까?
  • 완벽하지 않다! 그렇기에 보완하기 위한 알고리즘들이 계속 등장하고 있다.
				
					Shamir’s secret sharing의 특징은 모든 구성원이 나눠갖는 비밀이
서로 유일(unique)하며 평등하다는 것입니다.
또한 threshold 이상의 조각만 있으면 secret를 복구할 수 있기 때문에
일부 비밀 조각이 유실되더라도 안전하고, 소수의 배신자가 있더라도 secret을 복구할 수 없습니다.

이런 특징들은 한계점이 되기도 합니다.
모든 shared secret이 동등하기 때문에 비밀을 나눌 때 보안 등급에 따라 나누기가 힘들고,
비밀 조각을 제공할 때 일부러 틀린 비밀 값을 주는 것을 방지할 수 없으며 비밀을 복구할 때도
누군가 오염된 값을 줘서 비밀이 제대로 복구되었는지 알 수 없습니다.

Shamir’s secret sharing의 단점들은 이후에 나온 여러 연구들에 의해 보강 되었습니다.
1987년 Feldman과 Benaloh는 shared secret 소유지들이
자신의 share를 검증할 수 있는 verifiable secret sharing scheme을 각각 만들었습니다.
1996년 Stadler와 1999년 Schoenmakers, 2004년에는 Pederson이 자신의 share 뿐 아니라
다른 사람의 share도 검증 가능한 publicly verifiable secret sharing 알고리즘을 각각 만들었습니다.
				
			

Dynamic Secrets

key/value secret도 지원하지만, dynamic secrets (동적 비밀정보) 방식도 지원한다. (각 secret engine에서 생성)

  • dynamic secrets는 access 시점에 secret key가 생성된다. 즉, access 전까지는 secret key가 존재하지 않는다는 말이다. 때문에 서로 다른 클라이언트 간에 secret key 탈취에 대한 염려가 없다.
  • Vault에는 기본 해지 메커니즘이 있기 때문에 동적 비밀은 사용 후 즉시 해지 될 수 있으므로 비밀이 존재하는 시간을 최소화 할 수 있다.
  • 보통 secret 정보를 app 자체가 갖고 있거나 외부 저장소로부터 획득하여 사용했다면, vault를 사용할 경우 secret 정보를 동적으로 생성하고 app은 이를 사용하다가 필요가 없을 경우 폐기 또는 vault에 의해서 스스로 삭제될 수 있어 더 안전하다고 할 수 있다.
  • app은 secret 정보가 필요할 때마다 vault를 이용하면 된다.
- dynamic secret 생성 예 (AWS secrets engine 사용)
  1. AWS에 루트 사용자로 가입하고 로그인 한다.
  2. access key와 secret key를 생성한다.
    • 우측 상단 본인 아이디 메뉴를 열고 내 보안 자격 증명 선택 → 액세스 키 항목에서 만들 수 있다.
  3. secret engine을 aws로 설정하고 access key와 secret key를 Vault에 등록한다.
				
					$ vault secrets enable -path=aws aws
Success! Enabled the aws secrets engine at: aws/

$ vault write aws/config/root \
>     access_key=$AWS_ACCESS_KEY_ID \
>     secret_key=$AWS_SECRET_ACCESS_KEY \
>     region=us-east-1
Success! Data written to: aws/config/root
				
			

4. AWS API를 통해 IAM user를 생성할 때 적용할 정책을 role에 매핑한다. “my-role” 에 대한 자격 증명을 요청하면 자격 증명을 생성하고 IAM 정책을 연결하도록 설정한다.

				
					$ vault write aws/roles/my-role \
        credential_type=iam_user \
        policy_document=-<<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Stmt1426528957000",
      "Effect": "Allow",
      "Action": [
        "ec2:*"
      ],
      "Resource": [
        "*"
      ]
    }
  ]
}
EOF
				
			

5. 자격 증명을 요청해 보면 요청 할 때마다 새로운 IAM user와 access key, secret key가 생성 되는 것을 확인할 수 있다.

    • Vault에 의해 768시간 후 자동으로 삭제된다.
				
					$ vault read aws/creds/my-role
Key                Value
---                -----
lease_id           aws/creds/my-role/jIYSHn55DdCTLj4b4N9nXu1B
lease_duration     768h
lease_renewable    true
access_key         AKIAU6VQQXFVDHZTHE4S
secret_key         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
security_token     <nil>

$ vault read aws/creds/my-role
Key                Value
---                -----
lease_id           aws/creds/my-role/QupnHVvlej2lbj3Zf0J4zAVv
lease_duration     768h
lease_renewable    true
access_key         AKIAU6VQQXFVGNFZ42EM
secret_key         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
security_token     <nil>

$ vault read aws/creds/my-role
Key                Value
---                -----
lease_id           aws/creds/my-role/Hl8DaUkhud9mNHw5RtEeuKFn
lease_duration     768h
lease_renewable    true
access_key         AKIAU6VQQXFVJWFKOEVO
secret_key         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
security_token     <nil>
				
			
  1. 실제로 AWS 사용자 탭에 가보면 새로운 사용자들이 추가되어 있는 것을 볼 수 있다.

마치는 말

지금까지 민감 데이터의 관리 방법중 한 가지인 볼트 서비스에 대해 간략히 알아보았다. 실제 해시코프의 볼트(Vault) 서비스는 위에 언급한 내용 외에도 많은 기능을 제공하고 있다. 현재 루니버스에서도 Vault를 제공하고 있다. 다음 글에서는 루니버스의 볼트 서비스에 대해 살펴본다.

열번 읽는것보다 한번 보고 듣는게 이해하기 쉽습니다.

루니버스는 블록체인을 서비스에 쉽고 빠르게 도입할 수 있는 BaaS(Blockchain as a Service)입니다.
간단한 데모를 보시면 루니버스를 더 쉽게 이해하실 수 있습니다.
지금 바로 데모를 신청해 보세요.
Share on facebook
Facebook
Share on twitter
Twitter
Share on linkedin
LinkedIn

Related Posts

4~8F, 83, Uisadang-daero, Yeongdeungpo-gu, Seoul, Republic of Korea, 07325
Ceo. Park Jaehyun
Business Registration No. 694-86-01434
Online Marketing Report No. 2019-Seoul Gangnam-02255
Tel. +82-2-568-2560

ⓒ Lambda256 Inc, All Rights Reserved.