SSL/TLS는 모바일 애플리케이션과 서버 간의 데이터를 암호화하여 트래픽의 기밀성과 무결성을 보장하는 암호화 프로토콜입니다. Apple(2026)에 따르면 App Transport Security는 모든 iOS 기기에서 기본적으로 TLS 1.2 미만의 연결을 차단합니다. TLS 1.3은 TLS 1.2에 비해 핸드셰이크 시간을 2배 단축하여 모바일 애플리케이션의 UX를 개선합니다.
핵심 사항
SSL(Secure Sockets Layer)과 TLS(Transport Layer Security)는 네트워크를 통한 안전한 데이터 전송을 보장하는 암호화 프로토콜입니다. 1990년대 Netscape에서 개발한 SSL은 POODLE 및 BEAST 취약점으로 인해 버전 3.0 이후 구식으로 간주됩니다. 그 후속 제품인 TLS는 버전 1.0, 1.1, 1.2, 1.3을 거쳤으며 현재는 TLS 1.2와 TLS 1.3만 유효한 것으로 간주됩니다. 모든 최신 모바일 플랫폼은 네트워크 연결에 TLS를 요구하며 App Store와 Google Play는 검토 중에 이를 확인합니다.
TLS가 없으면 애플리케이션과 서버 간의 트래픽이 평문으로 전송됩니다 — 동일한 Wi-Fi 네트워크에 있는 누구나 Wireshark나 tcpdump를 사용하여 로그인, 비밀번호, 토큰 및 사용자의 개인 데이터를 가로챌 수 있습니다. TLS는 전송되는 모든 데이터를 암호화하고(전송 계층 암호화) X.509 인증서 체인을 통해 서버의 신뢰성을 확인합니다. IETF(2018)에 따르면 TLS 1.3은 최신 AEAD 암호(AES-GCM, ChaCha20-Poly1305)만 사용하며 RC4 및 3DES와 같은 구식 알고리즘을 제외합니다.
HTTPS(HTTP Secure)는 TLS 위의 HTTP입니다. 모바일 애플리케이션이 https://를 통해 요청하면 먼저 서버와 TLS 연결을 설정한 다음 암호화된 채널을 통해 HTTP 헤더와 요청 본문을 전송합니다. HTTPS 없이는 어떤 진지한 API도 작동해서는 안 됩니다 — 이것은 기본적인 보안 위생입니다. OWASP(2026)에 따르면 안전하지 않은 연결은 모바일 애플리케이션의 상위 3대 취약점 중 하나입니다.
TLS 핸드셰이크는 클라이언트와 서버 간의 보안 연결을 설정하는 프로세스입니다. 양측은 프로토콜 버전을 협상하고, 암호 제품군(cipher suite)을 선택하고, 비대칭 암호화를 통해 키를 교환하고, 인증서를 확인합니다. TLS 1.2에서는 핸드셰이크에 2회의 왕복 시간(2 RTT)이 필요합니다: 클라이언트 → 서버(ClientHello), 서버 → 클라이언트(ServerHello 및 Certificate), 그다음 최종 Finished 메시지. TLS 1.3은 이 프로세스를 1 RTT로 줄입니다.
첫 번째 단계: ClientHello — 클라이언트가 지원되는 TLS 버전, 암호 제품군 목록 및 난수를 보냅니다. 서버는 ServerHello로 응답하여 버전과 암호 제품군을 선택하고 X.509 인증서(Certificate)와 ServerHelloDone 메시지를 보냅니다. 클라이언트는 신뢰할 수 있는 인증 기관(CA) 체인을 통해 인증서를 확인하고, 사전 마스터 비밀(pre-master secret)을 생성한 후 인증서의 공개 키로 암호화하여 ClientKeyExchange에서 서버로 보냅니다. 그 후 양측은 세션 키를 생성하고 ChangeCipherSpec 및 Finished 메시지를 교환합니다. 이 시점부터 모든 데이터는 대칭적으로 암호화됩니다.
import Security
let url = URL(string: "https://api.example.com")!
let session = URLSession(configuration: .default,
delegate: self,
delegateQueue: nil)
func urlSession(
_ session: URLSession,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition,
URLCredential?) -> Void
) {
let trust = challenge.protectionSpace.serverTrust
guard let trust else {
completionHandler(.cancelAuthenticationChallenge, nil)
return
}
completionHandler(.useCredential, URLCredential(trust: trust))
}
URLSessionDelegate를 통한 iOS에서 URLAuthenticationChallenge 처리 예제. 이 메서드는 각 TLS 핸드셰이크 중에 호출되어 애플리케이션이 서버 인증서를 사용자 지정 확인할 수 있도록 합니다. 프로덕션 사용을 위해 SecTrustEvaluateWithError를 통한 인증서 확인을 추가하고 미리 저장된 지문과 비교한 후에만 useCredential을 호출하세요.
TLS 1.3(RFC 8446, 2018)은 10년 만의 첫 번째 주요 프로토콜 업데이트입니다. 주요 개선 사항: 핸드셰이크가 1 RTT로 단축(반복 연결의 경우 0 RTT), 구식 암호 제품군 제거(RSA 키 교환, CBC 모드), Perfect Forward Secrecy(PFS) 필수화, 서명된 전사(signed transcript)를 통한 다운그레이드 공격 방지. Qualys SSL Labs(2026)에 따르면 TLS 1.3은 PFS 덕분에 장기 서버 키가 손상된 경우에도 보호 기능을 제공합니다.
| 특성 | TLS 1.2 | TLS 1.3 |
|---|---|---|
| 핸드셰이크 | 2 RTT(완전) | 1 RTT(PSK로 0 RTT) |
| 암호 제품군 | 30개 이상 조합(RSA, DH, ECDH) | 5개 AEAD 제품군(AES-GCM, ChaCha20) |
| Forward Secrecy | 선택 사항(DHE, ECDHE) | 필수(모든 제품군) |
| iOS 지원 | iOS 5+ | iOS 12+ |
| Android 지원 | Android 4.0+ | Android 10+ |
| 구식 알고리즘 | RSA, CBC, RC4, 3DES | 완전히 제거됨 |
0-RTT(제로 왕복 시간)은 TLS 1.3의 기능으로, PSK(사전 공유 키)를 통한 반복 연결 시 ClientHello와 함께 데이터를 즉시 보낼 수 있습니다. 이는 특히 동일한 서버에 대한 빈번한 요청이 있을 때 모바일 애플리케이션에서 후속 화면 로딩을 가속화합니다. 그러나 0-RTT 데이터는 재생 공격(replay attacks)으로부터 보호되지 않습니다 — 가로채서 다시 보낼 수 있습니다. 부작용이 없는 멱등성 요청(GET, PUT)에만 0-RTT를 사용하세요.
App Transport Security(ATS)는 TLS 1.2 이상의 HTTPS 연결을 요구하는 Apple의 메커니즘으로 iOS 9부터 기본적으로 활성화됩니다. ATS는 모든 HTTP 연결 및 TLS 1.2 미만의 HTTPS를 차단합니다. 개발자는 특정 도메인에 대해 NSAppTransportSecurity를 통해 Info.plist에서 예외를 구성할 수 있지만 Apple은 예외를 최소화하고 모든 곳에서 HTTPS를 사용할 것을 권장합니다. ATS 요구 사항을 위반하면 App Store 검토에서 앱이 거부됩니다.
<!-- Info.plist — App Transport Security -->
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<false/>
<key>NSExceptionDomains</key>
<dict>
<key>cdn.example.com</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<false/>
<key>NSExceptionMinimumTLSVersion</key>
<string>TLSv1.2</string>
</dict>
</dict>
<key>NSAllowsLocalNetworking</key>
<true/>
</dict>
Info.plist의 ATS 구성. NSAllowsArbitraryLoads가 false로 설정되어 있습니다 — 모든 연결은 HTTPS를 사용해야 합니다. 도메인 cdn.example.com의 경우 최소 TLS 버전 1.2가 지정되고 NSAllowsLocalNetworking=true는 로컬 네트워크의 HTTP를 허용합니다(개발 서버에 유용). Apple은 NSExceptionDomains 없이 NSAllowsArbitraryLoads를 활성화하지 않는 것을 강력히 권장합니다 — 이는 일반 규칙이 아닌 예외여야 합니다.
Network Security Config는 Java/Kotlin 코드를 변경하지 않고 HTTPS와 TLS를 구성하는 Android의 메커니즘입니다. 구성은 network_security_config.xml 파일에 지정되고 android:networkSecurityConfig 속성을 통해 AndroidManifest에 연결됩니다. 신뢰할 수 있는 인증서(사용자 및 시스템 CA), Certificate Pinning, 평문 HTTP 비활성화, 디버그 재정의 및 트래픽 리디렉션 구성을 지원합니다.
<!-- res/xml/network_security_config.xml -->
<network-security-config>
<base-config cleartextTrafficPermitted="false">
<trust-anchors>
<certificates src="system" />
</trust-anchors>
</base-config>
<domain-config cleartextTrafficPermitted="false">
<domain includeSubdomains="true">api.example.com</domain>
<pin-set expiration="2027-01-01">
<pin digest="SHA-256">
47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=
</pin>
</pin-set>
</domain-config>
</network-security-config>
Android용 Network Security Config. Base-config는 평문 트래픽을 차단하고 시스템 CA 인증서만 신뢰합니다(사용자 인증서 없음 — 사용자의 MitM 인증서 설치 방지). api.example.com의 Domain-config에는 SHA-256 인증서 지문이 포함된 pin-set이 있습니다. 지정된 만료일 전에 서버 인증서가 변경되면 연결이 거부됩니다 — 이는 엄격한 형태의 Certificate Pinning입니다.
Certificate Pinning은 애플리케이션 코드에 서버의 인증서 또는 공개 키를 고정하는 기술입니다. 각 TLS 핸드셰이크 중에 클라이언트는 서버 인증서를 미리 저장된 지문(SHA-256 해시)과 비교합니다. 공격자가 신뢰할 수 있는 CA 인증서를 얻거나 인증 기관을 손상시키더라도 MitM 공격을 수행할 수 없습니다 — 애플리케이션이 CA 체인이 아닌 특정 지문을 확인하기 때문입니다. 이는 금융 애플리케이션 및 민감한 데이터를 처리하는 앱에 특히 중요합니다.
Certificate Pinning은 주의가 필요합니다: 서버 인증서가 변경되면 애플리케이션의 모든 이전 버전이 연결을 중단합니다. 여러 백업 지문(기본 + 백업)을 저장하고, pin-set 만료일을 지정하고, 표준 CA 확인을 통한 폴백 메커니즘을 구현하는 것이 좋습니다. 대안으로 Trust On First Use(TOFU)가 있습니다. 애플리케이션이 첫 연결 시 인증서를 기억하고 변경 시 사용자에게 경고합니다. OWASP(2026)에 따르면 Certificate Pinning의 부재는 모바일 애플리케이션의 상위 3대 취약점(M3: 안전하지 않은 통신) 중 하나입니다.
Alamofire 5+에서 Certificate Pinning은 ServerTrustManager와 PinnedCertificatesTrustEvaluator(전체 인증서 확인) 또는 PublicKeysTrustEvaluator(공개 키만)를 통해 구성됩니다. 공개 키가 더 좋습니다 — 동일한 CA로 인증서를 갱신할 때 변경되지 않기 때문입니다. [host: evaluator] 사전으로 ServerTrustManager를 만들고 Session에 전달한 후 보호된 API에 대한 모든 요청에 사용하세요.
자주 묻는 질문
SSL은 구식 프로토콜(버전 2.0 및 3.0)로 POODLE 및 BEAST 취약점으로 인해 안전하지 않은 것으로 간주됩니다. TLS는 그 후속 제품으로 TLS 1.0(RFC 2246, 1999)부터 시작됩니다. 현대의 “SSL 인증서”는 모두 TLS 프로토콜에서 사용하는 X.509 인증서입니다. SSL 3.0은 모든 최신 OS와 브라우저에서 금지됩니다.
App Transport Security는 Apple의 애플리케이션 보안 요구 사항입니다. HTTP는 데이터를 평문으로 전송하여 공용 Wi-Fi 네트워크에서 토큰 및 사용자의 개인 데이터를 가로챌 수 있습니다. ATS는 기본적으로 HTTP 및 TLS 1.2 미만의 HTTPS를 차단하여 개발자의 조치 없이도 사용자를 보호합니다.
SSL Labs(ssllabs.com/ssltest) 또는 명령줄을 사용하세요: openssl s_client -tls1_3 -connect example.com:443. 대부분의 클라우드 플랫폼(AWS CloudFront, Cloudflare, Nginx 1.19+, Caddy)에서 TLS 1.3은 기본적으로 활성화되어 있습니다. Android 10+에서는 시스템 Conscrypt 공급자에 지원이 내장되어 있습니다.
자체 서명 인증서는 인증 기관이 아닌 자체적으로 서명된 인증서입니다. 프로덕션에서 사용할 수 없습니다 — 모바일 OS가 이러한 인증서를 신뢰하지 않습니다. 로컬 개발에 사용됩니다: MDM을 통해 인증서를 신뢰할 수 있는 인증서에 추가하거나 확인을 비활성화한 디버그 빌드를 사용하세요.
ServerTrustManager를 PinnedCertificatesTrustEvaluator 또는 PublicKeysTrustEvaluator와 함께 만듭니다. 전자는 전체 인증서를 확인하고 후자는 공개 키만 확인합니다(권장). 관리자를 Session(configuration: serverTrustManager:)에 전달하고 모든 API 요청에 해당 세션을 사용하세요.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.