Cyber 101 · บทที่ 6
เขียนโปรแกรมสาย Security ด้วย Python
ทำไมคนสายป้องกันต้องเขียนโค้ดเป็น
ในบทก่อน ๆ เราใช้เครื่องมือสำเร็จรูปอย่าง Burp หรือ nmap กับแล็บของเรา แต่พอถึงจุดหนึ่งคุณจะเจองานซ้ำ ๆ ที่มนุษย์ทำเองไม่ไหว เช่น อ่าน log เป็นหมื่นบรรทัด หรือตรวจว่ารหัสผ่านของทั้งองค์กรอ่อนแอตรงไหน งานแบบนี้แหละที่ภาษาโปรแกรมช่วยได้ และภาษาที่คนสาย security ทั่วโลกใช้มากที่สุดคือ Python เพราะอ่านง่าย มีไลบรารีพร้อม และเขียนสคริปต์สั้น ๆ ได้เร็ว บทนี้เราจะเขียนโค้ด "ฝั่งป้องกัน" เท่านั้น คือเครื่องมือที่เอาไว้ตรวจ ตั้งรับ และอุดช่องโหว่ของระบบที่เราดูแลเอง ไม่ใช่เครื่องมือเจาะระบบคนอื่น
Python พื้นฐานที่ใช้บ่อยในงาน security
คุณไม่ต้องเป็นโปรแกรมเมอร์เต็มตัวก็เริ่มได้ ขอแค่เข้าใจก้อนความรู้ไม่กี่อย่าง ได้แก่ ตัวแปรและสตริง (ข้อความ), ลูป for เพื่อทำซ้ำกับข้อมูลหลายบรรทัด, เงื่อนไข if, ฟังก์ชันเพื่อจัดโค้ดเป็นก้อน และการเรียกใช้ไลบรารีมาตรฐาน สามตัวที่จะเจอตลอดในบทนี้คือ hashlib (ทำแฮช), secrets (สุ่มค่าแบบปลอดภัย) และ hmac (เทียบค่าลับแบบปลอดภัย)
ตัวอย่างเล็ก ๆ ที่รวมพื้นฐานหลายอย่างไว้ด้วยกัน
import hashlib, secrets
# สุ่ม token สำหรับ session อย่างปลอดภัย (ใช้ secrets ไม่ใช่ random)
token = secrets.token_hex(16)
# ทำ SHA-256 เพื่อตรวจความถูกต้องของไฟล์ (integrity) ไม่ใช่เก็บรหัสผ่าน
digest = hashlib.sha256(b"hello lab").hexdigest()
print(token, digest)
จุดสำคัญตั้งแต่บรรทัดแรก: เวลาต้องสุ่มอะไรที่เกี่ยวกับความปลอดภัย เช่น token หรือรหัสผ่านชั่วคราว ให้ใช้โมดูล secrets เสมอ อย่าใช้ random ธรรมดา เพราะ random คาดเดาได้และไม่ได้ออกแบบมาเพื่องานลับ
🧪 ลองในแล็บของคุณ
ติดตั้ง Python ในเครื่องหรือใน VM แล็บของคุณ เปิดไฟล์ใหม่ชื่อ lab.py แล้วพิมพ์โค้ดด้านบนลงไป รันด้วย python3 lab.py สังเกตว่าทุกครั้งที่รัน ค่า token จะเปลี่ยน แต่ค่า digest ของข้อความเดิมจะเท่ากันเสมอ นี่คือธรรมชาติของแฮชที่เราจะคุยกันต่อในหัวข้อถัดไป ทำทั้งหมดนี้บนเครื่องของคุณเองเท่านั้น
Hashing กับ Encryption ต่างกันตรงไหน
คนมักสับสนสองคำนี้ แต่มันคนละงานกันคนละทิศทาง เข้าใจผิดเมื่อไรระบบพังทันที เช่น เอา encryption ไปเก็บรหัสผ่าน หรือคิดว่า hashing คือการเข้ารหัส สรุปสั้น ๆ คือ hashing เป็นทางเดียว (แปลงกลับไม่ได้) ส่วน encryption เป็นสองทาง (มีกุญแจไว้ถอดกลับ)
เทียบให้เห็นภาพ
| หัวข้อ | Hashing | Encryption |
|---|---|---|
| ทิศทาง | ทางเดียว แปลงกลับไม่ได้ | สองทาง ถอดกลับได้ด้วยกุญแจ |
| ใช้ทำอะไร | เก็บรหัสผ่าน, ตรวจ integrity ของไฟล์ | ปกป้องข้อมูลระหว่างส่งหรือตอนเก็บ ที่ต้องอ่านกลับ |
| มีกุญแจไหม | ไม่มี (แต่ควรมี salt) | มี ต้องเก็บกุญแจให้ปลอดภัย |
| ตัวอย่าง | bcrypt, argon2, SHA-256 | AES, RSA, TLS ตอนเปิดเว็บ https |
กฎง่าย ๆ ที่จำไว้: รหัสผ่านต้อง hash ไม่ใช่ encrypt เพราะเราไม่จำเป็นต้องรู้รหัสผ่านจริงของผู้ใช้เลย เราแค่ต้องตรวจว่า "ตรงกันไหม" ก็พอ ส่วนข้อมูลที่ต้องอ่านกลับ เช่น เลขบัตรที่ต้องเอามาใช้ต่อ ถึงจะใช้ encryption
⚠️ จริยธรรมและกฎหมาย
เรื่องการเข้ารหัสข้อมูลส่วนบุคคลไม่ใช่แค่เทคนิค แต่เกี่ยวกับกฎหมายด้วย พ.ร.บ.คุ้มครองข้อมูลส่วนบุคคล (PDPA) กำหนดให้ผู้เก็บข้อมูลต้องมีมาตรการรักษาความมั่นคงปลอดภัยที่เหมาะสม การเก็บรหัสผ่านหรือข้อมูลอ่อนไหวแบบ plaintext จึงไม่ใช่แค่เขียนโค้ดไม่ดี แต่อาจเป็นการละเลยหน้าที่ตามกฎหมาย ข้อความนี้เป็นความรู้ทั่วไป ไม่ใช่คำแนะนำทางกฎหมาย
เครื่องมือฝั่งป้องกัน 1: ตรวจรหัสผ่านและแฮชให้ถูกวิธี
งานแรกที่คนสายป้องกันเขียนบ่อยที่สุดคือเรื่องรหัสผ่าน ทั้งการช่วยผู้ใช้ตั้งรหัสให้แข็งแรง และการเก็บรหัสให้ปลอดภัยฝั่งเซิร์ฟเวอร์ เราจะทำสองเครื่องมือเล็ก ๆ นี้ในแล็บของเราเอง
เครื่องมือที่ 1: ตัวตรวจความแข็งแรงของรหัสผ่าน
สคริปต์นี้รับรหัสผ่านเข้ามา แล้วบอกว่ามันอ่อนตรงไหน เพื่อช่วยผู้ใช้ของเราตั้งรหัสให้ดีขึ้น มันเป็นเครื่องมือป้องกัน ไม่ใช่เครื่องเดารหัสของคนอื่น
ตรวจเกณฑ์เบื้องต้นของรหัสผ่านหนึ่งตัว
import re
def check_strength(pw):
problems = []
if len(pw) < 12:
problems.append("สั้นเกินไป ควรยาวอย่างน้อย 12 ตัว")
if not re.search(r"[A-Z]", pw):
problems.append("ไม่มีตัวพิมพ์ใหญ่")
if not re.search(r"[0-9]", pw):
problems.append("ไม่มีตัวเลข")
return problems # ลิสต์ว่าง = ผ่านเกณฑ์เบื้องต้น
ความยาวสำคัญกว่าความซับซ้อน รหัสยาว ๆ ที่จำได้อย่างวลีหลายคำต่อกัน มักแข็งแรงกว่ารหัสสั้นที่มีอักขระพิเศษเยอะ ในงานจริงเรายังควรเทียบกับรายการรหัสที่รั่วบ่อย (เช่นชุด rockyou ที่ใช้ศึกษากันทั่วไป) เพื่อกันไม่ให้ผู้ใช้ตั้งรหัสที่โลกรู้กันหมดแล้ว
เครื่องมือที่ 2: แฮชรหัสผ่านด้วย bcrypt
ตอนผู้ใช้สมัคร เราจะไม่เก็บรหัสจริง แต่เก็บค่าที่แฮชแล้ว ตอนล็อกอินก็เอารหัสที่พิมพ์มาแฮชแล้วเทียบ วิธีที่ถูกต้องคือใช้อัลกอริทึมที่ออกแบบมาเพื่อรหัสผ่านโดยเฉพาะ เช่น bcrypt หรือ argon2 ซึ่งใส่ salt ให้อัตโนมัติและ "ช้าโดยตั้งใจ"
แบบผิด อย่าทำ: เก็บรหัสตรง ๆ หรือแฮชเร็วเปล่า ๆ
# plaintext = อันตรายสุด
db_save(username, password)
# แฮชเร็วไม่มี salt ก็ยังเสี่ยงต่อ rainbow table
bad = hashlib.md5(password.encode()).hexdigest()
แบบถูก: bcrypt มี salt ในตัวและตั้งใจให้แฮชช้า
import bcrypt
# ตอนสมัคร
hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt())
db_save(username, hashed)
# ตอนล็อกอิน เทียบแบบปลอดภัย
ok = bcrypt.checkpw(attempt.encode(), hashed)
🛡️ วิธีป้องกัน
สรุปการเก็บรหัสผ่านให้ปลอดภัย: (1) อย่าเก็บ plaintext เด็ดขาด (2) ใช้ bcrypt หรือ argon2 ไม่ใช่ MD5/SHA-1 เปล่า ๆ เพราะแฮชที่เร็วเกินไปเปิดช่องให้เดาทีละล้านค่าต่อวินาที (3) salt ที่มากับ bcrypt ทำให้ตารางแฮชสำเร็จรูป (rainbow table) ใช้ไม่ได้ เพราะรหัสเดียวกันของคนละคนจะได้ค่าแฮชต่างกัน (4) ใช้ bcrypt.checkpw ในการเทียบ อย่าเขียนเทียบสตริงเอง เพราะการเทียบทีละตัวอักษรอาจรั่วข้อมูลผ่านเวลาที่ใช้ (timing attack) ถ้าต้องเทียบค่าลับอื่น ๆ ให้ใช้ hmac.compare_digest ซึ่งใช้เวลาเท่ากันเสมอ
🧪 ลองในแล็บของคุณ
ติดตั้งไลบรารีด้วย pip install bcrypt ในแล็บ แล้วเขียนสคริปต์เล็ก ๆ ที่รับรหัสผ่านทดสอบ (ตั้งขึ้นเองล้วน ๆ ไม่ใช่รหัสจริงของใคร) ผ่าน check_strength ก่อน ถ้าผ่านเกณฑ์ค่อยส่งเข้า bcrypt.hashpw แล้วลองพิมพ์รหัสผิดกับรหัสถูกเข้า checkpw เพื่อดูว่ามันตอบ True/False ถูกต้องไหม เก็บทุกอย่างไว้ในเครื่องคุณ อย่าเอารหัสจริงของบัญชีที่ใช้งานอยู่มาทดสอบ
เครื่องมือฝั่งป้องกัน 2: อ่าน log และทำ honeypot ง่าย ๆ
ผู้บุกรุกมักทิ้งร่องรอยไว้ใน log เสมอ เช่น พยายามล็อกอินผิดซ้ำ ๆ จาก IP เดิม การเขียนสคริปต์อ่าน log อัตโนมัติจึงเป็นทักษะหัวใจของสายป้องกัน (blue team) เพราะช่วยให้เราเห็นสัญญาณโจมตีก่อนที่มันจะสำเร็จ
เข้าใจการโจมตีเพื่ออ่าน log ให้ออก: brute force
brute force คือการลองรหัสผ่านทีละค่าเรื่อย ๆ จนเจอ หรือลองบัญชีเดิมด้วยรหัสยอดฮิตหลายตัว (credential stuffing) ในแง่ผู้ป้องกัน สิ่งที่เราสนใจไม่ใช่วิธีทำ แต่คือ "หน้าตาของมันใน log" ซึ่งมักเป็นการล็อกอินล้มเหลวจำนวนมากในเวลาสั้น ๆ จาก IP เดียว
นับจำนวนการล็อกอินล้มเหลวแยกตาม IP จากไฟล์ log ของเครื่องเราเอง
from collections import Counter
fails = Counter()
for line in open("auth.log"):
if "Failed password" in line:
ip = line.split("from ")[1].split()[0]
fails[ip] += 1
# IP ที่พลาดบ่อยผิดปกติ = ควรสงสัยไว้ก่อน
for ip, n in fails.most_common(5):
print(ip, n)
สคริปต์นี้เปลี่ยน log ดิบเป็นสรุปที่คนอ่านได้ใน 5 บรรทัด ในงานจริงเราจะต่อยอดให้มันส่งแจ้งเตือนอัตโนมัติเมื่อ IP ไหนพลาดเกินเกณฑ์
🛡️ วิธีป้องกัน brute force
นอกจากตรวจ log แล้ว ระบบควรตั้งรับหลายชั้น: (1) จำกัดจำนวนครั้งที่ล็อกอินได้ต่อช่วงเวลา (rate limiting) (2) ล็อกบัญชีชั่วคราวหรือเพิ่มดีเลย์หลังพลาดหลายครั้ง (3) เปิด multi-factor authentication เพื่อให้แค่รหัสผ่านไม่พอที่จะเข้าได้ (4) ใช้ CAPTCHA เมื่อสงสัย (5) นำ IP ที่ยิงถี่ผิดปกติเข้าลิสต์เฝ้าระวังหรือบล็อก การผสมหลายชั้นแบบนี้คือแนวคิด defense in depth ที่เราจะเจออีกในบทถัดไป
honeypot คืออะไร
honeypot คือ "กับดักน้ำผึ้ง" เป็นบริการหลอกที่เราตั้งขึ้นในเครือข่ายของเราเอง เพื่อล่อให้ผู้บุกรุกเข้ามายุ่ง ของจริงไม่มีใครควรมาต่อ ดังนั้นถ้ามีการเชื่อมต่อเข้ามาที่ honeypot เมื่อไร แปลว่ามีบางอย่างผิดปกติแน่ ๆ มันเป็นเครื่องมือ "ตรวจจับ" ล้วน ๆ ไม่ได้ไปโจมตีใคร
honeypot อย่างง่ายที่แค่จดว่าใครมาต่อ ผูกกับ localhost ในแล็บเท่านั้น
import socket, datetime
s = socket.socket()
s.bind(("127.0.0.1", 2222)) # localhost ในแล็บเท่านั้น
s.listen()
while True:
conn, addr = s.accept()
print(datetime.datetime.now(), "มีการเชื่อมต่อจาก", addr)
conn.close()
🛡️ วิธีป้องกัน: เอา honeypot และ log มาใช้จริง
honeypot มีค่าก็ต่อเมื่อมีคนดูผลของมัน ดังนั้นควรต่อ honeypot หรือ log monitor เข้ากับระบบแจ้งเตือน เช่น เขียนไฟล์บันทึกแยก แล้วให้สคริปต์อีกตัวคอยอ่านและส่งข้อความหาเราเมื่อเจอเหตุการณ์ นอกจากนี้ควรเก็บ log ไว้ในที่ที่ผู้บุกรุกแก้ไม่ได้ (เช่น ส่งออกไปเซิร์ฟเวอร์ log กลาง) เพราะสิ่งแรก ๆ ที่ผู้บุกรุกทำคือลบร่องรอยของตัวเองใน log
⚠️ จริยธรรมและกฎหมาย
honeypot และ log monitor ต้องรันในเครือข่ายหรือเครื่องที่เราเป็นเจ้าของหรือได้รับมอบหมายให้ดูแลเท่านั้น การเอาไปวางดักในระบบคนอื่นถือว่าเข้าถึงระบบโดยไม่ได้รับอนุญาต และ log มักมีข้อมูลส่วนบุคคล เช่น IP หรือชื่อผู้ใช้ จึงอยู่ภายใต้ PDPA ต้องเก็บเท่าที่จำเป็นและมีมาตรการปกป้อง อีกเรื่องที่ต้องระวังคือ honeypot ต้องไม่กลายเป็นฐานให้คนอื่นใช้โจมตีต่อ (อย่าเปิดช่องให้มันถูกยึดไปใช้งาน) ทั้งหมดนี้เป็นความรู้ทั่วไป ไม่ใช่คำแนะนำทางกฎหมาย
กติกาของคนเขียนเครื่องมือ และสรุปบท
โค้ดชุดเดียวกันอาจเป็นได้ทั้งเครื่องมือป้องกันและอาวุธ ขึ้นกับว่าเราชี้มันไปที่ระบบของใคร เส้นแบ่งนี้ชัดและสำคัญมาก บทนี้เราจึงเขียนแต่เครื่องมือฝั่งตรวจและตั้งรับกับระบบของเราเองล้วน ๆ
เข็มทิศเวลาเขียนเครื่องมือ security
ถามตัวเองสามข้อก่อนรันสคริปต์ทุกครั้ง: หนึ่ง เป้าหมายเป็นของเราหรือแล็บของเราไหม สอง จุดประสงค์คือการตรวจ ป้องกัน หรือทำความเข้าใจ ไม่ใช่ทำลายหรือขโมยใช่ไหม สาม ถ้าเจ้าของระบบเห็นสิ่งที่เราทำ เขาจะโอเคไหม ถ้าตอบไม่ผ่านข้อใดข้อหนึ่ง ให้หยุด เครื่องมือที่จุดประสงค์หลักคือเจาะระบบคนอื่นที่ไม่ยินยอม เราไม่เขียนและไม่ใช้
ข่าวดีคือทักษะเขียนโค้ดฝั่งป้องกันในบทนี้ ทั้งการแฮชที่ถูกต้อง การตรวจรหัสผ่าน และการอ่าน log ล้วนเป็นทักษะที่ตลาดงานต้องการจริง และฝึกได้เต็มที่บนแพลตฟอร์มถูกกฎหมายอย่าง picoCTF หรือกับเว็บฝึกที่จงใจมีช่องโหว่อย่าง OWASP Juice Shop ที่คุณติดตั้งเองในแล็บ
🧪 ลองในแล็บของคุณ
รวมสามเครื่องมือของบทนี้เป็นชุดเดียว: เขียนสคริปต์ที่ (1) ตรวจความแข็งแรงของรหัสทดสอบ (2) แฮชด้วย bcrypt แล้วเทียบกลับ (3) อ่านไฟล์ log ตัวอย่างที่คุณสร้างเองแล้วนับ IP ที่ล็อกอินพลาดบ่อย ลองสร้างไฟล์ auth.log ปลอมด้วยมือเพื่อทดสอบ logic ของตัวนับ ทั้งหมดรันในเครื่องคุณ ไม่ต่อกับระบบภายนอกใด ๆ