Certificate Pinning은 모바일 애플리케이션이 CA 체인의 인증서를 단순히 신뢰하는 대신, 서버의 인증서가 사전에 알려진 샘플과 일치하는지 확인하는 보안 기술입니다. 수백 개의 인증 기관에 의존하는 일반적인 TLS 검증과 달리, pinning은 신뢰를 특정 단일 인증서나 그 공개키로 좁힙니다. OWASP 모바일 보안 테스트 가이드(2024)에 따르면, Certificate Pinning을 구현하면 인증서 교체와 관련된 Man-in-the-Middle 공격 시나리오의 100%를 차단할 수 있습니다. OWASP MSTG, 2024
핵심 요약
Certificate Pinning은 애플리케이션이 서버 인증서의 샘플을 저장(또는 “고정”)하고 각 연결 시 수신된 인증서를 이 샘플과 비교하는 보안 메커니즘입니다. 인증서가 일치하지 않으면 — 신뢰할 수 있는 인증 기관에 의해 공식적으로 서명된 경우에도 — 연결이 종료됩니다. 이는 공격자가 손상된 CA를 통해 가짜 인증서를 얻는 공격(2011년 DigiNotar 또는 2011년 Comodo 사례)으로부터 보호합니다.
Pinning 프로세스는 세 단계로 구성됩니다: 신뢰할 수 있는 인스턴스에서 인증서 또는 공개키의 지문(fingerprint) 추출; 이 지문을 애플리케이션 코드 또는 리소스에 저장; TLS 핸드셰이크 중 비교. 개발자는 전체 인증서의 SHA-256 지문 또는 공개키만(Public Key Pinning) 고정할 수 있습니다. 두 번째 접근 방식이 선호됩니다: 인증서가 갱신되어도 공개키는 종종 동일하게 유지되므로 앱이 서버와의 연결을 잃지 않습니다. OWASP 권장 사항에 따르면 최소 핀 수는 2개입니다: 현재 핀 1개와 키 순환을 위한 백업 핀 1개입니다. OkHttp 및 TrustKit과 같은 최신 라이브러리는 개발자의 추가 노력 없이 각 TLS 연결 시 지정된 핀의 검증 프로세스를 자동화합니다. Pinning은 표준 TLS 검증을 대체하는 것이 아니라 보완한다는 점을 이해하는 것이 중요합니다: 먼저 인증서 체인 검증과 함께 일반 핸드셰이크가 수행된 후 추가 pinning 검사가 수행됩니다. 이 2단계 보호는 잘못된 인증서 발급 및 인증 기관 인프라에 대한 공격을 포함하여 CA 손상과 관련된 취약점을 제거합니다.
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 지문 — 공개키의 추상화 — 를 저장합니다. 동일한 키로 인증서가 갱신되면(키 재사용) 핀은 유효하게 유지됩니다. 키가 변경되면 — 개발자는 사전에 애플리케이션 업데이트에 백업 핀을 추가합니다. 모바일 프로젝트에서는 최소/최대 핀 전략이 사용됩니다: 백업을 포함한 최소 2개 핀, 비대화 및 검증 시간 증가를 방지하기 위한 최대 4개 핀입니다.
특정 pinning 유형의 선택은 애플리케이션 아키텍처와 요구 사항에 따라 달라집니다. 단일 도메인을 통해 REST API로 작업하는 공개 모바일 애플리케이션의 경우 OkHttp 또는 TrustKit을 통한 2개 핀이 있는 Public Key Pinning이 최적입니다. 자체 인증 기관이 있는 엔터프라이즈 애플리케이션의 경우 CA Pinning이 적합합니다 — 신뢰가 최종 인증서가 아닌 CA에 바인딩되므로 클라이언트 인증서 변경 시 업데이트가 필요하지 않습니다. IoT 및 임베디드 시스템의 경우 전체 인증서 고정을 통한 Certificate Pinning이 권장됩니다: 장치가 거의 업데이트되지 않으므로 전체 신뢰 체인에 대한 제어가 중요합니다. 핀 만료일 모니터링은 필수 관행입니다: 현재 인증서가 만료되기 전에 새 핀으로 애플리케이션 업데이트를 출시하기 위해 인증서 만료 30일, 14일, 7일 전에 알림을 설정합니다. 새 핀으로 업데이트 출시를 자동화하려면 앱 스토어에 새 버전을 게시하지 않고 핀 목록을 동적으로 업데이트할 수 있는 Firebase Remote Config 또는 사용자 지정 구성 API를 사용하는 것이 좋습니다.
Certificate Pinning은 모바일 애플리케이션 보안을 크게 향상시키지만 개발 팀에 운영 부담을 줍니다. 잘못된 구현으로 인한 연결 차단 위험과 보안 이점을 저울질하는 것이 중요합니다.
주요 장점은 CA 손상 사례를 포함한 Man-in-the-Middle 공격으로부터의 보호입니다. Pinning은 공격자가 발행한 가짜 인증서를 무용지물로 만듭니다: CA가 위조 서명을 한 경우에도 애플리케이션이 이를 거부합니다. 추가 이점은 트래픽 검사를 위해 인증서를 교체하는 기업용 프록시 서버로부터의 보호입니다. Google Security Blog(2023)에 따르면, pinning을 사용하는 애플리케이션은 표준 TLS 검증만 사용하는 애플리케이션에 비해 트래픽 가로채기를 통해 손상될 확률이 86% 낮습니다.
Pinning의 주요 단점은 자체 차단 위험입니다: 애플리케이션 업데이트가 출시되기 전에 서버 인증서가 변경(갱신, 제공자 변경, 키 순환)되면 사용자가 서버에 액세스할 수 없게 됩니다. 추가 단점: 디버깅 복잡성(구성 변경 시마다 핀 업데이트 필요), TrustKit 사용 시 APK 크기 5–15 KB 증가, 새 릴리스 없이 변경 사항을 신속하게 롤백할 수 없음. 위험을 최소화하기 위해 백업 핀, 2–3개월마다 자동 순환, 애플리케이션이 이전 및 새 인증서를 모두 수용하는 유예 기간이 사용됩니다. 또한 pinning을 활성화한 개발 중에는 프록시 도구(Burp Suite, Charles)를 사용하여 네트워크 요청을 디버깅할 수 없다는 점도 고려해야 합니다 — 개발 빌드의 경우 BuildConfig.DEBUG 플래그를 통해 pinning을 비활성화해야 하며, QA 테스트는 보호 기능이 활성화된 릴리스 서명으로 수행해야 합니다. 일부 팀은 개발 중에도 보호를 유지하기 위해 개발 환경용 별도 pinning 인증서가 있는 스테이징 도메인을 사용합니다.
OkHttp — 네트워크 요청의 표준 라이브러리 — 를 사용하여 Android에서 Certificate Pinning을 구현하는 예를 살펴보겠습니다. OkHttp는 공개키의 SHA-256 해시를 수락하는 내장 CertificatePinner를 제공합니다.
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. 지문을 코드에 일반 텍스트로 저장하지 않고 암호화 또는 난독화하여 저장하는 것이 중요합니다: MobSF 정적 분석은 DEX 파일에서 원시 SHA-256 문자열을 쉽게 찾습니다. 핀은 AES를 통해 암호화된 res/raw 리소스에 저장하고 네이티브 코드(NDK/JNI)를 통해 애플리케이션 시작 시 복호화하는 것이 좋습니다.
iOS에서 Certificate Pinning의 주요 도구는 오픈 소스 TrustKit 라이브러리입니다. OkHttp와 달리 TrustKit은 Info.plist를 통해 선언적으로 구성되므로 애플리케이션을 다시 컴파일하지 않고도 핀을 변경할 수 있습니다. 구성에는 도메인이 있는 딕셔너리와 SHA-256 공개키 지문 배열이 포함됩니다. TrustKit은 NSURLSession 요청을 자동으로 가로채고 데이터 전송이 시작되기 전에 인증서를 확인합니다. TrustKit의 중요한 기능은 핀 검증 보고서 지원입니다: 라이브러리는 핀 불일치 시 지정된 엔드포인트에 보고서를 보내 인증서 이상에 신속하게 대응할 수 있습니다. Apple은 iOS 14부터 Info.plist에서 네이티브 NSPinnedDomains 메커니즘도 제공하지만, TrustKit은 더 유연한 구성, 보고서 지원 및 OS 업데이트 없이 핀을 핫스왑할 수 있는 기능으로 인해 선호되는 선택입니다. TrustKit은 didReceiveChallenge 델리게이트를 통해 URLSession과 통합되며, 핀 검증 성공 시 .performDefaultHandling을, 불일치 시 .cancelAuthenticationChallenge를 반환합니다. 핀 검증 보고서 모니터링을 위해 오류 빈도를 분석하는 별도의 엔드포인트를 설정하는 것이 좋습니다: 보고서 수가 급격히 증가하면 — 이는 MitM 공격 또는 즉각적인 핀 업데이트가 필요한 임박한 인증서 만료를 나타낼 수 있습니다.
자주 묻는 질문
Certificate Pinning은 휴대전화에 친구의 지문을 저장하는 것과 같습니다: “올바른” 서버 인증서가 어떻게 생겼는지 기억하고, 누군가 “공식” 기관의 신분증을 보여줘도 다른 사람을 신뢰하지 않습니다.
일반 HTTPS는 수백 개 기관 중 어떤 CA가 서명한 인증서든 신뢰합니다. Certificate Pinning은 추가 검사를 추가합니다: 인증서는 단순히 유효한 것뿐만 아니라 애플리케이션 코드에 하드코딩한 특정 인증서여야 합니다.
2–3개의 핀을 저장하는 것이 좋습니다: 현재 핀과 새 인증서용 백업 핀입니다. 인증서 변경 1–2개월 전에 향후 인증서의 핀을 추가한 애플리케이션의 새 버전을 출시합니다. 변경 후 이전 핀은 다음 릴리스에서 제거됩니다.
네, 가능합니다. Pinning은 Let’s Encrypt를 포함한 모든 인증서에서 작동합니다. 무료 인증서는 유효 기간이 짧다(3개월)는 점을 기억하는 것이 중요하므로 백업 핀 전략과 자동 순환이 필수가 됩니다.
Pinning 테스트에는 Burp Suite 또는 mitmproxy를 사용합니다. Pinning을 사용하는 애플리케이션이 올바르게 구성된 경우 프록시 도구가 트래픽을 가로챌 수 없습니다 — 핸드셰이크 단계에서 연결이 종료됩니다. 통합 테스트에는 OkHttp의 MockWebServer를 사용합니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.