การทำ Data Encryptionด้วย Cloud KMS + Tink (และการใช้งานร่วมกับ BigQuery Encryption Functions)

Wongsathorn Phaopongjan
CJ Express Tech (TILDI)
6 min readAug 16, 2022

การทำ Data encryption หรือ การเข้ารหัสข้อมูล เป็นหนึ่งในวิธีการรักษาความปลอดภัยของข้อมูล ผู้ใช้สามารถเข้าถึงหรือถอดรหัสข้อมูลได้โดยใช้รหัสการเข้าถึงที่ถูกต้องเท่านั้น

ในบทความนี้จะอธิบายหัวข้อดังต่อไปนี้

  • Encryption และ Decryption แบบคร่าวๆ
  • Scenario ตัวอย่าง
  • ทำไมถึงเลือกใช้ Cloud KMS และ Tink?
  • ตัวอย่างการใช้งาน Cloud KMS และ Tink ในการ encrypt data ด้วย Python
  • ตัวอย่างการใช้งาน BigQuery Encryption Function

Encryption และ Decryption แบบคร่าวๆ

อ้างอิงรูปจาก https://www.skyhighsecurity.com/en-us/cybersecurity-defined/tokenization-vs-encryption.html

Encryption (การเข้ารหัส) คือ process ในการแปลงข้อมูลธรรมดาหรือข้อมูลดิบ (หรือ Plaintext) โดยการใข้งาน Cryptographic key และ Encryption Algorithm เพื่อให้ข้อมูลอยู่ในรูปที่ไม่สามารถอ่านได้ (หรือCiphertext)

Decryption (การถอดรหัส) คือ process ตรงกันข้ามกับ Encryption คือการแปลง Ciphertext กลับไปเป็น Plaintext โดยใช้ Keyที่ถูกต้อง

**ในบทความนี้จะได้ไม่เล่าไปถึงประเภทของ encryption รวมไปถึง algorithmต่างๆ

Scenario ตัวอย่าง

ในการใช้งานใน data pipeline ตัวอย่าง เราจะมีการดึงข้อมูลมาจาก Source และข้อมูลจะถูกprocess และจัดเก็บทั้งใน Data Lake (Google Cloud Storage) และ Data Warehouse (BigQuery)

โดยจำเป็นต้องมีการencryptข้อมูลเพราะข้อมูลต้นทางที่ไปดึงมาประกอบไปด้วยข้อมูลของผู้ใช้งาน เช่น ชื่อ ที่อยู่ เบอร์ติดต่อ โดยทางทีมต้องการให้ข้อมูลเหล่านี้ถูกencrypt ก่อนที่จะถูกจัดเก็บใน GCS และ BigQuery

ในแง่ของการใช้งานของ Data user จะมีการนำข้อมูลนี้ไปใช้ในการทำ Dashboardและมีความจำเป็นในเชิงbusinessที่ต้องใช้ข้อมูลจริงที่ถูกdecryptแล้วในบางโอกาส

จากตรงนี้เราสามารถสรุปได้ว่า

  • เราดึงข้อมูลมาและต้อง encrypt ตั้งแต่ใน Airflow ก่อนเอาเข้า GCS โดยเราอาจจะเขียน taskนึงที่เป็น Python Operator เพื่อprocessจุดนั้น >> เขียน Python เพื่อ Encrypt
  • ข้อมูลที่ถูกเก็บบน BigQuery มีความจำเป็นที่จะต้องถูก decrypt ไม่ทางใดก็ทางหนึ่ง เช่น ทำ pipeline เพื่อ decrypt และจัดเก็บข้อมูลอีกชุด หรือ เขียนSQL เพื่อdecryptข้อมูล

ในหัวข้อถัดไปๆจะอธิบายและยกตัวอย่างโดยอิงจาก 2 จุดนี้นะครับ

ทำไมถึงเลือกใช้ Cloud KMS และ Tink?

ก่อนที่เราจะไปดูว่าทำไมเราถึงเลือกใช้ tool 2 ตัวนี้ เรามาดูว่า tool แต่ละตัวคืออะไร และสามารถทำอะไรได้บ้าง

Cloud KMS

