Cyber 101 · บทที่ 7
การเข้ารหัสเบื้องต้น
การเข้ารหัสคืออะไร และมีกี่แบบ
การเข้ารหัส (cryptography) คือศาสตร์ของการเปลี่ยนข้อมูลให้อ่านไม่ออกสำหรับคนที่ไม่มีกุญแจ เพื่อรักษาความลับ ความถูกต้อง และยืนยันตัวตน มันคือกระดูกสันหลังที่ทำให้เราส่งรหัสผ่าน โอนเงิน และแชตกันได้อย่างปลอดภัยบนอินเทอร์เน็ต บทนี้เราจะเข้าใจว่ามันทำงานอย่างไรในระดับแนวคิด เพื่อจะได้ใช้มันป้องกันระบบของเราเองได้ถูกต้อง
คำศัพท์ 4 คำที่ต้องรู้ก่อน
ก่อนไปต่อ ให้จำสี่คำนี้ให้ขึ้นใจ เพราะจะใช้ตลอดทั้งบท
- Plaintext — ข้อความต้นฉบับที่อ่านออก เช่น "โอนเงินให้แม่ 500 บาท"
- Ciphertext — ข้อความที่เข้ารหัสแล้ว อ่านไม่รู้เรื่อง
- Key (กุญแจ) — ความลับที่ใช้เข้าและถอดรหัส ความปลอดภัยอยู่ที่กุญแจ ไม่ใช่ที่อัลกอริทึม
- Algorithm (อัลกอริทึม) — สูตรคณิตศาสตร์ที่ใช้แปลง เช่น AES, RSA ซึ่งเป็นสาธารณะและถูกตรวจสอบมาแล้ว
หลักสำคัญคือ Kerckhoffs's principle: ระบบที่ดีต้องปลอดภัยแม้ศัตรูจะรู้อัลกอริทึมทุกอย่าง ตราบใดที่กุญแจยังเป็นความลับ นี่คือเหตุผลว่าทำไมการ "ปิดบังอัลกอริทึม" (security through obscurity) จึงไม่ใช่ความปลอดภัยจริง
Symmetric (กุญแจสมมาตร)
ใช้กุญแจ ดอกเดียวกัน ทั้งเข้าและถอดรหัส เร็วมาก เหมาะกับข้อมูลปริมาณมาก เช่น AES-256 ตัวอย่างจริงคือการเข้ารหัสไฟล์ในฮาร์ดดิสก์ ปัญหาคือ "จะส่งกุญแจให้อีกฝ่ายอย่างปลอดภัยได้อย่างไร" โดยไม่ถูกดักระหว่างทาง
Asymmetric (กุญแจอสมมาตร)
มีกุญแจ คู่: กุญแจสาธารณะ (public key) แจกใครก็ได้ และกุญแจส่วนตัว (private key) เก็บเป็นความลับสุดชีวิต ข้อมูลที่ล็อกด้วย public key จะเปิดได้ด้วย private key เท่านั้น เช่น RSA, ECC ช้ากว่าแบบสมมาตรมาก แต่แก้ปัญหาการส่งกุญแจได้
Hybrid (แบบผสม)
ของจริงเกือบทั้งหมด (รวม HTTPS) ใช้แบบผสม: ใช้ asymmetric เพื่อ "ตกลงกุญแจลับร่วมกัน" อย่างปลอดภัยตอนเริ่มต้น แล้วสลับไปใช้ symmetric ที่เร็วกว่าในการรับส่งข้อมูลจริง ได้ทั้งความเร็วและความปลอดภัย
ตัวอย่างเชิงแนวคิด: เข้ารหัสไฟล์ของเราเองด้วยไลบรารีที่ผ่านการตรวจสอบ (Fernet ใช้ AES ข้างใน) โค้ดนี้เพื่อ "ปกป้องข้อมูลของเรา" ไม่ใช่โจมตีใคร
from cryptography.fernet import Fernet
key = Fernet.generate_key() # เก็บกุญแจนี้ให้ดีที่สุด
f = Fernet(key)
token = f.encrypt(b"my secret note") # ได้ ciphertext
plain = f.decrypt(token) # ถอดกลับด้วยกุญแจเดิม
แนวคิดการโจมตี: brute force และอัลกอริทึมที่ล้าสมัย
วิธีโจมตีการเข้ารหัสที่ตรงไปตรงมาที่สุดคือ brute force คือลองกุญแจทุกความเป็นไปได้จนเจอ ถ้ากุญแจสั้นเกินไป (เช่น DES ที่ยาว 56 บิต) คอมพิวเตอร์สมัยนี้เดาได้ในเวลาไม่นาน นอกจากนี้อัลกอริทึมเก่าอย่าง DES, RC4 หรือโหมด ECB มีจุดอ่อนเชิงคณิตศาสตร์ที่ทำให้เดาบางส่วนได้โดยไม่ต้องลองทุกกุญแจ การเข้าใจว่าอะไรอ่อนแอ คือก้าวแรกของการเลือกใช้ของที่แข็งแรง
🛡️ วิธีป้องกัน
- ใช้อัลกอริทึมมาตรฐานที่ยังแข็งแรง: AES-256 สำหรับ symmetric และ RSA 2048 บิตขึ้นไป หรือ ECC (Curve25519) สำหรับ asymmetric
- เลือกโหมดที่ปลอดภัย เช่น AES-GCM (ให้ทั้งความลับและตรวจการถูกแก้ไข) เลี่ยงโหมด ECB เด็ดขาด
- กุญแจยิ่งยาวยิ่งดี ทุกบิตที่เพิ่มคือเวลาที่ผู้โจมตีต้องใช้เพิ่มเป็นเท่าตัว
- อย่าฮาร์ดโค้ดกุญแจไว้ในซอร์สโค้ด ให้เก็บใน environment variable หรือ secret manager
Hashing และ Salt สำหรับรหัสผ่าน
คนมักสับสนระหว่าง hashing กับ encryption แต่มันคนละเรื่องกัน และการเข้าใจผิดตรงนี้คือสาเหตุอันดับต้น ๆ ที่ทำให้ฐานข้อมูลรหัสผ่านรั่วแล้วถูกถอดได้ทั้งหมด บทนี้จะสอนวิธีเก็บรหัสผ่านที่ "ถูกต้อง" ซึ่งเป็นสิ่งที่เว็บของคุณเองต้องทำ
Hashing ต่างจาก Encryption อย่างไร
Encryption เป็นแบบ "สองทาง" เข้ารหัสแล้วถอดกลับได้ถ้ามีกุญแจ ส่วน Hashing เป็นแบบ "ทางเดียว" คือแปลงข้อมูลเป็นสตริงความยาวคงที่ (เช่น SHA-256) แล้ว ถอดกลับไม่ได้ โดยตั้งใจ ข้อมูลเดิมเหมือนกันจะได้ค่า hash เท่ากันเสมอ แต่เปลี่ยนแค่ตัวอักษรเดียวค่าจะเปลี่ยนไปทั้งหมด
เราจึงไม่ควร "เข้ารหัส" รหัสผ่านผู้ใช้ (เพราะถ้ากุญแจรั่ว รหัสผ่านทั้งหมดเปิดหมด) แต่ให้ hash แทน เวลาผู้ใช้ล็อกอิน เราก็ hash สิ่งที่เขาพิมพ์แล้วเทียบกับค่าที่เก็บไว้ ตัวเราเองก็ไม่มีทางรู้รหัสผ่านจริงของผู้ใช้
แนวคิดการโจมตี: rainbow table และ offline brute force
ถ้าฐานข้อมูลรั่ว ผู้โจมตีจะได้ค่า hash ไปทั้งชุด เขาจะไม่นั่งเดาทีละอัน แต่ใช้ rainbow table คือตารางที่คำนวณ hash ของรหัสผ่านยอดฮิตนับพันล้านไว้ล่วงหน้า แล้วเทียบหาค่าที่ตรงกัน ยิ่งถ้าเว็บใช้ hash ที่เร็วอย่าง MD5 หรือ SHA-1 การ์ดจอตัวเดียวเดาได้หลายพันล้านครั้งต่อวินาที รหัสผ่านง่าย ๆ จึงถูกถอดในไม่กี่วินาที นี่คือเหตุผลที่รหัสผ่านซ้ำกันหลายเว็บอันตรายมาก
🛡️ วิธีป้องกัน
- ใส่ salt — ค่าสุ่มเฉพาะของผู้ใช้แต่ละคน นำไปผสมก่อน hash ทำให้ rainbow table สำเร็จรูปใช้ไม่ได้ และผู้ใช้ที่รหัสผ่านเหมือนกันจะได้ hash ต่างกัน (ไลบรารีที่ดีสร้าง salt ให้อัตโนมัติและฝังไว้ในผลลัพธ์)
- ใช้ hash ที่ออกแบบมาให้ "ช้า" โดยตั้งใจ เช่น bcrypt, scrypt, argon2 ซึ่งปรับความหนักได้ (work factor) ทำให้การเดาทีละล้านครั้งกลายเป็นเรื่องแพงเกินคุ้ม อย่าใช้ MD5/SHA-1/SHA-256 เปล่า ๆ กับรหัสผ่าน
- เพิ่ม pepper (ค่าลับระดับระบบ เก็บแยกจากฐานข้อมูล) เป็นชั้นเสริม
- บังคับรหัสผ่านที่ยาวพอและเปิด MFA เพื่อลดผลกระทบแม้ hash หลุด
เทียบวิธีผิดกับวิธีถูกในการเก็บรหัสผ่าน (ฝั่งป้องกันของเว็บเราเอง)
# ผิด: เก็บดิบ หรือ hash เร็วไม่มี salt
db.save(user, password) # เก็บ plaintext อันตรายสุด
db.save(user, md5(password)) # ถอดด้วย rainbow table ได้ง่าย
# ถูก: ใช้ไลบรารีที่ตรวจสอบแล้ว มี salt + ช้าโดยตั้งใจ
import bcrypt
hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt())
db.save(user, hashed) # ตอนล็อกอินใช้ bcrypt.checkpw() เทียบ
🧪 ลองในแล็บของคุณ
ทำบนเครื่องหรือ VM ของคุณเองเท่านั้น
- ติดตั้ง Python แล้วลอง hash คำว่า
password123ด้วย SHA-256 หลาย ๆ รอบ สังเกตว่าได้ค่าเดิมทุกครั้ง แล้วลอง bcrypt สังเกตว่าได้ค่าต่างกันทุกครั้งเพราะ salt - เปิด CyberChef (เครื่องมือวิเคราะห์ในเบราว์เซอร์) ลองใส่ข้อความแล้วดู hash แบบต่าง ๆ เพื่อเห็นภาพ
- ลองโจทย์หมวด crypto บน picoCTF ซึ่งเป็นสนามฝึกถูกกฎหมายที่ออกแบบมาให้แก้โจทย์
TLS/HTTPS และใบรับรอง (Certificate)
ทุกครั้งที่คุณเห็นแม่กุญแจหน้าเว็บและ https:// นั่นคือ TLS กำลังทำงาน มันคือส่วนผสมของทุกอย่างในบทนี้: asymmetric เพื่อตกลงกุญแจ, symmetric เพื่อรับส่งข้อมูล, hashing เพื่อตรวจความถูกต้อง และ certificate เพื่อยืนยันว่าเรากำลังคุยกับเว็บตัวจริง
TLS handshake ทำงานอย่างไร (แบบย่อ)
- เบราว์เซอร์ทักไปที่เซิร์ฟเวอร์ เซิร์ฟเวอร์ส่ง certificate กลับมา ซึ่งข้างในมี public key และข้อมูลว่าเป็นของโดเมนอะไร
- เบราว์เซอร์ตรวจว่า certificate นี้ถูกเซ็นรับรองโดย CA (Certificate Authority) ที่เชื่อถือได้หรือไม่ และตรงกับโดเมนที่กำลังเข้าไหม (ระบบ chain of trust)
- ทั้งสองฝ่ายใช้ asymmetric เพื่อ "ตกลงกุญแจลับร่วม" อย่างปลอดภัย
- จากนั้นสลับไปใช้ symmetric ที่เร็วกว่าในการรับส่งข้อมูลจริงทั้งหมด
ผลลัพธ์คือคนที่ดักฟังกลางทางเห็นแต่ ciphertext อ่านอะไรไม่ได้ และมั่นใจได้ว่าเว็บไม่ได้ถูกสวมรอย
แนวคิดการโจมตี: MITM และ downgrade
ถ้าไม่มี TLS หรือ TLS ถูกทำให้อ่อนลง ผู้โจมตีที่อยู่ "ตรงกลาง" (Man-in-the-Middle) เช่นบน Wi-Fi สาธารณะที่เขาคุม อาจแอบอ่านหรือแก้ไขข้อมูลระหว่างทางได้ เทคนิคที่พบคือการบังคับให้เชื่อมต่อแบบ http ธรรมดา (downgrade) หรือยัด certificate ปลอมให้เหยื่อกด "ยอมรับความเสี่ยง" การเข้าใจกลไกนี้ทำให้เรารู้ว่าทำไม "อย่ากดข้ามคำเตือน certificate" จึงสำคัญมาก — เรื่องนี้เพื่อป้องกันตัวเราและผู้ใช้ของเรา ไม่ใช่เพื่อดักผู้อื่น
🛡️ วิธีป้องกัน
- เปิด HTTPS ทุกหน้า ของเว็บเรา และตั้ง HSTS เพื่อบังคับให้เบราว์เซอร์ใช้ https เสมอ กัน downgrade
- ใช้ TLS 1.2 เป็นอย่างต่ำ แนะนำ TLS 1.3 และปิดโปรโตคอลเก่า (SSL, TLS 1.0/1.1)
- ต่ออายุ certificate อัตโนมัติ (เช่นผ่าน Let's Encrypt) อย่าปล่อยให้หมดอายุ
- ในฐานะผู้ใช้: อย่ากรอกข้อมูลสำคัญบนหน้า http และอย่ากดข้ามคำเตือน certificate เด็ดขาด
🧪 ลองในแล็บของคุณ
สำรวจ certificate ของเว็บที่ คุณเป็นเจ้าของ หรือ localhost ของคุณเอง
- เปิดเว็บของคุณแล้วคลิกแม่กุญแจในเบราว์เซอร์ ดูว่าใครออก certificate หมดอายุเมื่อไร
- บนเครื่องคุณลองคำสั่งดูรายละเอียด certificate ของเซิร์ฟเวอร์ตัวเอง
อ่านข้อมูล certificate ของเซิร์ฟเวอร์ที่คุณดูแลเอง
openssl s_client -connect localhost:443 -servername localhost
# หรือดูไฟล์ cert ที่มีอยู่
openssl x509 -in mycert.pem -text -noout
ลายเซ็นดิจิทัลและความถูกต้องของข้อมูล
การเข้ารหัสดูแลเรื่อง "ความลับ" ส่วนลายเซ็นดิจิทัล (digital signature) ดูแลอีกสองเรื่องที่สำคัญไม่แพ้กัน คือ "ข้อมูลนี้ไม่ถูกแก้ไข" (integrity) และ "มาจากคนที่อ้างจริง" (authenticity) นี่คือสิ่งที่ทำให้เรามั่นใจได้ว่าไฟล์ที่ดาวน์โหลดมาไม่ได้ถูกฝังของแปลกปลอม
ลายเซ็นดิจิทัลทำงานกลับด้านกับการเข้ารหัส
ผู้ส่งใช้ private key ของตัวเองเซ็นข้อมูล (จริง ๆ คือเซ็นค่า hash ของข้อมูล) ใครก็ตามที่มี public key ของผู้ส่งจะตรวจสอบได้ว่าลายเซ็นนี้ถูกต้องและข้อมูลไม่ถูกแก้ไขแม้แต่บิตเดียว ถ้ามีคนแก้ไฟล์กลางทาง ค่า hash จะเปลี่ยน ลายเซ็นจะไม่ผ่านทันที นี่ให้ทั้ง integrity, authenticity และ non-repudiation (ผู้เซ็นปฏิเสธไม่ได้ว่าไม่ได้เซ็น)
แนวคิดการโจมตี: การปลอมแปลงและ supply chain
ถ้าซอฟต์แวร์หรืออัปเดตไม่มีการเซ็น ผู้โจมตีอาจสลับไฟล์จริงด้วยไฟล์ที่ฝังโค้ดอันตราย แล้วผู้ใช้ก็ติดตั้งไปโดยไม่รู้ตัว การโจมตีแบบนี้เรียกว่า supply chain attack ซึ่งเกิดขึ้นจริงกับหลายโปรเจกต์ใหญ่ การตรวจ checksum และลายเซ็นก่อนติดตั้งจึงเป็นนิสัยที่ควรมี
🛡️ วิธีป้องกัน
- ตรวจ checksum (เช่น SHA-256) ของไฟล์ที่ดาวน์โหลดกับค่าที่ผู้พัฒนาประกาศไว้ ก่อนติดตั้ง
- ตรวจ ลายเซ็น GPG/PGP ของแพ็กเกจหรือ release ถ้าผู้พัฒนามีให้
- สำหรับซอฟต์แวร์ที่เราปล่อยเอง ควรทำ code signing เพื่อให้ผู้ใช้ตรวจสอบต้นทางได้
- ดึง dependency จากแหล่งทางการและล็อกเวอร์ชัน (เช่น lockfile) เพื่อลดความเสี่ยงถูกสลับของ
ตรวจ checksum ของไฟล์ที่ดาวน์โหลดมาไว้ใช้เอง (ฝั่งป้องกัน)
# คำนวณ hash ของไฟล์แล้วเทียบด้วยตากับค่าที่เว็บทางการประกาศ
shasum -a 256 downloaded-file.iso
🧪 ลองในแล็บของคุณ
- ดาวน์โหลดไฟล์โอเพนซอร์สที่มีการประกาศ SHA-256 (เช่นอิมเมจ Linux) แล้วลองคำนวณ checksum เทียบดูว่าตรงไหม ลองแก้ไฟล์นิดเดียวแล้วคำนวณใหม่ สังเกตว่าค่าเปลี่ยนทันที
- สร้างกุญแจ GPG ของตัวเอง เซ็นไฟล์ข้อความสั้น ๆ แล้วให้เพื่อนที่ยินยอมลองตรวจด้วย public key ของคุณ
กฎเหล็ก: อย่าเขียน Crypto เอง และเส้นทางฝึกต่อ
ถึงตอนนี้คุณเข้าใจภาพรวมของการเข้ารหัสแล้ว แต่มีกฎข้อหนึ่งที่แม้แต่โปรแกรมเมอร์เก่ง ๆ ก็ต้องเคารพ นั่นคือ "อย่าประดิษฐ์ crypto ขึ้นใช้เอง"
ทำไมห้ามเขียน crypto เอง
อัลกอริทึมมาตรฐานอย่าง AES หรือ RSA ผ่านการตรวจสอบจากนักคณิตศาสตร์และนักวิจัยทั่วโลกมาหลายสิบปี ในขณะที่โค้ดที่เราเขียนเองมักมีจุดพลาดเล็ก ๆ ที่มองไม่เห็น เช่น การใช้ nonce/IV ซ้ำ, การสุ่มที่ไม่สุ่มจริง, หรือช่องโหว่ timing attack ที่ทำให้ความลับรั่วโดยที่ผังตรรกะดู "ถูกต้อง" ทุกอย่าง จุดพลาดพวกนี้ทำลายความปลอดภัยทั้งระบบได้ กฎคือ: ใช้ไลบรารีที่ผ่านการตรวจสอบและยังได้รับการดูแล อย่าเขียนเอง อย่าดัดแปลงแกนของมันเอง
libsodium
ไลบรารีที่ออกแบบให้ใช้ยากที่จะพลาด เหมาะกับงานทั่วไปหลายภาษา
cryptography (Python)
ครอบคลุมและมี "recipe" ระดับสูงอย่าง Fernet ให้ใช้ปลอดภัยตั้งแต่แกะกล่อง
Web Crypto API
มากับเบราว์เซอร์ ใช้ทำ crypto ฝั่ง client โดยไม่ต้องพึ่งโค้ดของเราเอง
🧪 ลองในแล็บของคุณ
- ลองโจทย์หมวด crypto บน CryptoHack และ picoCTF ซึ่งออกแบบมาให้เรียนรู้แบบแก้โจทย์ที่ถูกกฎหมาย ส่วนใหญ่คือการหาจุดที่ crypto ถูกใช้ผิดวิธี
- ฝึกใช้ CyberChef เพื่อถอด encoding พื้นฐาน (base64, hex, ROT13) ให้ชิน — จำไว้ว่า encoding ไม่ใช่ encryption มันไม่ได้ให้ความลับ
- ลองเขียนสคริปต์เล็ก ๆ ที่ ใช้ ไลบรารีมาตรฐานเข้ารหัสไฟล์ของคุณเอง แล้วถอดกลับ เพื่อฝึกใช้ของจริงให้ถูกวิธี
⚠️ จริยธรรมและกฎหมาย
ความรู้เรื่องการเข้ารหัสมีไว้เพื่อปกป้องข้อมูลของเราและผู้ใช้ของเรา ไม่ใช่เพื่อเข้าถึงข้อมูลของผู้อื่น การดักฟัง แก้ไข หรือถอดรหัสข้อมูลของคนอื่นโดยไม่ได้รับอนุญาต เข้าข่ายความผิดตาม พ.ร.บ.ว่าด้วยการกระทำความผิดเกี่ยวกับคอมพิวเตอร์ พ.ศ. 2560 เช่น การเข้าถึงระบบหรือข้อมูลของผู้อื่นโดยมิชอบ และการดักรับข้อมูลระหว่างส่ง ซึ่งมีทั้งโทษปรับและจำคุก ข้อความนี้เป็นความรู้ทั่วไป ไม่ใช่คำแนะนำทางกฎหมาย ให้ฝึกทุกอย่างเฉพาะบนระบบของคุณเอง แล็บ localhost หรือสนามฝึกที่อนุญาตเท่านั้น