🐟
AWS KMSで暗号・復号する
Key Management Service
AWSのKMSは、データの暗号・復号の他、署名の検証なども出来ます。
自社でクレデンシャルを生成・保存するときは、暗号・復号のデータで得たい情報の取得や、検証処理自体を別の小さいなサービスにしてしまうなどが理想的と言えそうですが、自社のデータストアーで管理する際は、暗号処理を行って、漏洩時のリスクに備える形が最低限必要な対策かと思います。
KMSのキー作成
キーは、暗号化および復号化目的で対称鍵で作成し、キーはKMS側で自動ローテーションする設定です。対称鍵の場合、自動ローテーションが可能で、ローテーションが自動でされるメリットは大きいです。
暗号化
getKeyUserCredentials
について割愛しましたが、AwsCredentialIdentity
を応答する関数で、KMSのキーユーザのクレデンシャルが必要です。
AWSのクライアントのバージョンが異なると実装が異なりますが、流れは以下の通りです。
- クレデンシャルを取得
- KMSクライアントを生成
- 暗号化したいプレーンテキストとKMSのKeyID、暗号化の文脈になる情報をKeyValueで渡す
- 暗号化されたテキストを取得
「暗号化の文脈になる情報」とは、暗号化・復号化する情報へのアクセスの条件を付け加えることができます。例えば、キーをUserId、値をログインしているユーザIDにすることで、暗号・復号の条件にそういった特定のユーザであることを条件付けるということです。
import {DecryptCommand, EncryptCommand, KMSClient} from "@aws-sdk/client-kms"
import {AwsCredentialIdentity} from "@aws-sdk/types"
const awsKmsKeyId = "xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx"
const uint8ToBase64 = (arr: Uint8Array): string =>
Buffer.from(
Array(arr.length)
.fill("")
.map((_, i) => String.fromCharCode(arr[i]))
.join(""), "binary").toString("base64")
const encryptByKms = async (
plaintext: string,
context: Record<string, string>,
): Promise<null | string> => {
const credentials = await getKeyUserCredentials()
if (!credentials) return null
const client = new KMSClient({
region: "ap-northeast-1",
credentials,
})
const result = await client.send(new EncryptCommand({
KeyId: awsKmsKeyId,
Plaintext: new TextEncoder().encode(plaintext),
EncryptionContext: context,
}))
const ciphertextBlob = result?.CiphertextBlob
return ciphertextBlob ? uint8ToBase64(ciphertextBlob) : null
}
const plainText = "アクセストークン"
const userId = "特定のユーザID"
const cipherText = await encryptByKms(plainText, {userId})
復号化
暗号化同様に、クレデンシャルの取得方法については、自社のニーズに合わせて取得ください。
暗号化と同じような流れで復号化していきます。暗号時に条件付けした場合は、復号時も同一の条件である必要があります。
const decryptByKms = async (
ciphertextBlob: string,
context: Record<string, string>,
): Promise<null | string> => {
const credentials = await getKeyUserCredentials()
if (!credentials) return null
const client = new KMSClient({
region: "ap-northeast-1",
credentials,
})
const result = await client.send(new DecryptCommand({
KeyId: awsKmsKeyId,
CiphertextBlob: Buffer.from(ciphertextBlob, "base64"),
EncryptionContext: context,
}))
const plaintext = result?.Plaintext
return plaintext ? Buffer.from(plaintext).toString() : null
}
const cipherText = "暗号化されたテキスト"
const userId = "特定のユーザID"
const plainText = await decryptByKms(cipherText, {userId})
最後に
AWS KMSを使うことで、簡単に自社のニーズに合わせてデータの保管を安全に行うことができます。
Discussion