KMS หรือชื่อเต็มว่า Key Management Service เป็นserviceตัวนึงบน GCP โดยผู้ใช้งานสามารถเข้าไปสร้าง Keyring และ Key ตามที่ต้องการได้ทั้งผ่าน GCP console และการใช้งานด้วย API หรือ SDK เราสามารถใช้งานkeyเหล่านั้นได้ เช่น encrypt/decrypt text ต่างๆ เป็นต้น

โดยทางทีมมีการใช้งาน GCP เป็นหลักอยู่แล้ว การเลือกใช้ Cloud KMS เลยเป็นตัวเลือกที่ดีตัวนึง

Tink

Tink เป็น library ตัวนึงที่ถูกพัฒนาโดยกลุ่ม Cryptographer และ Security engineer ของ Google โดย Tink จะ provide APIในการทำ Cryptographyต่างๆ

ตัวlibrary รองรับได้หลายภาษา ไม่ว่าจะเป็น Python, Java, Golang และอื่นๆ

สามารถเข้าไปดู repositoryของ Tinkได้ตามlinkนี้

แล้วทำไมถึงจำเป็นต้องใช้ทั้งคู่?

จริงๆแล้วแค่ ​Cloud KMS ตัวเดียวก็สามารถทำ encrypt/decryptได้แบบที่ต้องการได้แล้วผ่านทั้ง gcloud cli และ Python

สามารถเข้าไปดูตัวอย่างได้จากlinkนี้

แต่ถ้าเราจะใช้ท่านี้ เวลาเรา decrypt ข้อมูล เราจำเป็นต้องมี process แยก (อาจจะในรูปแบบของ ​data pipeline หรือ script) ก็คือไม่สามารถ decryptผ่านBigQueryตรงๆได้

เราไปเจอมาว่า ในBigQuery มันมี encryption function ที่สามารถทำสิ่งนี้ได้ โดยเราสามารถเขียน SQL บน BigQuery เพื่อdecryptข้อมูลโดยที่ไม่ต้องดึงข้อมูลออกไปprocessข้างนอกก่อนเลย functionนี้ถูกเรียกว่า AEAD Encryption Function โดยจะเป็นการใช้ร่วมกันระหว่าง key จาก KMS และkeyที่ถูกสร้างแยกอีกชุดนึง (ใช้ KMS เดี่ยวๆไม่ได้)

แต่ก่อนจะไปถึงตรงนั้น อยากจะอธิบายถึงการใช้งานร่วมงานของ KMS และ Tink ก่อน โดยเราอ้างถึงpracticeนึงที่เรียกว่า Envelope encryption

อ้างอิงรูปภาพจาก GCP document : https://cloud.google.com/kms/docs/envelope-encryption

คร่าวๆสั้นๆเลยก็คือ เราจะมี key อยู่ด้วยกัน 2 ชุด

  1. Data Encryption Key (DEK)
  • Key ที่ไว้ encrypt ข้อมูล
  • ใช้ Tink

2. Key Encryption Key (KEK)

  • Key ที่ไว้ wrap key
  • ใช้ Cloud KMS

เราจะใช้ Tink ในการ encrypt plaintext แล้วใช้ Cloud KMS ในการ wrap (encrypt) Tink key อีกทีนึง

ประโยชน์หลักๆจากการใช้ practice นี้คือ ความ secure ที่เพิ่มขึ้น และเรายังสามารถนำเอา ciphertext ที่ถูก encryptด้วยวิธีการนี้ไปใช้งานร่วมกัน BigQuery AEAD Encryption Function ได้อีกด้วย

ตัวอย่างการใช้งาน Cloud KMS และ Tink ในการ encrypt data ด้วย Python

Prerequisite:

  • สร้าง Keyring และ key สำหรับ Cloud KMS โดยให้เลือกใช้ Purpose เป็น Symmetric encrypt/decrypt
  • Install library Tink ผ่าน pip

*หากติดปัญหา install Tinkแล้วเจอปัญหา สามารถเอา Dockerfile นี้ไปรันบน Docker ได้

ใน requirements.txt

tink==1.6.1protobuf==3.20.*

แล้วก็ build image และ mount path อะไรให้เรียบร้อย

docker buildx build --platform=linux/amd64 -t tink-encryption  -f Dockerfile --load .docker run -it -d --platform linux/amd64 -v $(pwd)/scripts:/sc tink-encryptiondocker exec -it <container_id> bash

