Certificate Pinning คือเทคนิคความปลอดภัยที่แอปพลิเคชันมือถือตรวจสอบว่าใบรับรองของเซิร์ฟเวอร์ตรงกับตัวอย่างที่ทราบล่วงหน้า แทนที่จะเชื่อถือใบรับรองใด ๆ จากห่วงโซ่ CA ต่างจากการตรวจสอบ TLS ทั่วไปที่อาศัยหน่วยงานออกใบรับรองหลายร้อยแห่ง Pinning จะจำกัดความเชื่อถือให้เหลือเพียงใบรับรองเฉพาะหรือคีย์สาธารณะของมันเท่านั้น ตามคู่มือการทดสอบความปลอดภัยมือถือของ OWASP (2024) การนำ Certificate Pinning ไปใช้จะบล็อก 100% ของสถานการณ์การโจมตีแบบ Man-in-the-Middle ที่เกี่ยวข้องกับการแทนที่ใบรับรอง OWASP MSTG, 2024
ประเด็นสำคัญ
Certificate Pinning เป็นกลไกความปลอดภัยที่แอปพลิเคชันจัดเก็บ (หรือ “ปักหมุด”) ตัวอย่างใบรับรองของเซิร์ฟเวอร์และเปรียบเทียบใบรับรองที่ได้รับกับตัวอย่างนี้ในการเชื่อมต่อแต่ละครั้ง หากใบรับรองไม่ตรงกัน — การเชื่อมต่อจะถูกยกเลิก แม้ว่าจะได้รับการลงนามอย่างเป็นทางการโดยหน่วยงานออกใบรับรองที่เชื่อถือได้ก็ตาม ซึ่งป้องกันการโจมตีที่ผู้โจมตีได้รับใบรับรองปลอมผ่าน CA ที่ถูกบุกรุก (เช่นที่เกิดขึ้นกับ DigiNotar ในปี 2011 หรือ Comodo ในปี 2011)
กระบวนการ pinning ประกอบด้วยสามขั้นตอน: การแยกลายพิมพ์นิ้วมือ (fingerprint) ของใบรับรองหรือคีย์สาธารณะจากอินสแตนซ์ที่เชื่อถือได้; การจัดเก็บลายพิมพ์นิ้วมือนี้ในโค้ดหรือทรัพยากรของแอปพลิเคชัน; การเปรียบเทียบในระหว่างการจับมือ TLS นักพัฒนาสามารถปักหมุดลายพิมพ์นิ้วมือ SHA-256 ของใบรับรองทั้งหมดหรือเฉพาะคีย์สาธารณะ (Public Key Pinning) วิธีที่สองดีกว่า: เมื่อต่ออายุใบรับรอง คีย์สาธารณะมักจะยังคงเดิม และแอปจะไม่สูญเสียการเชื่อมต่อกับเซิร์ฟเวอร์ ตามคำแนะนำของ OWASP จำนวนพินขั้นต่ำคือ 2: พินปัจจุบันหนึ่งอันและพินสำรองหนึ่งอันสำหรับการหมุนเวียนคีย์ ไลบรารีสมัยใหม่อย่าง OkHttp และ TrustKit จะทำให้กระบวนการตรวจสอบพินที่ระบุในแต่ละการเชื่อมต่อ TLS เป็นอัตโนมัติโดยไม่ต้องใช้ความพยายามเพิ่มเติมจากนักพัฒนา สิ่งสำคัญคือต้องเข้าใจว่า pinning ไม่ได้แทนที่การตรวจสอบ TLS มาตรฐาน แต่เสริมมัน: ขั้นแรกจะมีการจับมือปกติพร้อมการตรวจสอบความถูกต้องของห่วงโซ่ใบรับรอง จากนั้นจึงมีการตรวจสอบ pinning เพิ่มเติม การป้องกันสองระดับนี้ช่วยขจัดช่องโหว่ที่เกี่ยวข้องกับการบุกรุก CA รวมถึงกรณีการออกใบรับรองที่ผิดพลาดและการโจมตีโครงสร้างพื้นฐานของหน่วยงานออกใบรับรอง
มีหลายวิธีในการ implement Certificate Pinning แต่ละวิธีมีลักษณะการจัดเก็บและการตรวจสอบของตัวเอง การเลือกวิธีขึ้นอยู่กับสถาปัตยกรรมของแอปพลิเคชัน ความถี่ในการอัปเดตใบรับรอง และข้อกำหนดด้านความยืดหยุ่น
| ประเภทของ Pinning | สิ่งที่จัดเก็บ | ความยืดหยุ่น | ตัวอย่างการใช้งาน |
|---|---|---|---|
| Certificate Pinning | ใบรับรอง X.509 ทั้งหมด | ต่ำ | ใบรับรองคงที่ 1–2 ปี |
| Public Key Pinning | คีย์สาธารณะ (SPKI) | ปานกลาง | วิธีที่ OWASP แนะนำ |
| Hash Pinning | ลายพิมพ์นิ้วมือ SHA-256 | ปานกลาง | เป็นที่นิยมใน OkHttp (certificatePinner) |
| CA Pinning | CA กลาง | สูง | แอปพลิเคชันองค์กร |
วิธีที่สมดุลที่สุดคือ Public Key Pinning ซึ่งแนะนำโดย OWASP และ Google แทนที่จะใช้ใบรับรองเฉพาะ (ซึ่งเปลี่ยนแปลงทุก 1–2 ปี) แอปพลิเคชันจะจัดเก็บลายพิมพ์นิ้วมือ SubjectPublicKeyInfo — ซึ่งเป็นนามธรรมของคีย์สาธารณะ หากใบรับรองถูกต่ออายุด้วยคีย์เดียวกัน (การ reuse คีย์) พินจะยังคงใช้ได้ หากคีย์เปลี่ยนแปลง — นักพัฒนาจะเพิ่มพินสำรองในการอัปเดตแอปพลิเคชันล่วงหน้า ในโปรเจกต์มือถือจะใช้กลยุทธ์พินขั้นต่ำ/สูงสุด: ขั้นต่ำ 2 พินรวมถึงพินสำรอง และสูงสุด 4 พินเพื่อป้องกันการบวมและเวลาในการตรวจสอบที่เพิ่มขึ้น
การเลือกประเภท pinning เฉพาะนั้นขึ้นอยู่กับสถาปัตยกรรมและข้อกำหนดของแอปพลิเคชัน สำหรับแอปพลิเคชันมือถือสาธารณะที่ทำงานกับ REST API ผ่านโดเมนเดียว Public Key Pinning ด้วยสองพินผ่าน OkHttp หรือ TrustKit เหมาะสมที่สุด สำหรับแอปพลิเคชันองค์กรที่มีหน่วยงานออกใบรับรองของตัวเอง CA Pinning เหมาะสม — ไม่ต้องอัปเดตเมื่อใบรับรองไคลเอ็นต์เปลี่ยนแปลง เนื่องจากความเชื่อถือผูกติดอยู่กับ CA ไม่ใช่ใบรับรองปลายทาง สำหรับระบบ IoT และระบบฝังตัว แนะนำให้ใช้ Certificate Pinning ที่มีการปักหมุดใบรับรองเต็มรูปแบบ: อุปกรณ์ไม่ค่อยได้รับการอัปเดต ดังนั้นการควบคุมห่วงโซ่ความเชื่อถือทั้งหมดจึงสำคัญ การตรวจสอบวันที่หมดอายุของพินเป็นแนวปฏิบัติบังคับ: ตั้งค่าการแจ้งเตือนล่วงหน้า 30, 14 และ 7 วันก่อนใบรับรองหมดอายุเพื่อเผยแพร่การอัปเดตแอปพลิเคชันด้วยพินใหม่ก่อนที่ใบรับรองปัจจุบันจะกลายเป็นไม่ถูกต้อง สำหรับการทำให้การเผยแพร่อัปเดตด้วยพินใหม่เป็นอัตโนมัติ แนะนำให้ใช้ Firebase Remote Config หรือ API การกำหนดค่าแบบกำหนดเองที่อนุญาตให้อัปเดตรายการพินแบบไดนามิกโดยไม่ต้องเผยแพร่เวอร์ชันใหม่ในร้านค้าแอป
Certificate Pinning เพิ่มความปลอดภัยของแอปพลิเคชันมือถืออย่างมาก แต่สร้างภาระในการดำเนินงานให้กับทีมพัฒนา สิ่งสำคัญคือต้องชั่งน้ำหนักประโยชน์ด้านความปลอดภัยกับความเสี่ยงของการถูกบล็อกการเชื่อมต่อเนื่องจากการ implement ที่ไม่ถูกต้อง
ข้อได้เปรียบหลักคือการป้องกันการโจมตีแบบ Man-in-the-Middle รวมถึงกรณีการบุกรุก CA Pinning ทำให้ใบรับรองปลอมที่ออกโดยผู้โจมตีไร้ประโยชน์: แม้ว่า CA จะลงนามในใบรับรองปลอม แอปพลิเคชันจะปฏิเสธมัน ข้อดีเพิ่มเติมคือการป้องกันพร็อกซีเซิร์ฟเวอร์ขององค์กรที่แทนที่ใบรับรองเพื่อตรวจสอบการรับส่งข้อมูล ตาม Google Security Blog (2023) แอปพลิเคชันที่มี pinning มีโอกาสถูกบุกรุกผ่านการสกัดกั้นการรับส่งข้อมูลน้อยกว่า 86% เมื่อเทียบกับแอปพลิเคชันที่ใช้เฉพาะการตรวจสอบ TLS มาตรฐาน
ข้อเสียหลักของ pinning คือความเสี่ยงในการถูกบล็อกตัวเอง: หากใบรับรองเซิร์ฟเวอร์เปลี่ยนแปลง (การต่ออายุ การเปลี่ยนผู้ให้บริการ การหมุนเวียนคีย์) ก่อนที่การอัปเดตแอปพลิเคชันจะเผยแพร่ ผู้ใช้จะสูญเสียการเข้าถึงเซิร์ฟเวอร์ ข้อเสียเพิ่มเติม: ความซับซ้อนในการดีบัก (ทุกการเปลี่ยนแปลงการตั้งค่าต้องอัปเดตพิน) ขนาด APK เพิ่มขึ้น 5–15 KB เมื่อใช้ TrustKit และไม่สามารถย้อนกลับการเปลี่ยนแปลงได้อย่างรวดเร็วหากไม่มีการเผยแพร่ใหม่ เพื่อลดความเสี่ยง จะใช้พินสำรอง การหมุนเวียนอัตโนมัติทุก 2–3 เดือน และระยะเวลาผ่อนผันที่แอปพลิเคชันยอมรับทั้งใบรับรองเก่าและใหม่ สิ่งสำคัญที่ต้องพิจารณาคือระหว่างการพัฒนาโดยเปิดใช้ pinning จะไม่สามารถใช้เครื่องมือพร็อกซี (Burp Suite, Charles) เพื่อดีบักคำขอเครือข่ายได้ — สำหรับบิลด์พัฒนา pinning ต้องถูกปิดใช้งานผ่านแฟล็ก BuildConfig.DEBUG และการทดสอบ QA ควรดำเนินการบนลายเซ็นเผยแพร่โดยเปิดใช้การป้องกันไว้ บางทีมใช้โดเมน staging ที่มีใบรับรอง pinning แยกต่างหากสำหรับสภาพแวดล้อม dev เพื่อรักษาการป้องกันแม้ในระหว่างการพัฒนา
มาดูตัวอย่างการ implement Certificate Pinning บน Android โดยใช้ OkHttp — ไลบรารีมาตรฐานสำหรับคำขอเครือข่าย OkHttp มี CertificatePinner ในตัวที่ยอมรับแฮช SHA-256 ของคีย์สาธารณะ
val certificatePinner = CertificatePinner.Builder()
.add(
"api.example.com",
"sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
)
.add(
"api.example.com",
"sha256/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB="
)
.build()
val client = OkHttpClient.Builder()
.certificatePinner(certificatePinner)
.build()
ในโค้ดด้านบน เราเพิ่มพินสองอันสำหรับโดเมน api.example.com: พินหลัก (ใบรับรองปัจจุบัน) และพินสำรอง (สำหรับการหมุนเวียน) OkHttp จะตรวจสอบโดยอัตโนมัติว่าใบรับรองของเซิร์ฟเวอร์ตรงกับลายพิมพ์นิ้วมือ SHA-256 ที่ระบุหนึ่งรายการ ในการรับลายพิมพ์นิ้วมือ SHA-256 ของใบรับรอง ให้ใช้คำสั่ง: openssl s_client -connect api.example.com:443 | openssl x509 -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | base64 สิ่งสำคัญคือต้องจัดเก็บลายพิมพ์นิ้วมือไม่ใช่เป็นข้อความธรรมดาในโค้ด แต่เข้ารหัสหรือทำให้สับสน: การวิเคราะห์แบบ static ของ MobSF สามารถค้นหาสตริง SHA-256 ดิบในไฟล์ DEX ได้อย่างง่ายดาย แนะนำให้จัดเก็บพินในทรัพยากร res/raw เข้ารหัสผ่าน AES และถอดรหัสเมื่อเริ่มต้นแอปพลิเคชันผ่านโค้ดเนทีฟ (NDK/JNI)
บน iOS เครื่องมือหลักสำหรับ Certificate Pinning คือไลบรารีโอเพนซอร์ส TrustKit ต่างจาก OkHttp ตรงที่ TrustKit ถูกกำหนดค่าแบบ declarative ผ่าน Info.plist ซึ่งอนุญาตให้เปลี่ยนพินโดยไม่ต้องคอมไพล์แอปพลิเคชันใหม่ การกำหนดค่ารวมถึงพจนานุกรมที่มีโดเมนและอาร์เรย์ของลายพิมพ์นิ้วมือ SHA-256 ของคีย์สาธารณะ TrustKit จะสกัดกั้นคำขอ NSURLSession โดยอัตโนมัติและตรวจสอบใบรับรองก่อนการส่งข้อมูลจะเริ่มขึ้น คุณสมบัติที่สำคัญของ TrustKit คือการสนับสนุนรายงานการตรวจสอบพิน: ไลบรารีสามารถส่งรายงานไปยัง endpoint ที่ระบุเมื่อพินไม่ตรงกัน ทำให้สามารถตอบสนองต่อความผิดปกติของใบรับรองได้อย่างรวดเร็ว Apple ยังมีกลไกดั้งเดิม NSPinnedDomains ใน Info.plist ตั้งแต่ iOS 14 แต่ TrustKit ยังคงเป็นตัวเลือกที่ต้องการเนื่องจากการกำหนดค่าที่ยืดหยุ่นกว่า การสนับสนุนรายงาน และความสามารถในการสลับพินแบบ hot-swap โดยไม่ต้องอัปเดตระบบปฏิบัติการ สิ่งสำคัญที่ควรทราบคือ TrustKit ผสานรวมกับ URLSession ผ่าน delegate didReceiveChallenge โดยส่งคืน .performDefaultHandling เมื่อตรวจสอบพินสำเร็จ และ .cancelAuthenticationChallenge เมื่อไม่ตรงกัน สำหรับการตรวจสอบรายงานการตรวจสอบพิน แนะนำให้ตั้งค่า endpoint แยกต่างหากที่วิเคราะห์ความถี่ของข้อผิดพลาด: หากจำนวนรายงานเพิ่มขึ้นอย่างรวดเร็ว — นี่อาจบ่งบอกถึงการโจมตี MitM หรือใบรับรองกำลังจะหมดอายุซึ่งจำเป็นต้องอัปเดตพินทันที
คำถามที่พบบ่อย
Certificate Pinning เหมือนกับการบันทึกลายนิ้วมือของเพื่อนในโทรศัพท์ของคุณ: คุณจำได้ว่าใบรับรองเซิร์ฟเวอร์ที่ “ถูกต้อง” มีลักษณะอย่างไร และคุณจะไม่เชื่อถือใครอื่น แม้ว่าจะมีคนแสดงบัตรประจำตัวจากหน่วยงาน “ทางการ” ก็ตาม
HTTPS ทั่วไปเชื่อถือใบรับรองใด ๆ ที่ลงนามโดย CA ใด ๆ จากหลายร้อยหน่วยงาน Certificate Pinning เพิ่มการตรวจสอบเพิ่มเติม: ใบรับรองไม่เพียงแต่ต้องใช้ได้เท่านั้น แต่ต้องเป็นใบรับรองที่คุณ hardcode ไว้ในโค้ดของแอปพลิเคชันโดยเฉพาะ
แนะนำให้จัดเก็บ 2–3 พิน: พินปัจจุบันและพินสำรองสำหรับใบรับรองใหม่ 1–2 เดือนก่อนการเปลี่ยนแปลงใบรับรอง ให้เผยแพร่แอปพลิเคชันเวอร์ชันใหม่ที่มีพินของใบรับรองในอนาคตเพิ่มเข้าไป หลังจากการเปลี่ยนแปลง พินเก่าจะถูกลบออกในการเผยแพร่ครั้งถัดไป
ได้ สามารถใช้ได้ Pinning ทำงานกับใบรับรองใด ๆ รวมถึง Let’s Encrypt สิ่งสำคัญคือต้องจำไว้ว่าใบรับรองฟรีมีอายุสั้น (3 เดือน) ดังนั้นกลยุทธ์พินสำรองและการหมุนเวียนอัตโนมัติจึงกลายเป็นสิ่งจำเป็น
ใช้ Burp Suite หรือ mitmproxy สำหรับทดสอบ pinning หากแอปพลิเคชันที่มี pinning ถูกกำหนดค่าอย่างถูกต้อง เครื่องมือพร็อกซีจะไม่สามารถสกัดกั้นการรับส่งข้อมูลได้ — การเชื่อมต่อจะถูกยกเลิกในขั้นตอนการจับมือ สำหรับการทดสอบ integration ให้ใช้ MockWebServer ของ OkHttp
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม