บทที่ 3 · Part 1 — Security Foundations
Cryptography, Keys & Secrets
เลือก encryption, hash, MAC และ signature ให้ตรงเป้าหมาย พร้อม TLS, envelope encryption, KMS/HSM และ key lifecycle
ทีมพัฒนาเข้ารหัส customer database และเปิด TLS ทุก endpoint จึงเชื่อว่าข้อมูลสำคัญปลอดภัย แต่ attacker ที่ได้ signing key จาก CI log สามารถสร้าง webhook ว่า payout สำเร็จได้เอง โดยไม่ต้องถอดรหัส database แม้แต่ byte เดียว
Cryptography ไม่ใช่เกราะคำเดียว: encryption, hashing, MAC และ digital signature รับประกันคนละ property ถ้าเลือก primitive ผิดหรือดูแล key ไม่ดี algorithm ที่แข็งแรงที่สุด ก็ช่วยไม่ได้
Learning Outcomes
- เลือก encryption, hash, password hash, MAC และ signature ให้ตรง security goal ได้
- อธิบาย nonce, IV, salt, tag และ Additional Authenticated Data ได้
- เข้าใจ TLS ว่าป้องกันอะไรและไม่แทน application authorization อย่างไร
- ออกแบบ envelope encryption ด้วย KMS/HSM และวางแผน key rotation ได้
- จัดการ secret ตั้งแต่สร้างจน revoke/destroy โดยลด blast radius
- มองเห็น crypto failure ที่เกิดจาก protocol และ operation ไม่ใช่เฉพาะ algorithm
Begin with the Security Goal
ก่อนถามว่า “ใช้ AES หรือ RSA” ให้ระบุ property ที่ต้องการ:
| Goal | คำถาม | Primitive ที่มักใช้ |
|---|---|---|
| Confidentiality | ผู้ไม่มี key อ่านข้อมูลได้ไหม | authenticated encryption |
| Integrity | ข้อมูลถูกแก้ระหว่างทางหรือไม่ | MAC, signature หรือ AEAD tag |
| Authenticity | ใครเป็นผู้สร้าง message | MAC หรือ digital signature ตาม trust model |
| Password verification | ตรวจ password โดยไม่เก็บ password ได้อย่างไร | password hashing function |
| Fingerprint | ข้อมูล 2 ชุดเหมือนกันไหม | cryptographic hash |
| Non-repudiation context | พิสูจน์ผู้ถือ private key พร้อม policy/evidence อื่น | digital signature + key governance |
คำว่า “encrypted” ไม่ได้แปลว่า integrity ปลอดภัยเสมอ encryption mode เก่าบางแบบทำให้ ciphertext ถูกแก้แล้ว plaintext เปลี่ยนโดยตรวจไม่พบ ระบบใหม่ควรใช้ authenticated encryption ผ่าน library และ primitive ที่ผ่านการ review
Encoding Is Not Encryption
Base64, hexadecimal และ URL encoding เปลี่ยนรูปข้อมูลเพื่อส่งหรือแสดงผล ไม่ได้ใช้ secret และย้อนกลับได้ทันที
plaintext: account=1234567890
base64: YWNjb3VudD0xMjM0NTY3ODkw
การใส่ API key ใน Base64 header ไม่ได้ปกปิด key การป้องกันมาจาก TLS และการจัดการ credential ไม่ใช่ encoding
Know the Core Primitives
Cryptographic Hash
hash แปลง input ขนาดใดก็ได้เป็น digest ขนาดคงที่และออกแบบให้ย้อนกลับหรือหา 2 input ที่ชนกันได้ยาก ใช้ตรวจ fingerprint, content addressing และเป็นส่วนประกอบของ protocol
hash ธรรมดา ไม่พิสูจน์ผู้ส่ง เพราะ attacker ที่แก้ message ได้ก็ hash ค่าใหม่ได้
message ── hash ──> digest
อย่าใช้ fast hash เก็บ password
SHA-256 ถูกออกแบบให้เร็ว ซึ่งเป็นข้อดีสำหรับไฟล์แต่เป็นข้อเสียสำหรับ password attacker สามารถลอง candidate จำนวนมากต่อวินาที ต้องใช้ password hashing function ที่จงใจให้ช้าและใช้ memory สูงตาม baseline ปัจจุบันขององค์กร
Password Hashing
password storage ต้องใช้ function เช่น Argon2id, scrypt, bcrypt หรือ PBKDF2 ตาม platform, ข้อกำกับ และ library ที่องค์กรอนุมัติ โดยมีหลักสำคัญ:
- สร้าง salt แบบสุ่มไม่ซ้ำ ต่อ credential เพื่อกัน precomputed table และ hash ซ้ำ
- เก็บ algorithm/version/parameters กับ hash เพื่อ upgrade ได้
- ปรับ cost จาก benchmark บน production-class hardware ไม่ hardcode ตามบทความเก่า
- จำกัด online guessing ด้วย throttling และ breached-password detection
- rehash เมื่อผู้ใช้ login สำเร็จหลัง baseline เปลี่ยน
pepper เป็น secret ระดับระบบที่เก็บแยกจาก password database ได้ แต่เพิ่ม lifecycle และ recovery risk ถ้า pepper หาย credential ทั้งหมดอาจใช้ไม่ได้ ถ้ารั่วต้องวางแผน reset/rotation ที่ชัดเจน
stored credential = algorithm + parameters + salt + password hash
Message Authentication Code
MAC ใช้ shared secret เพื่อยืนยันว่า message มาจากผู้ถือ secret และไม่ถูกแก้ HMAC เป็นรูปแบบ ที่ใช้แพร่หลาย เหมาะเมื่อ 2 ฝั่งไว้ใจกันและไม่ต้องแยกว่าใครในกลุ่มเป็นผู้ลงนาม
ตัวอย่าง verify webhook ใน Go:
func validSignature(body, received, secret []byte) bool {
mac := hmac.New(sha256.New, secret)
_, _ = mac.Write(body)
expected := mac.Sum(nil)
return hmac.Equal(expected, received)
}
hmac.Equal เปรียบเทียบโดยลด timing leak แต่ signature ที่ถูกต้องยังไม่กัน replay
protocol ต้องตรวจ timestamp/nonce, event ID, destination context และ idempotency ด้วย
Digital Signature
digital signature ใช้ private key ลงนามและ public key ตรวจสอบ จึงกระจาย verifier ได้โดยไม่แจก ความสามารถในการลงนาม เหมาะกับ artifact signing, asymmetric webhook trust หรือ token บางชนิด
signature ไม่ได้เข้ารหัส message ทุกคนยังอ่านข้อความได้ และคำว่า non-repudiation ต้องอาศัย identity proofing, key custody, timestamp, policy และ audit นอก algorithm ด้วย
Authenticated Encryption
AEAD เช่น AES-GCM หรือ ChaCha20-Poly1305 ให้ Confidentiality และ Integrity พร้อมกัน ผลลัพธ์ประกอบด้วย ciphertext และ authentication tag
AEAD.Encrypt(key, nonce, plaintext, additionalData)
→ ciphertext + authentication tag
Additional Authenticated Data (AAD) ไม่ถูกเข้ารหัสแต่ถูกผูกกับ tag เช่น tenant ID, record type, schema version หรือ transaction ID ทำให้ attacker ย้าย ciphertext ไปใช้ต่าง context โดยไม่ถูกตรวจพบไม่ได้
Treat Nonces and Randomness as Critical Inputs
nonce หรือ IV ไม่จำเป็นต้องเป็น secret แต่มีเงื่อนไขเฉพาะ algorithm หลาย mode ห้ามใช้ nonce ซ้ำกับ key เดิม เพราะอาจทำลาย Confidentiality และ Integrity อย่างรุนแรง
หลักปฏิบัติ:
- ใช้ cryptographically secure random generator จาก platform
- ให้ library สร้าง nonce เมื่อทำได้
- อย่าใช้ timestamp, counter ที่ reset ได้ หรือ
math/randแทน secure random - เก็บ nonce กับ ciphertext ตาม format ที่ version ได้
- rotate key ก่อนชน limit การใช้งานที่กำหนดโดย primitive/library
ตัวอย่าง AES-GCM เพื่อแสดงโครงสร้างข้อมูล ไม่ใช่ encryption framework สำหรับคัดลอกทั้งระบบ:
func encrypt(key, plaintext, aad []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
aead, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
nonce := make([]byte, aead.NonceSize())
if _, err := cryptorand.Read(nonce); err != nil {
return nil, err
}
// Prefix nonce so decrypt can recover it; key ID and format version
// should live in an authenticated envelope around this value.
return aead.Seal(nonce, nonce, plaintext, aad), nil
}
production design ยังต้องตอบเรื่อง key source, key ID, format version, error handling, usage limit, rotation, access control, telemetry และ backup/recovery
Understand What TLS Provides
TLS โดยทั่วไปให้:
- Confidentiality ของข้อมูลระหว่าง endpoints ที่สร้าง connection
- Integrity ป้องกัน traffic ถูกแก้โดยไม่ตรวจพบ
- Server authentication ผ่าน certificate chain และ hostname validation
- Client authentication เมื่อออกแบบ mTLS
TLS ไม่ได้บอกว่า user มีสิทธิ์ถอนเงิน, request เป็น business action ที่ถูกต้อง หรือ server หลัง reverse proxy จะไม่ log plaintext และ TLS termination เปลี่ยน trust boundary: ต้องรู้ว่า traffic ถูกถอดรหัสที่ไหน และ hop ถัดไปป้องกันอย่างไร
ปิด certificate validation เพื่อให้ test ผ่านคือการปิด authentication
option แบบ InsecureSkipVerify, trust-all certificate หรือ hostname bypass ทำให้ attacker
ที่คั่นกลาง connection แสดง certificate ใดก็ได้ ควรแก้ trust store, certificate lifecycle
หรือ test PKI แทนการปิด verification
Use mTLS for the Right Boundary
mTLS พิสูจน์ client certificate ที่ transport layer เหมาะกับ workload หรือ partner connection แต่ยังต้องมี application authorization:
- certificate นี้แทน workload/partner ใด
- เรียก operation และ resource ใดได้
- rotate/revoke อย่างไร
- proxy ส่ง verified identity ต่ออย่างไรโดยปลอม header ไม่ได้
Layer Data Protection
“Encryption at rest” อาจหมายถึงหลายชั้นและป้องกัน attacker คนละแบบ:
| Layer | ป้องกันได้ดี | ไม่ได้ป้องกันโดยตัวมันเอง |
|---|---|---|
| Disk/volume encryption | disk/snapshot ถูกนำออกโดยไม่มี key | application หรือ DB admin ที่อ่านข้อมูลผ่านระบบปกติ |
| Database-managed encryption | file, backup และ storage media | query ที่มีสิทธิ์และ SQL injection ผ่าน application |
| Application field encryption | จำกัด plaintext เฉพาะ service ที่มี key | compromised service ที่ได้รับ decrypt permission |
| Client-side encryption | cloud/service ไม่เห็น plaintext บางกรณี | compromised client และ metadata ที่ไม่ถูกเข้ารหัส |
| Tokenization | ลดระบบที่ต้องถือข้อมูลจริง | token vault compromise และ mapping abuse |
เลือก layer จาก threat model ไม่ใช่เพียงข้อกำหนดว่า “ต้อง encrypted” และเก็บ metadata เช่น identifier, index, log และ backup เข้า data-flow analysis ด้วย
Use Envelope Encryption
การส่งข้อมูลทุก byte เข้า KMS โดยตรงช้า, แพง และติด service limit Envelope encryption แยก key เป็น 2 ระดับ:
- Data Encryption Key (DEK) เข้ารหัสข้อมูลจริงแบบ local
- Key Encryption Key (KEK) ใน KMS/HSM เข้ารหัสหรือ wrap DEK
record ที่เก็บควรมี format version, algorithm identifier, encrypted DEK, KEK/key ID, nonce, ciphertext, tag และ authenticated context เท่าที่จำเป็น การมี key ID ทำให้ decrypt ข้อมูลเก่าหลัง rotate KEK ได้
KMS and HSM Solve Different Layers
KMS ให้ API และ policy สำหรับสร้าง/ใช้/rotate/audit key โดยมักใช้ HSM ใต้บริการ HSM คือ boundary สำหรับ key operation ที่ต้องการ assurance และ control เฉพาะ การใช้ KMS/HSM ไม่ได้แก้ทุกอย่าง:
- workload ที่มี decrypt permission ยังขอ plaintext ได้
- policy กว้างเกินทำให้ blast radius ใหญ่
- log อาจเก็บ plaintext ก่อน encryption
- key deletion ที่ไม่วางแผนทำให้ข้อมูลกู้ไม่ได้
ให้ผูก decrypt permission กับ workload identity, key context, environment และ operation พร้อม log การใช้ key แต่ อย่า log plaintext key หรือ secret
Design the Key Lifecycle
key มีวงจรชีวิต ไม่ใช่ไฟล์ config:
ทุก key class ต้องตอบ:
- ใครสร้างและ entropy มาจากไหน
- key material อยู่ที่ใดและออกจาก boundary ได้หรือไม่
- workload ใดใช้ operation อะไรได้
- key ID/version ถูกผูกกับ ciphertext หรือ signature อย่างไร
- rotate แบบใด: เปลี่ยน key สำหรับข้อมูลใหม่, re-wrap หรือ re-encrypt ข้อมูลเก่า
- revoke เมื่อ compromise อย่างไรและใช้เวลานานเท่าไร
- backup/recovery ทดสอบแล้วหรือยัง
- ใครอนุมัติ destruction และพิสูจน์ได้อย่างไร
Rotation ไม่เท่ากับลบ key เก่า
ถ้า ciphertext, backup หรือ signature เก่ายังต้อง verify/decrypt การลบ key ทันทีจะทำลาย Availability และ auditability ต้องแยกสถานะ “ห้ามใช้สร้างข้อมูลใหม่” ออกจาก “ยังใช้เปิดข้อมูลเก่าได้” และมี retention decision จากเจ้าของข้อมูล
Manage Secrets as Credentials
secret ครอบคลุม API key, database password, OAuth client secret, private signing key, recovery code และ bootstrap credential
หลักสำคัญ:
- อย่าใส่ใน source control, image, mobile app หรือ frontend bundle
- ใช้ workload identity ขอ short-lived credential เมื่อ platform รองรับ
- เก็บ long-lived secret ใน secret manager ที่มี access control และ audit
- inject เมื่อ runtime และจำกัดผู้/ระบบที่อ่านได้
- redact จาก log, trace, error, crash dump และ support bundle
- rotate อัตโนมัติพร้อมรองรับช่วงที่ old/new credential overlap
- ตรวจ secret leakage และ revoke ไม่ใช่เพียงลบจาก Git commit ล่าสุด
environment variable ดีกว่า hardcode ใน image แต่ยังอาจรั่วผ่าน debug endpoint, process dump, deployment manifest หรือ support tooling จึงไม่ใช่ secret manager โดยตัวมันเอง
Never Ship a Shared Secret in a Mobile App
ไฟล์ใน mobile package ถูกอ่านและ reverse engineer ได้ secret ที่ฝังใน app จึงถือว่าแจกให้ผู้โจมตี ใช้ public identifier ได้ แต่การพิสูจน์ app/device ต้องใช้ platform attestation, user authentication และ backend risk decision โดยเข้าใจข้อจำกัด ซึ่งจะลงรายละเอียดใน Part 3
Bind Cryptography to Context
failure จำนวนมากไม่ได้เกิดจาก primitive แตก แต่เกิดจาก message เดียวถูกนำไปใช้อีกบริบท:
- signature ของ “จ่าย merchant A” ถูก replay เป็นคำสั่งรอบใหม่
- ciphertext ของ tenant A ถูกย้ายไป record ของ tenant B
- access token สำหรับ API หนึ่งถูกยอมรับโดยอีก API
- signed callback ของ sandbox ถูกส่งเข้า production
ผูก environment, tenant, purpose, issuer, audience, transaction ID และ version เข้า AAD, signed message หรือ protocol validation ตามเหมาะสม ใช้ canonical serialization ที่ 2 ฝั่งตรงกัน และกำหนด replay/idempotency semantics แยกจาก signature
Prepare for Crypto Agility
algorithm, key size, certificate chain และ provider เปลี่ยนได้ การเตรียม crypto agility หมายถึง:
- format มี version และ algorithm/key ID
- reader รองรับข้อมูลเก่าระหว่าง migration
- policy อยู่ในจุดควบคุม ไม่กระจาย magic string ทั่ว codebase
- inventory รู้ว่า key/algorithm ใดปกป้อง data ที่ไหน
- migration และ rollback ถูกทดสอบกับ backup
ไม่ได้หมายถึงเปิดให้ client เลือก algorithm ใดก็ได้ เพราะ algorithm negotiation ที่กว้างเกิน อาจสร้าง downgrade attack
Common Failure Patterns
| Failure | ทำไมอันตราย | แนวทางที่ดีกว่า |
|---|---|---|
| สร้าง cipher/protocol เอง | composition และ edge case review ยาก | ใช้ high-level library/protocol ที่ผ่าน review |
| key อยู่ข้าง ciphertext | attacker ได้ทั้ง 2 อย่างพร้อมกัน | แยก key boundary และใช้ KMS/HSM |
| reuse nonce | ทำลาย security property ของ mode บางชนิด | secure generation + usage limit |
| encrypt แต่ไม่ authenticate | ciphertext อาจถูกแก้ | ใช้ AEAD |
| signature ครอบเฉพาะ body บาง field | attacker เปลี่ยน destination/context | canonical signed envelope |
| key เดียวทุก environment/tenant | compromise 1 จุดกระทบทั้งหมด | แยก key ตาม blast radius ที่มีเหตุผล |
| rotate โดยไม่มี key ID | เปิดข้อมูลเก่าไม่ได้ | versioned envelope และ key ring |
| log decrypt error พร้อม payload | ข้อมูลรั่วผ่าน observability | structured error ที่ไม่มี secret/plaintext |
Review Checklist
- ระบุ security goal ก่อนเลือก primitive
- แยก encoding, encryption, hashing, MAC และ signature ได้ถูกต้อง
- password ใช้ password hashing function พร้อม unique salt และ upgrade path
- ข้อมูลใหม่ใช้ authenticated encryption ผ่าน approved library
- nonce/random มาจาก cryptographically secure source และไม่ซ้ำตาม requirement
- TLS ตรวจ certificate chain, hostname, time และ policy โดยไม่ใช้ trust-all workaround
- encryption layer ถูกเลือกจาก threat model และรวม log/backup/metadata
- envelope มี format version, key ID และ authenticated context
- key/secret access ผูกกับ workload identity และ least privilege
- มี rotation, revocation, recovery และ destruction procedure ที่ทดสอบแล้ว
- protocol ป้องกัน replay และ cross-context reuse แยกจาก cryptographic verification
- mobile/frontend bundle ไม่มี shared secret
สรุป
Cryptography ที่ปลอดภัยคือการเลือก primitive ให้ตรง property แล้วดูแล key, context, protocol และ lifecycle ให้ครบ Encryption ไม่แทน authorization, signature ไม่กัน replay และ KMS ไม่ช่วยเมื่อ workload ที่ถูกยึดมี decrypt permission กว้างเกินไป