รูปแบบความเสี่ยง: OWASP Top 10 A07:2025 Authentication Failures
คำอธิบาย: ระบบยืนยันตัวความน่าเชื่อถือต่ำ ไม่ได้สัดส่วนกับความสำคัญของระบบ
วันที่รายงาน: 20 มีนาคม 2569
วันที่หน่วยงานรับทราบ: 20 มีนาคม 2569
วันที่หน่วยงานแก้ไข: 21 มีนาคม 2569
สถานะ: ✅ แก้ไขแล้ว
พบช่องโหว่ในระบบภายในของหน่วยงานรัฐแห่งหนึ่ง (ต่อไปนี้จะเรียกว่า "หน่วยงานรัฐ") จำนวน 2 ระบบ ซึ่งทำให้ผู้ไม่หวังดีสามารถ สืบค้นข้อมูลส่วนบุคคลของประชาชนไทยได้แบบไม่จำกัดจำนวน เพียงแค่ทราบชื่อ-นามสกุล โดยข้อมูลที่ได้ครอบคลุมตั้งแต่เลขบัตรประชาชน ที่อยู่ วันเกิด สิทธิรักษาพยาบาล ไปจนถึง เลขบัตรประชาชนของบิดา-มารดา
ผู้เขียนได้แจ้งเตือนหน่วยงานที่เกี่ยวข้องตามหลัก Responsible Disclosure แล้ว และเผยแพร่รายงานนี้หลังจากหน่วยงานดำเนินการแก้ไขเรียบร้อยแล้ว
ผู้เขียนได้รับ source code ของเครื่องมือหนึ่งจากตลาดมืด ซึ่งถูกออกแบบมาเป็น Discord Bot สำหรับให้บริการสืบค้นข้อมูลส่วนบุคคลของประชาชนไทย โดยเครื่องมือนี้ทำงานโดยเชื่อมต่อกับระบบภายในของ "หน่วยงานรัฐ" แบบอัตโนมัติ
จากการวิเคราะห์ source code พบว่าเครื่องมือดังกล่าว:
- ใช้ credentials (username/password) จริง ที่ฝังอยู่ใน source code เพื่อ login เข้าระบบ "หน่วยงานรัฐ"
- credentials เหล่านี้ ใช้งานได้จริง ณ วันที่จัดทำรายงาน
- สามารถสืบค้นข้อมูลได้เพียงแค่ พิมพ์ชื่อ-นามสกุล ของเป้าหมาย
- ถูกห่อหุ้มเป็น Discord Bot เพื่อ ขายบริการในวงจำกัด โดยจำกัดการเข้าถึงด้วย Discord Role (เฉพาะผู้จ่ายเงิน)
ผู้เขียน ไม่ได้ ใช้เครื่องมือนี้สืบค้นข้อมูลของบุคคลใด แต่ได้วิเคราะห์ source code อย่างละเอียดและจัดทำรายงานแจ้งหน่วยงานที่เกี่ยวข้อง
เครื่องมือเข้าถึงระบบ "หน่วยงานรัฐ" 2 ระบบ แบบ chain attack:
flowchart LR
INPUT["👤 ชื่อ-นามสกุล"] --> SYS1["🔓 ระบบที่ 1\nแปลงชื่อ → เลขบัตร ปชช."]
SYS1 --> SYS2["🔓 ระบบที่ 2\nแปลงเลขบัตร → ข้อมูลส่วนบุคคล"]
SYS2 --> OUTPUT["📦 ข้อมูล 30+ fields\nเลขบัตร, ที่อยู่, ครอบครัว,\nสิทธิรักษาพยาบาล ฯลฯ"]
style SYS1 fill:#d0021b,color:#fff
style SYS2 fill:#d0021b,color:#fff
style OUTPUT fill:#8b0000,color:#fff
| ระบบ | หน้าที่ | ช่องทางเข้าถึง |
|---|---|---|
| ระบบที่ 1 System A | แปลง ชื่อ-นามสกุล → เลขบัตรประชาชน | Web Application (ใช้ browser automation) |
| ระบบที่ 2 System B | แปลง เลขบัตรประชาชน → ข้อมูลส่วนบุคคลทั้งหมด | REST API โดยตรง ไม่ต้องเปิด browser |
| # | ช่องโหว่ | รายละเอียด |
|---|---|---|
| 1 | Credentials รั่วไหล | พบ username/password ฝังใน source code 2 ชุด สำหรับเข้าระบบ 2 ระบบ ยืนยันว่าใช้งานได้จริง |
| 2 | รหัสผ่านอ่อนแอ | ระบบที่ 2 ใช้รหัสผ่านเพียง 4 หลัก ไม่มีนโยบายความเข้มงวด |
| 3 | Over-privileged API | API ของระบบที่ 2 ส่งคืนข้อมูล 50+ fields ทั้งที่ function ที่เรียกใช้ต้องการข้อมูลเพียงบางส่วน |
| 4 | ไม่มี Rate Limiting | API เรียกได้ไม่จำกัดจำนวนครั้ง ทำให้สามารถดึงข้อมูลจำนวนมาก (bulk extraction) |
| 5 | Token ไม่หมดอายุ | เมื่อ login ได้ token มาแล้ว ใช้ได้ต่อเนื่องโดยไม่ต้อง login ใหม่ |
| 6 | ไม่มี MFA | ไม่มีการยืนยันตัวตนแบบสองชั้น |
| 7 | บัญชีต้องสงสัย | ชื่อผู้ใช้ไม่ตรงกับรูปแบบขององค์กร สงสัยว่าผู้โจมตีอาจสร้างบัญชีเองได้ |
เพื่อยืนยันว่าช่องโหว่ยังสามารถใช้งานได้จริง ณ วันที่จัดทำรายงาน ผู้รายงานได้ทดสอบเรียก API GetPersonData ด้วย credentials ที่พบใน source code โดย เลือกทดสอบกับบุคคลสาธารณะ (นักการเมือง) เพื่อลดผลกระทบต่อบุคคลทั่วไป
{
"address": "●●/●",
"birthdate": "2509●●●●",
"catm": "●●●●●●●●",
"count_select": "0",
"ffname": "●●●●●●●",
"flname": "ชาญวีรกูล",
"fname": "อนุทิน",
"fpid": "3●●●●●●●●●●41",
"lname": "ชาญวีรกูล",
"maininscl": "OFC",
"maininscl_main": "C",
"maininscl_name": "สิทธิข้าราชการ/สิทธิหน่วยงานรัฐ",
"mfname": "●●●●●●●",
"mlname": "ชาญวีรกูล",
"mpid": "3●●●●●●●●●●59",
"nation": "099",
"nation_name_en": "THAILAND",
"nation_name_shorten": "THA",
"nation_name_th": "ไทย",
"person_id": "3●●●●●●●●●●67",
"primary_aumphur_name": "●●●●●●●●●●●●●●",
"primary_moo_name": "04",
"primary_mooban_name": "●●●●●●●●",
"primary_tumbon_name": "●●●●●",
"primaryprovince": "●●●●",
"primaryprovince_name": "บุรีรัมย์",
"sex": "1",
"startdate": "2569●●●●",
"startdatetime": "2026-●●-●●T00:00:00+07:00",
"status_dola": "0",
"status_dola_desc": "บุคคลนี้มีภูมิลำเนาอยู่ในบ้านนี้",
"status_dola_shortdesc": "มีภูมิลำเนาอยู่ในบ้านนี้",
"subinscl": "O1",
"subinscl_name": "สิทธิเบิกกรมบัญชีกลาง (ข้าราชการ)",
"tile_name": "นาย",
"title": "003",
"ws_data_source": "NHSO",
"ws_date_request": "2026-●●-●●T00:00:00+07:00",
"ws_status": "WS001",
"wsid": "WS000000●●●●●●●70",
"wsid_batch": "WSB00000●●●●●●●45",
"full_address": "●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●● บุรีรัมย์",
"full_name": "อนุทิน ชาญวีรกูล",
"result": "ok"
}ข้อมูลที่ API ส่งกลับมาครอบคลุม 50 fields:
| หมวด | ตัวอย่างข้อมูล | ระดับความอ่อนไหว |
|---|---|---|
| ข้อมูลพื้นฐาน | เลขบัตรประชาชน 13 หลัก, ชื่อ-นามสกุล, เพศ, วันเกิด, สัญชาติ, สถานะทะเบียนบ้าน | 🔴 สูงมาก |
| ที่อยู่ | บ้านเลขที่, หมู่, ตำบล, อำเภอ, จังหวัด (ที่อยู่เต็ม) | 🔴 สูงมาก |
| สิทธิรักษาพยาบาล | ชื่อสิทธิ (บัตรทอง/ประกันสังคม/ข้าราชการ), สถานพยาบาลหลัก/รอง, จังหวัดลงทะเบียน | 🟠 สูง |
| ข้อมูลครอบครัว | ชื่อ-นามสกุลบิดา-มารดา, เลขบัตรประชาชนบิดา-มารดา | 🔴 สูงมาก |
| ข้อมูลอ้างอิง | Card ID, Mastercup ID | 🟠 สูง |
สิ่งที่น่าตกใจที่สุด คือ API ส่งคืน เลขบัตรประชาชนของบิดาและมารดา มาด้วย ทั้งที่ function ที่เรียกใช้ไม่น่าจะต้องการข้อมูลเหล่านี้ นี่คือ Over-privileged API Response ที่เป็นต้นเหตุหลักของการรั่วไหลในวงกว้าง
จากการวิเคราะห์ source code ร่วมกับผลทดสอบจริง พบว่า API ของระบบ System B ตอบกลับข้อมูลจำนวน 50+ fields ต่อ 1 request ซึ่งประกอบด้วยข้อมูลส่วนบุคคลและ metadata ของระบบ แบ่งเป็น 6 หมวดดังนี้:
| # | Field Name | คำอธิบาย | ระดับความอ่อนไหว |
|---|---|---|---|
| 1 | person_id |
เลขบัตรประชาชน 13 หลัก | 🔴 สูงมาก |
| 2 | tile_name |
คำนำหน้าชื่อ | 🟡 ปานกลาง |
| 3 | full_name |
ชื่อ-นามสกุลเต็ม | 🟠 สูง |
| 4 | fname |
ชื่อจริง | 🟠 สูง |
| 5 | lname |
นามสกุล | 🟠 สูง |
| 6 | sex |
เพศ (1=ชาย, 2=หญิง) | 🟡 ปานกลาง |
| 7 | birthdate |
วันเกิด (รูปแบบ YYYYMMDD พุทธศักราช) | 🟠 สูง |
| 8 | nation_name_th |
สัญชาติ | 🟡 ปานกลาง |
| 9 | status_dola_desc |
สถานะทะเบียนบ้าน | 🟠 สูง |
| # | Field Name | คำอธิบาย | ระดับความอ่อนไหว |
|---|---|---|---|
| 10 | full_address |
ที่อยู่เต็ม | 🔴 สูงมาก |
| 11 | address |
บ้านเลขที่ | 🟠 สูง |
| 12 | primary_moo_name |
หมู่ | 🟡 ปานกลาง |
| 13 | primary_mooban_name |
หมู่บ้าน | 🟡 ปานกลาง |
| 14 | primary_tumbon_name |
ตำบล/แขวง | 🟡 ปานกลาง |
| 15 | primary_aumphur_name |
อำเภอ/เขต | 🟡 ปานกลาง |
| 16 | primaryprovince_name |
จังหวัด | 🟡 ปานกลาง |
| # | Field Name | คำอธิบาย | ระดับความอ่อนไหว |
|---|---|---|---|
| 17 | maininscl_name |
ชื่อสิทธิหลัก | 🟠 สูง |
| 18 | maininscl |
รหัสสิทธิหลัก | 🟡 ปานกลาง |
| 19 | subinscl_name |
ชื่อสิทธิย่อย | 🟡 ปานกลาง |
| 20 | hmain_name |
สถานพยาบาลหลัก | 🟠 สูง |
| 21 | hsub_name |
สถานพยาบาลรอง | 🟡 ปานกลาง |
| 22 | hmain_op_name |
สถานพยาบาล OP | 🟡 ปานกลาง |
| 23 | purchaseprovince_name |
จังหวัดลงทะเบียน | 🟡 ปานกลาง |
| 24 | startdate |
วันที่เริ่มสิทธิ | 🟡 ปานกลาง |
| 25 | expdate |
วันหมดอายุบัตร | 🟡 ปานกลาง |
⚠️ หมวดนี้มีความอ่อนไหวสูงที่สุด จากการทดสอบพบว่า ข้อมูลบิดา-มารดาไม่ได้มีครบทุกรายการ บางบุคคลจะไม่มี field เหล่านี้ อย่างไรก็ตาม สำหรับรายการที่มีข้อมูลครบ การรั่วไหลของเลขบัตรประชาชนบิดา-มารดา หมายความว่าการค้นหาบุคคล 1 คน จะทำให้ได้ข้อมูลระบุตัวตนของอีก 2 คนโดยอัตโนมัติ ซึ่งสามารถ นำเลขบัตรที่ได้ (fpid/mpid) ไปค้นต่อในขั้นตอนที่ 2 โดยตรง (ข้าม step 1) เพื่อดึงข้อมูลเป็นลูกโซ่ได้
| # | Field Name | คำอธิบาย | ระดับความอ่อนไหว |
|---|---|---|---|
| 26 | ffname |
ชื่อบิดา | 🟠 สูง |
| 27 | flname |
นามสกุลบิดา | 🟠 สูง |
| 28 | fpid |
เลขบัตรประชาชนบิดา | 🔴 สูงมาก |
| 29 | mfname |
ชื่อมารดา | 🟠 สูง |
| 30 | mlname |
นามสกุลมารดา | 🟠 สูง |
| 31 | mpid |
เลขบัตรประชาชนมารดา | 🔴 สูงมาก |
flowchart LR
A["🔍 ค้นหาบุคคล A"] --> B["📋 ได้ข้อมูลบุคคล A\n+ เลขบัตรบิดา (fpid)\n+ เลขบัตรมารดา (mpid)"]
B --> C["🔍 ค้นหาบิดา\nด้วย fpid"]
B --> D["🔍 ค้นหามารดา\nด้วย mpid"]
C --> E["📋 ได้ข้อมูลบิดา\n+ เลขบัตรปู่/ย่า"]
D --> F["📋 ได้ข้อมูลมารดา\n+ เลขบัตรตา/ยาย"]
E --> G["🔄 ค้นต่อเป็นลูกโซ่\nได้ข้อมูลทั้งตระกูล"]
F --> G
style A fill:#4a90d9,color:#fff
style B fill:#d0021b,color:#fff
style E fill:#d0021b,color:#fff
style F fill:#d0021b,color:#fff
style G fill:#8b0000,color:#fff
| # | Field Name | คำอธิบาย | ระดับความอ่อนไหว |
|---|---|---|---|
| 32 | cardid |
Card ID | ⚪ ต่ำ |
| 33 | mastercup_id |
Mastercup ID | ⚪ ต่ำ |
Fields เพิ่มเติมที่พบจากผลการทดสอบจริง ซึ่ง source code ไม่ได้ดึงมาแสดงผล แต่ API ส่งคืนมาด้วย
| # | Field Name | คำอธิบาย | ระดับความอ่อนไหว |
|---|---|---|---|
| 34 | catm |
รหัส CATM (รหัสที่ตั้งทะเบียนบ้าน) | 🟡 ปานกลาง |
| 35 | count_select |
จำนวนครั้งที่ถูกค้นหา | 🟡 ปานกลาง |
| 36 | maininscl_main |
รหัสกลุ่มสิทธิหลัก (ตัวย่อ) | 🟡 ปานกลาง |
| 37 | nation |
รหัสสัญชาติ (ตัวเลข) | 🟡 ปานกลาง |
| 38 | nation_name_en |
สัญชาติ (ภาษาอังกฤษ) | 🟡 ปานกลาง |
| 39 | nation_name_shorten |
สัญชาติ (ตัวย่อ) | 🟡 ปานกลาง |
| 40 | primaryprovince |
รหัสจังหวัด | 🟡 ปานกลาง |
| 41 | startdatetime |
วันเริ่มสิทธิ (ISO 8601) | 🟡 ปานกลาง |
| 42 | status_dola |
รหัสสถานะทะเบียนบ้าน (ตัวเลข) | 🟡 ปานกลาง |
| 43 | status_dola_shortdesc |
คำอธิบายสถานะทะเบียนบ้าน (สั้น) | 🟡 ปานกลาง |
| 44 | title |
รหัสคำนำหน้าชื่อ (ตัวเลข) | 🟡 ปานกลาง |
| 45 | ws_data_source |
แหล่งข้อมูล | ⚪ ต่ำ |
| 46 | ws_date_request |
วันที่-เวลา request (ISO 8601) | ⚪ ต่ำ |
| 47 | ws_status |
สถานะ (เช่น "WS001") | ⚪ ต่ำ |
| 48 | wsid |
Tracking ID ของ request | ⚪ ต่ำ |
| 49 | wsid_batch |
Batch tracking ID | ⚪ ต่ำ |
| 50 | result |
สถานะผลลัพธ์ (เช่น "ok") | ⚪ ต่ำ |
| ระดับ | จำนวน Fields | ตัวอย่าง |
|---|---|---|
| 🔴 สูงมาก | 4 | เลขบัตรประชาชน (ตนเอง, บิดา, มารดา), ที่อยู่เต็ม |
| 🟠 สูง | 12 | ชื่อ-นามสกุล, วันเกิด, สถานพยาบาล, ข้อมูลครอบครัว, สถานะทะเบียนบ้าน |
| 🟡 ปานกลาง | 26 | เพศ, สัญชาติ, รหัสสิทธิ, ตำบล/อำเภอ/จังหวัด, CATM, รหัสต่างๆ |
| ⚪ ต่ำ (metadata) | 8 | แหล่งข้อมูล, tracking ID, สถานะ request |
หมายเหตุ: ข้อมูลทั้งหมดนี้ถูกส่งกลับมาใน single API response ต่อ 1 เลขบัตรประชาชน นี่คือ Over-privileged API Response ที่เป็นสาเหตุหลักของการรั่วไหลในปริมาณมาก
API สามารถเรียกใช้ได้โดยตรงผ่าน HTTP POST โดยไม่ต้องเปิด browser ทำให้ผู้โจมตีสามารถเขียน script ดึงข้อมูลได้อย่างรวดเร็ว
ข้อมูลผู้มีสิทธิบัตรทอง ประมาณ 46-47 ล้านคน ข้อมูลผู้มีสิทธิประกันสังคม ประมาณ 12-13 ล้านคน ข้อมูลผู้มีสิทธิข้าราชการ/รัฐวิสาหกิจ ประมาณ 5-5.6 ล้านคน
flowchart TD
A["ประชาชนทุกคนในฐานข้อมูล หน่วยงานรัฐ"]
A --> B["+ ครอบครัว (บิดา-มารดา)\nผ่านข้อมูลเลขบัตร ที่ API ส่งคืนมา"]
style A fill:#d0021b,color:#fff
style B fill:#8b0000,color:#fff
| ภัยคุกคาม | อธิบาย |
|---|---|
| 🔴 Identity Theft | ใช้เลขบัตร + ชื่อ + วันเกิด + ที่อยู่ ไปเปิดบัญชี สมัครสินเชื่อ หรือทำ KYC ปลอม |
| 🔴 Social Engineering | ใช้ข้อมูลครอบครัว (ชื่อพ่อ-แม่) เป็น "คำถามยืนยันตัวตน" หลอกลวงเป้าหมาย หรือกลับข้างกัน เช่น โทรแจ้งพ่อแม่ว่าลูกคุณเข้าโรงพยาบาล (ให้ข้อมูลโรงพยาบาลตามสิทธิ์ได้อย่างถูกต้อง) และหลอกให้โอนเงินค่าผ่าตัดด่วน |
| 🔴 Financial Fraud | ใช้แอบอ้างเพื่อฉ้อโกงผู้อื่น ทำหนังสือกู้ยืมเงินระหว่างบุคคลปลอม |
| 🟠 Stalking | ใช้ที่อยู่เต็มติดตามตัวบุคคล |
| 🟠 เลือกปฏิบัติ | ใช้ข้อมูลสิทธิรักษาพยาบาลจำแนกฐานะเศรษฐกิจ |
จุดที่ทำให้ช่องโหว่นี้ร้ายแรงกว่าการรั่วไหลทั่วไป คือ เลขบัตรประชาชนของบิดา-มารดา ที่ API ส่งกลับมา สามารถนำไปค้นต่อได้เป็นลูกโซ่:
flowchart LR
A["🔍 ค้นหาบุคคล A"] --> B["ได้ข้อมูล A\n+ เลขบัตรพ่อ/แม่"]
B --> C["🔍 ค้นหาพ่อ"]
B --> D["🔍 ค้นหาแม่"]
C --> E["ได้ข้อมูลพ่อ\n+ เลขบัตรปู่/ย่า"]
D --> F["ได้ข้อมูลแม่\n+ เลขบัตรตา/ยาย"]
E --> G["🔄 ค้นต่อเรื่อยๆ\nได้ข้อมูลทั้งตระกูล"]
F --> G
style A fill:#4a90d9,color:#fff
style B fill:#d0021b,color:#fff
style E fill:#d0021b,color:#fff
style F fill:#d0021b,color:#fff
style G fill:#8b0000,color:#fff
กล่าวคือ การค้นหาบุคคลเพียง 1 คน อาจนำไปสู่การเข้าถึงข้อมูลของ ทั้งครอบครัว/ตระกูล โดยอัตโนมัติ
หมายเหตุ: จากการวิเคราะห์ source code พบว่าข้อมูลบิดา-มารดาไม่ได้มีครบทุกรายการ บางบุคคลอาจไม่มีข้อมูลส่วนนี้ อย่างไรก็ตาม สำหรับรายการที่มีข้อมูล ความเสี่ยงนี้มีอยู่จริง
หากคุณ มีสิทธิบัตรทอง สิทธิประกันสังคม สิทธิข้าราชการ/รัฐวิสาหกิจ มีความเป็นไปได้ว่าข้อมูลของคุณ อาจถูกเข้าถึงได้ ผ่านช่องโหว่นี้ อย่างไรก็ตาม ไม่มีทางทราบแน่ชัดว่าข้อมูลของใครถูกดึงไปแล้วบ้าง หากไม่มีการตรวจสอบ access log จากฝั่ง "หน่วยงานรัฐ"
- ระวังการติดต่อที่น่าสงสัย หากมีคนโทรมาอ้างว่าเป็นหน่วยงานราชการแล้วสามารถบอกข้อมูลส่วนตัวของคุณได้ อย่าเชื่อทันที
- ระวัง Social Engineering อย่าให้ข้อมูลเพิ่มเติม แม้อีกฝ่ายจะรู้ชื่อพ่อ-แม่ หรือข้อมูลอื่นของคุณ
- ตรวจสอบเครดิตบูโร หากกังวลว่าเลขบัตรประชาชนอาจถูกนำไปใช้ ควรตรวจสอบรายงานเครดิตของคุณ
- ติดตามข่าวสาร จาก "หน่วยงานรัฐ" และ สคส. เกี่ยวกับมาตรการแก้ไขและเยียวยา
Disclaimer: รายงานฉบับนี้จัดทำเพื่อแจ้งเตือนสาธารณะเกี่ยวกับช่องโหว่ด้านความปลอดภัยที่ได้รับการแก้ไขแล้ว ไม่มีการเปิดเผย credentials, exploit code, หรือรายละเอียดทางเทคนิคที่สามารถนำไปใช้โจมตีระบบได้ ผู้เขียนมิได้มีเจตนาทำลายชื่อเสียงของหน่วยงานใด แต่เชื่อว่าประชาชนมีสิทธิ์ทราบเมื่อข้อมูลส่วนบุคคลของตนอาจได้รับผลกระทบ