SSL Pinning เป็นเทคนิคความปลอดภัยที่แอปพลิเคชันตรวจสอบใบรับรองเซิร์ฟเวอร์กับลายนิ้วมือหรือใบรับรองที่ทราบล่วงหน้า แทนที่จะพึ่งพาห่วงโซ่ความเชื่อถือของ CA ต่างจากการตรวจสอบมาตรฐาน การปักหมุดป้องกันการสกัดกั้นการรับส่งข้อมูลผ่านศูนย์ออกใบรับรองระดับรากปลอม ตาม OWASP Mobile Security Testing Guide (2025) เทคนิคนี้อยู่ใน 3 อันดับแรกของการควบคุมที่แนะนำสำหรับการป้องกันการโจมตีแบบ MITM หากไม่มีการปักหมุด ผู้โจมตีที่มีใบรับรองรากปลอมสามารถถอดรหัสการรับส่งข้อมูล HTTPS ทั้งหมดของแอปพลิเคชันได้
ประเด็นสำคัญ
SSL Pinning เป็นกลไกความปลอดภัยที่แอปพลิเคชันมือถือหรือเว็บจดจำใบรับรองเซิร์ฟเวอร์ที่เชื่อถือได้หรือคีย์สาธารณะ และปฏิเสธการเชื่อมต่อใดๆ ที่ใบรับรองไม่ตรงกับที่เก็บไว้ ในรูปแบบ HTTPS มาตรฐาน ไคลเอ็นต์ตรวจสอบใบรับรองผ่านห่วงโซ่ความเชื่อถือไปยัง CA ราก — CA ใดๆ ก็สามารถลงนามใบรับรองสำหรับโดเมนใดๆ ได้ SSL Pinning ขจัดจุดอ่อนนี้: แทนที่จะเชื่อถือ CA นับร้อย แอปพลิเคชันเชื่อถือเพียงใบรับรองเฉพาะเพียงใบเดียว
ปัญหาของการตรวจสอบมาตรฐานคือ CA รากนับร้อยแห่งใดแห่งหนึ่งสามารถออกใบรับรองที่ถูกต้องสำหรับโดเมนของคุณได้ — โดยบังเอิญหรือภายใต้การบังคับขู่เข็ญ ผู้โจมตีที่สามารถเข้าถึงพร็อกซีองค์กรที่มีใบรับรองรากของตนเองสามารถดำเนินการ โจมตี MITM โดยไม่มีการเตือนจากเบราว์เซอร์ SSL Pinning ปิดช่องโหว่นี้: แม้ว่า CA จะออกใบรับรองปลอม แอปพลิเคชันจะปฏิเสธเพราะลายนิ้วมือไม่ตรงกับที่บันทึกไว้
ในแอปพลิเคชันมือถือ SSL Pinning มีความสำคัญเป็นพิเศษเพราะอุปกรณ์มักทำงานบนเครือข่ายที่ไม่ปลอดภัย — Wi-Fi สาธารณะ พร็อกซีองค์กรที่มีการตรวจสอบการรับส่งข้อมูล จุดเชื่อมต่อที่ติดเชื้อ ตาม Verizon Mobile Security Index (2025) กว่า 60% ของการละเมิดข้อมูลในแอปพลิเคชันมือถือเกี่ยวข้องกับการสกัดกั้นการรับส่งข้อมูลในชั้นการขนส่ง
แอปพลิเคชันมือถือส่งข้อมูลที่ละเอียดอ่อน — โทเค็นการยืนยันตัวตน ข้อมูลการชำระเงิน ข้อมูลส่วนบุคคลของผู้ใช้ หากไม่มีการป้องกันเพิ่มเติม HTTPS อาจถูกบุกรุกผ่านการแทนที่ใบรับรองรากบนอุปกรณ์ — ตัวอย่างเช่น หลังจากติดตั้งโปรไฟล์องค์กรหรือแอปพลิเคชันที่เป็นอันตราย SSL Pinning รับประกันว่าแม้มีการติดตั้ง CA รากปลอมบนอุปกรณ์ แอปพลิเคชันจะยังคงตรวจสอบใบรับรองกับบัญชีขาวของตนเอง
กระบวนการ SSL Pinning ประกอบด้วยสามขั้นตอน: การจับลายนิ้วมือ การตรวจสอบเมื่อเชื่อมต่อ และการจัดการข้อผิดพลาด ในระหว่างการพัฒนา วิศวกรจะได้รับลายนิ้วมือ SHA-256 ของใบรับรองเซิร์ฟเวอร์ (openssl x509 -fingerprint -sha256) และฝังลงในโค้ดแอปพลิเคชันหรือไฟล์การกำหนดค่า ในการร้องขอ HTTPS แต่ละครั้ง แอปพลิเคชันจะคำนวณลายนิ้วมือของใบรับรองที่ได้รับและเปรียบเทียบกับที่เก็บไว้ — หากค่าไม่ตรงกัน การเชื่อมต่อจะถูกยกเลิก
ขั้นตอนแรก คือการปักหมุดในเวลาสร้าง: นักพัฒนารู้จักใบรับรองเซิร์ฟเวอร์ล่วงหน้าและฝังแฮชของพวกเขา ขั้นตอนที่สอง คือการปักหมุดเมื่อเชื่อมต่อครั้งแรก (เชื่อถือเมื่อใช้ครั้งแรก, TOFU): แอปพลิเคชันจดจำใบรับรองในการร้องขอครั้งแรกและใช้เพื่อตรวจสอบการร้องขอทั้งหมดที่ตามมา TOFU สะดวกสำหรับสภาพแวดล้อมแบบไดนามิกแต่เสี่ยงต่อการโจมตีครั้งแรก — หากการเชื่อมต่อครั้งแรกถูกสกัดกั้นแล้ว ใบรับรองปลอมจะถูกยอมรับว่าเชื่อถือได้
รายละเอียดที่สำคัญคือ หมุดสำรอง (backup pins) ใบรับรองมีวันที่หมดอายุ และเมื่อถูกแทนที่ แอปพลิเคชันที่ไม่ได้รับการอัปเดตจะสูญเสียการเชื่อมต่อกับเซิร์ฟเวอร์ วิศวกรเพิ่มลายนิ้วมืออีก 2–3 รายการ — ตัวอย่างเช่น ลายนิ้วมือของใบรับรองสำรองและลายนิ้วมือของ CA ราก หากใบรับรองหลักเปลี่ยนแปลง แอปพลิเคชันจะตรวจสอบกับหมุดสำรอง และการเชื่อมต่อยังคงทำงานต่อไป
# การรับลายนิ้วมือ SHA-256 ของใบรับรอง
openssl s_client -connect example.com:443 </dev/null 2>/dev/null | \
openssl x509 -pubkey -noout | \
openssl pkey -pubin -outform der | \
openssl dgst -sha256 -binary | \
base64
มีสองแนวทางหลักในการimplement การปักหมุด: การผูกกับใบรับรองทั้งหมด (certificate pinning) และการผูกกับคีย์สาธารณะ (public key pinning) แต่ละแนวทางมีจุดแข็งและข้อจำกัดของตัวเองที่ส่งผลต่อความปลอดภัยและการบำรุงรักษา
| ประเภท | วัตถุที่ผูก | ความยืดหยุ่น | ความปลอดภัย |
|---|---|---|---|
| Certificate Pinning | ใบรับรอง X.509 ทั้งหมด | ต่ำ — ต้องอัปเดตเมื่อใบรับรองเปลี่ยน | สูง — ผูกที่แม่นยำ |
| Public Key Pinning | คีย์สาธารณะของใบรับรอง | ปานกลาง — คีย์อาจอยู่ในใบรับรองใหม่ | สูง — ไวต่อรายละเอียดใบรับรองน้อยกว่า |
| Hash Pinning | แฮช SHA-256 ของใบรับรองหรือคีย์ | สูง — สามารถเปลี่ยนใบรับรองได้โดยไม่เปลี่ยนคีย์ | ปานกลาง — ขึ้นอยู่กับความแข็งแรงของแฮช |
การปักหมุดใบรับรองเป็นวิธีที่เข้มงวดที่สุด แอปพลิเคชันเก็บสำเนาของใบรับรองที่เชื่อถือได้หรือลายนิ้วมือ SHA-256 และเปรียบเทียบกับใบรับรองเซิร์ฟเวอร์ในทุก การเชื่อมต่อ HTTPS วิธีนี้ให้ความปลอดภัยสูงสุดแต่สร้างปัญหาระหว่างการหมุน — ใบรับรองมักมีอายุ 1–2 ปี หลังจากนั้นจำเป็นต้องอัปเดตแอปพลิเคชันแบบบังคับ แนะนำสำหรับระบบที่สำคัญที่มีวงจรการอัปเดตที่ควบคุมได้
การปักหมุดคีย์สาธารณะเป็นแนวทางที่ยืดหยุ่นกว่า แทนที่จะใช้ใบรับรองทั้งหมด แอปพลิเคชันจะจดจำเฉพาะคีย์สาธารณะ RSA หรือ ECDSA ของเซิร์ฟเวอร์ คีย์สามารถคงอยู่ ไม่เปลี่ยนแปลง เมื่อออกใบรับรองใหม่ หากบริษัทใช้คู่คีย์เดียวกัน ซึ่งช่วยลดความถี่ในการอัปเดตแอปพลิเคชัน อย่างไรก็ตาม หากคีย์ถูกบุกรุก จะต้องเปลี่ยนแบบต่อเนื่องในทุกไคลเอ็นต์
บนแพลตฟอร์ม Apple SSL Pinning ถูกimplement ผ่านตัวแทน URLSession นักพัฒนาสร้างคลาสที่implement โปรโตคอล URLSessionDelegate และแทนที่เมธอด didReceive challenge ซึ่งตรวจสอบใบรับรองเซิร์ฟเวอร์ด้วยตนเองกับลายนิ้วมือที่เก็บไว้ แนวทางอื่นคือการใช้ Alamofire กับ ServerTrustManager ซึ่งทำให้การกำหนดค่าง่ายขึ้น
class SSLPinningDelegate: NSObject, URLSessionDelegate {
let pinnedHash = "sha256/Wi24BE7j5qLk0iLvPq6ePEsVRqZ1yW0F6wLg="
func urlSession(_ session: URLSession,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
guard let serverTrust = challenge.protectionSpace.serverTrust
else { return completionHandler(.cancelAuthenticationChallenge, nil) }
if validate(serverTrust, pinnedHash) {
completionHandler(.useCredential, URLCredential(trust: serverTrust))
} else {
completionHandler(.cancelAuthenticationChallenge, nil)
}
}
}
ในตัวอย่าง ตัวแทนได้รับการร้องขอการยืนยันตัวตนจาก URLSession แยก serverTrust จาก challenge และเปรียบเทียบลายนิ้วมือ SHA-256 ของใบรับรองกับที่เก็บไว้ หากลายนิ้วมือตรงกัน — การเชื่อมต่อ ดำเนินต่อไป มิฉะนั้น challenge จะถูกปฏิเสธ สำหรับการใช้งานจริง ควรเพิ่มการตรวจสอบหมุดสำรองหลายตัวและการบันทึกข้อผิดพลาดสำหรับการตรวจสอบ
เริ่มจาก iOS 14 Apple เพิ่มการสนับสนุนในตัวสำหรับ Certificate Pinning ผ่าน Info.plist นักพัฒนาระบุใบรับรองที่เชื่อถือได้ในคีย์ NSAppTransportSecurity กับพจนานุกรมย่อย NSPinnedDomains แนวทางนี้ไม่ต้องเขียนโค้ดแต่ยืดหยุ่นน้อยกว่า — ไม่สามารถเปลี่ยนหมุดแบบไดนามิกหรือบันทึกข้อผิดพลาดการตรวจสอบ
บน Android มีสามวิธีหลักในการimplement SSL Pinning: ผ่าน CertificatePinner ของไลบรารี OkHttp, ผ่าน Network Security Config ใน XML, และผ่านการตรวจสอบแบบกำหนดเองใน HttpsURLConnection OkHttp เป็นแนวทางที่ได้รับความนิยมและแนะนำมากที่สุด ใช้ใน Retrofit และไคลเอ็นต์ HTTP อื่นๆ
val certificatePinner = CertificatePinner.Builder()
.add("api.example.com",
"sha256/Wi24BE7j5qLk0iLvPq6ePEsVRqZ1yW0F6wLg=")
.add("api.example.com",
"sha256/FiPq6ePEsVRqZ1yW0F6wLgWi24BE7j5qLk0iL=") // หมุดสำรอง
.build()
val client = OkHttpClient.Builder()
.certificatePinner(certificatePinner)
.build()
ในการกำหนดค่า OkHttp นักพัฒนาระบุโดเมนและลายนิ้วมือ SHA-256 หนึ่งรายการขึ้นไป ที่ลายนิ้วมือแรก OkHttp เปรียบเทียบใบรับรองเซิร์ฟเวอร์กับหมุดที่ระบุ หากไม่ตรงกัน ไคลเอ็นต์จะโยน SSLPeerUnverifiedException หมุดสำรองเป็นสิ่งจำเป็น — หากไม่มี เมื่อใบรับรองเปลี่ยนแปลง คำขอ API จะเริ่มล้มเหลวทันที
Android รองรับ Certificate Pinning แบบประกาศผ่านการกำหนดค่า XML ตั้งแต่ API 24 ไฟล์ res/xml/network_security_config.xml มีรายการโดเมนและลายนิ้วมือของพวกเขา วิธีนี้สะดวกสำหรับการกำหนดค่าแบบคงที่แต่อนุญาตให้implement TOFU หรือตรรกะการตรวจสอบแบบกำหนดเองด้วย การบันทึก ความผิดปกติ
<!-- res/xml/network_security_config.xml -->
<network-security-config>
<domain-config cleartextTrafficPermitted="false">
<domain includeSubdomains="true">api.example.com</domain>
<pin-set expiration="2027-12-31">
<pin digest="SHA-256">
Wi24BE7j5qLk0iLvPq6ePEsVRqZ1yW0F6wLg=</pin>
<pin digest="SHA-256">
FiPq6ePEsVRqZ1yW0F6wLgWi24BE7j5qLk0iL=</pin>
</pin-set>
</domain-config>
</network-security-config>
SSL Pinning เพิ่มความปลอดภัยของแอปพลิเคชันมือถืออย่างมากแต่สร้างความซับซ้อนในการดำเนินงาน ข้อดีหลักคือการป้องกันการโจมตี MITM แม้เมื่อ CA รากถูกบุกรุก แอปพลิเคชันเชื่อถือเฉพาะใบรับรองที่นักพัฒนาระบุอย่างชัดเจน ไม่ใช่โครงสร้างพื้นฐานทั้งหมดของหน่วยงานออกใบรับรองสาธารณะ ซึ่งสำคัญเป็นพิเศษสำหรับ แอปพลิเคชันการเงิน แอปส่งข้อความ และแอปพลิเคชันที่มีข้อมูลที่ละเอียดอ่อน
ข้อเสียหลักคือความซับซ้อนของการหมุนใบรับรอง หากใบรับรองหมดอายุหรือถูกเพิกถอน ผู้ใช้ที่ไม่อัปเดตแอปพลิเคชันจะสูญเสียการเชื่อมต่อ นี้แก้ไขได้ผ่านหมุดสำรองและกลไกการอัปเดตแบบค่อยเป็นค่อยไป: แอปพลิเคชันใหม่รู้จักทั้งใบรับรองเก่าและใหม่ และหลังจากการอัปเดตผู้ใช้ทั้งหมด หมุดเก่าจะถูกลบออกจากโค้ด แนะนำให้รวม อย่างน้อย 2 หมุดสำรอง — หนึ่งสำหรับใบรับรองปัจจุบัน หนึ่งสำหรับอนาคต
การประนีประนอมอีกอย่างคือไม่สามารถใช้พร็อกซีสาธารณะสำหรับการดีบักการรับส่งข้อมูล (Charles Proxy, Burp Suite) โดยไม่ปิดการปักหมุด ซึ่งทำให้ การดีบัก คำขอเครือข่ายระหว่างการพัฒนาซับซ้อน วิธีแก้คือการคอมไพล์แบบมีเงื่อนไข: การปักหมุดถูกปิดในบิลด์ดีบักและเปิดในบิลด์รีลีส OWASP แนะนำให้ใช้แฟล็ก BuildConfig.DEBUG สำหรับการสลับ
| ด้าน | ข้อดี | ข้อเสีย |
|---|---|---|
| ความปลอดภัย | ป้องกัน MITM ผ่าน CA ปลอม | ความซับซ้อนเมื่อคีย์ถูกบุกรุก |
| การบำรุงรักษา | ควบคุมความเชื่อถืออย่างชัดเจน | การหมุนต้องอัปเดตแอปพลิเคชัน |
| การดีบัก | รับประกันการเชื่อมต่อกับเซิร์ฟเวอร์ที่ถูกต้อง | บล็อกพร็อกซีดีบัก |
คำถามที่พบบ่อย
การตรวจสอบ HTTPS มาตรฐานเชื่อถือใบรับรองใดๆ ที่ลงนามโดย CA รากที่รู้จัก SSL Pinning เชื่อถือเฉพาะ ใบรับรองเฉพาะ หรือคีย์ — หาก CA ออกใบรับรองปลอม แอปพลิเคชันจะปฏิเสธ
ใบรับรองมักมีอายุ 1–2 ปี แนะนำให้อัปเดตหมุด 3–6 เดือน ก่อนใบรับรองปัจจุบันหมดอายุ เพิ่มลายนิ้วมือใหม่เป็นหมุดสำรอง และลบอันเก่าหลังการหมุน
ได้ แต่ต้องพิจารณาว่า CDN อาจเปลี่ยนใบรับรองเมื่อสลับระหว่างเซิร์ฟเวอร์ขอบ แนะนำให้ปักหมุดที่ คีย์สาธารณะ แทนใบรับรองเฉพาะ และใช้หมุดสำรองหลายตัว
การเชื่อมต่อถูกยกเลิกด้วยข้อผิดพลาด — บน Android คือ SSLPeerUnverifiedException บน iOS challenge ถูกปฏิเสธด้วย .cancelAuthenticationChallenge แอปพลิเคชันควรจัดการข้อผิดพลาดนี้อย่างถูกต้องและแจ้งให้ผู้ใช้ทราบ
ไม่ แต่ OWASP แนะนำสำหรับแอปพลิเคชันที่จัดการกับข้อมูลที่ละเอียดอ่อน: ธนาคาร การดูแลสุขภาพ ระบบองค์กร สำหรับแอปพลิเคชันแบบอ่านอย่างเดียวทั่วไป การตรวจสอบ HTTPS มาตรฐานกับ ใบรับรอง EV มักเพียงพอ
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