การทำ Data Encryptionด้วย Cloud KMS + Tink (และการใช้งานร่วมกับ BigQuery Encryption Functions)
การทำ Data encryption หรือ การเข้ารหัสข้อมูล เป็นหนึ่งในวิธีการรักษาความปลอดภัยของข้อมูล ผู้ใช้สามารถเข้าถึงหรือถอดรหัสข้อมูลได้โดยใช้รหัสการเข้าถึงที่ถูกต้องเท่านั้น
ในบทความนี้จะอธิบายหัวข้อดังต่อไปนี้
- Encryption และ Decryption แบบคร่าวๆ
- Scenario ตัวอย่าง
- ทำไมถึงเลือกใช้ Cloud KMS และ Tink?
- ตัวอย่างการใช้งาน Cloud KMS และ Tink ในการ encrypt data ด้วย Python
- ตัวอย่างการใช้งาน BigQuery Encryption Function
Encryption และ Decryption แบบคร่าวๆ
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
คร่าวๆสั้นๆเลยก็คือ เราจะมี key อยู่ด้วยกัน 2 ชุด
- 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 เรียบร้อยแล้ว
จริงๆ เราสามารถสร้าง 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:
จะเห็นได้ว่า 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');
การ 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;
การใช้งาน keyset ร่วมกับ Cloud KMS
จะมี function ที่ชื่อว่า KEYS.KEYSET_CHAIN()
ซึ่งจะรับ parameter 2 ตัว
- 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
เรา declare และ set value ของ parameter ที่จะถูกใช้ใน KEYS.KEYSET_CHAIN()
และเอาไปใข้ใน function ในการ decrypt **ในเทเบิลตัวอย่างนี้ ข้อมูลถูกencryptด้วย keyset ชุดนี้เลยสามารถ decryptออกมาได้
แต่ในทางปฏิบัติจริงๆ เราคงไม่อยากให้ data user มานั่งqueryแบบยาวๆอย่างนี้ทุกครั้ง โดยเราสามารถสร้าง view ครอบ query นี้ได้
ด้วยความที่การใช้ 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
- https://cloud.google.com/kms/docs/envelope-encryption
- https://life.wongnai.com/envelope-encryption-f93837e5309f
BigQuery AEAD Encryption Functions