Generate Tink key

โดยใน sample code ด้านบนจะเป็นการ generate AES_GCM 256 bit key โดยที่ key ชุดนี้ได้ถูก encrypt โดย KMS เรียบร้อยแล้ว

Output json file จากการ generate Tink kety

จริงๆ เราสามารถสร้าง key ที่เป็น plaintext (cleartext keyset) แต่ไม่ recommend เพราะสุดท้ายสิ่งที่เราจะเอาไปใช้คือ encrypted keyset ไม่มีความจำเป็นที่จะต้องเอา keyset แบบโล้นๆมา

Encrypt & Decrypt a string with Tink key

เราสามารถนำ key ที่ generate มาทดลอง encrypt และ decrypt string ได้ โดยในsample code จะเป็นการไปอ่านไปอ่าน encrypted keyset จากไฟล์ที่เราsaveไว้ โดยระบุ gcp_aead ซึ่งก็คือ KMS ที่เราใช้ encryptไป หลังจากนั้นก็ใช้ keyset handle นั้นในการ encrypt และ decrypt string

Output:

Output จากการรัน

จะเห็นได้ว่า function ในการ encrypt/decrypt ของ Tink สามารถทำงานได้อย่างปกติ โดยถ้าในการใช้งานจริงใน pipeline เราสามารถนำเอา ค่า base64 encoded stringไปใช้งานได้ (อาจจะdecodeมันอีกทีให้มันอยู่ในstring format จะได้handleมันได้ง่ายขึ้น)

Code ด้านบนนี้เป็นเพียงตัวอย่างในการใช้งานเพื่อให้ทุกคนเข้าใจไอเดีย ในการใช้งานจริง เราอาจะเก็บ encrypted keyset ไว้ใน database หรือ storage ที่secureเพื่อให้เข้าถึงได้เฉพาะกลุ่ม และอาจจะปรับแต่งcode เพื่อให้เข้ากับ data pipeline ที่เรามีได้

สามารถเข้าไปดูตัวอย่างการใช้งาน Tink กับ Pythonได้จากlinkนี้

ตัวอย่างการใช้งาน BigQuery Encryption Function

หลังจากเราสามารถใช้ Python ในการ encrypt string เรียบร้อยแล้ว เรามาลองดูหลายๆ feature ที่มีใน encryption function

สร้าง keyset

SELECT KEYS.NEW_KEYSET('AEAD_AES_GCM_256');
Output จากการสร้าง keyset ผ่าน BigQuery

การ encrypt ด้วย keyset ที่สร้างผ่าน BigQuery

WITH CustomerKeysets AS (SELECT 1 AS customer_id, KEYS.NEW_KEYSET('AEAD_AES_GCM_256') AS keyset UNION ALLSELECT 2, KEYS.NEW_KEYSET('AEAD_AES_GCM_256') UNION ALLSELECT 3, KEYS.NEW_KEYSET('AEAD_AES_GCM_256')), PlaintextCustomerData AS (SELECT 1 AS customer_id, 'elephant' AS favorite_animal UNION ALLSELECT 2, 'walrus' UNION ALLSELECT 3, 'leopard')SELECTpcd.customer_id,favorite_animal,AEAD.ENCRYPT((SELECT keysetFROM CustomerKeysets AS ckWHERE ck.customer_id = pcd.customer_id),pcd.favorite_animal,CAST(pcd.customer_id AS STRING)) AS encrypted_animalFROM PlaintextCustomerData AS pcd;
Output จากการ encrypt data ด้วย keyset ที่สร้างผ่าน BigQuery

การใช้งาน keyset ร่วมกับ Cloud KMS

จะมี function ที่ชื่อว่า KEYS.KEYSET_CHAIN() ซึ่งจะรับ parameter 2 ตัว

  1. kms_resource_name

URI ของ KMS โดยจะอยู่ในformat ตามนี้

gcp-kms://projects/<gcp_project_id>/locations/<region>/keyRings/<keyring_name/cryptoKeys/<key_name>

2. first_level_keyset

Ciphertext ที่ถูกสร้างจากการ encrypt Tink keyset ด้วย KMS key **ถ้าระบุ KMS key ไม่ถูกหรือไม่ตรงกับ key ที่ใช้ จะไม่สามารถรันได้

ถ้าเรา generate Tink keyset ตามตัวอย่างในหัวข้อก่อนหน้า ก็สามารถใช้ตัว encrypted keyset นั้นได้เลย (เพราะkeyมันถูกencryptมาแล้ว)

-- อ่านด้วย from_base64() เพื่อให้ BigQuery ไม่มองว่ามันเป็น string
from_base64("CiQAvPfp+aB02gfSDHwmau1+aC9SRDIn17neqBAlABAbvyvt6jMShQEAQ7t+eAaNANq9kta5tCMb6/3i9ZUAn+cBfjChlsj83fWpz63cI6qPuJLR8RTbViad2HVu67qUV92RAj3GdYa/xsY17gQVHrAmCf8MpnmpumO+LEVE3HTVvzF4RBIqUpAQfhT2y0WQsqIbjENyBOL1+CE4REopiF5Gg5+Kpx8TE99ayNvB"

โดยปกติแล้ว function นี้ไม่ได้ถูกใช้งานเดี่ยวๆ แต่จะถูกเอาไป replace ในส่วนของ parameter ของ function อื่นๆที่รับค่าของ keyset

การใช้ KMS key ร่วมกับ keyset เพื่อ decrypt ข้อมูล

Scenario: เรามีข้อมูลตัวอย่างที่มี 1 column ที่ระบุ postcode โดยเราได้ทำการ encrypt ข้อมูลด้วย Pythonมาแล้วและimport เข้ามายัง BigQuery

Sample table ที่ถูก encrypt มาก่อนแล้ว

เรา declare และ set value ของ parameter ที่จะถูกใช้ใน KEYS.KEYSET_CHAIN() และเอาไปใข้ใน function ในการ decrypt **ในเทเบิลตัวอย่างนี้ ข้อมูลถูกencryptด้วย keyset ชุดนี้เลยสามารถ decryptออกมาได้

Output จากการ decrypt ด้วย SQL

แต่ในทางปฏิบัติจริงๆ เราคงไม่อยากให้ data user มานั่งqueryแบบยาวๆอย่างนี้ทุกครั้ง โดยเราสามารถสร้าง view ครอบ query นี้ได้

สร้าง view สำหรับ decrypt ข้อมูล

ด้วยความที่การใช้ view ไม่สามารถใช้ร่วมกันกับ declare และ setได้ เราจึงนำค่านั้นไปวางไว้ใน query เลย

อีกสิ่งหนึ่งที่เปลี่ยน ถ้าสังเกตดีๆคือ ในส่วนของ argument ที่เป็น first level keyset แทนที่เราจะใช้ FROM_BASE64() เราเปลี่ยนไปใช้ CAST("<encrypted_keyset>" AS BYTES FORMAT "BASE64") แทน เพราะว่าถ้าเราใส่ FROM_BASE64() ไป ตัวqueryมันจะ error แปลกๆ ก็เลยต้องเปลี่ยนมา cast ซึ่งให้ output ที่ถูกต้องเหมือนกัน

Permission!

เพื่อที่ user จะสามารถเข้ามา query view หรือเรียกใช้งาน decrypt function ที่มีการใช้ KMS key ร่วมอยู่ด้วย จำเป็นต้องมี permission ที่มากพอด้วย โดยuserจำเป็นต้องมี roleCloud KMS CryptoKey Decrypter Via Delegation หรือก็คือเป็นการเรียกใช้ Decrypt operation ผ่าน GCP service อื่นๆ ซึ่งในที่นี้ก็คือผ่าน BigQueryนั่นเอง (แต่ถ้าเกิดเป็น role editor เลยก็จบ ทำได้หมด)

สามารถเข้าไปดู functions อื่นๆได้จากlinkนี้

และวิธีการใช้งานqueryได้จากlinkนี้

สรุป

ในบทความนี้เราได้ walkthrough การใช้งานและตัวอย่างของ Cloud KMS ร่วมกับ Tink และประโยชน์จากการใช้ function ของ BigQuery encryption function ที่ค่อนข้างมีประโยชน์และใช้งานไม่ยาก โดยสามารถนำตัวอย่างนี้ไปปรับใช้กับงานจริงได้

References

Encryption/Decryption

Cloud KMS

Tink

Envelope Encryption

BigQuery AEAD Encryption Functions

--

--