주절주절.ZIP

Android와 iOS의 서로 다른 SSL 인증서 체인 검증 방식.ZIP (feat. React Native FETCH_ERROR)

NURGET 2024. 11. 6. 16:41

문제 상황

최근 스프링부트 유지보수를 부탁받아 진행하고 있는 프로젝트에서 특이한 이슈를 만났습니다. 개발 서버에서는 로그인이 잘 되는데, 운영 서버에서는 간헐적으로 로그인이 안 되는 문제였는데요. 더 특이한 점은 iOS에서는 잘 되는데 Android에서만 문제가 발생한다는 점이었습니다.

 

앱 개발자 분이 보내주신 오류 메시지

{ "status": "FETCH_ERROR", "error": "TypeError: Network request failed" }

 

 

처음에는 당연히 클라이언트 쪽 문제일 거라고 생각했습니다. 하지만 앱 개발자분이 "개발 서버와 운영 서버는 엔드포인트만 다르고 호출 방식은 동일하다"고 하심 .. ㅎㅎ

🔍 원인 분석

백엔드 코드를 살펴보니 커스텀 에러 코드를 사용하고 있기 때문에

  • MsAP1xx: Business Error
  • MsAP4xx: Authentication Error
  • MsAP5xx: Server Error

 FETCH_ERROR는 RN도 스프링부트도 정의하지 않은 에러 코드였습니다. 이 부분에서 힌트를 얻었는데용 ㅎㅎ

💡 SSL 인증서 체인 확인

운영 서버의 SSL 인증서를 확인해보니:

verify error:num=20:unable to get local issuer certificate 
verify error:num=21:unable to verify the first certificate

 

중간 인증서가 누락되어 있었습니다.

 

반면 개발 서버는:

depth=2 C = US, O = Internet Security Research Group, CN = ISRG Root X1
depth=1 C = US, O = Let's Encrypt, CN = R10
depth=0 CN = dev.e-paps.kr

 

전체 인증서 체인이 정상적으로 구성되어 있었구요.

🤔 왜 iOS에서는 됐을까?

출처: 나

 

여기서 재미있는 부분을 발견했습니다. iOS와 Android의 SSL 인증서 체인 검증 방식이 다르더라구요:

  • iOS: 비교적 유연한 SSL 체인 검증
  • Android: 매우 엄격한 SSL 체인 검증 (중간 인증서 필수)

🛠 해결 방법

Sectigo 중간 인증서를 추가하여 전체 인증서 체인을 완성했습니다:

  1. 도메인 인증서와 중간 인증서를 하나의 파일로 결합
    - 중간 인증서 생성
sudo bash -c 'cat > intermediate.crt << "EOF"
-----BEGIN CERTIFICATE-----
MIIGEzCCA/ugAwIBAgIQfVtRJrR2uhHbdBYLvFMNpzANBgkqhkiG9w0BAQwFADCB
iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl
oUmGv38
sbZXQm2V0TP2ORQGgkE49Y9Y3IBbpNV9lXj9p5v//cWoaasm56ekBYdbqbe4oyAL
l6lFhd2zi+WJN44pDfwGF/Y4QA5C5BIG+3vzxhFoYt/jmPQT2BVPi7Fp2RBgvGQq
6jG35LWjOhSbJuMLe/0CjraZwTiXWTb2qHSihrZe68Zk6s+go/lunrotEbaGmAhY
LcmsJWTyXnW0OMGuf1pGg+pRyrbxmRE1a6Vqe8YAsOf4vmSyrcjC8azjUeqkk+B5
yOGBQMkKW+ESPMFgKuOXwIlCypTPRpgSabuY0MLTDXJLR27lk8QyKGOHQ+SwMj4K
00u/I5sUKUErmgQfky3xxzlIPK1aEn8=
-----END CERTIFICATE-----
EOF'

 

# 병합
sudo bash -c 'cat e-ensemble_kr.crt intermediate.crt > fullchain.crt'

 

확인했을 때

아래처럼 ----BEGIN CERTIFICATE ----- 가 두 번 나와야 정상.

# 확인
sudo cat fullchain.crt | grep "BEGIN CERTIFICATE"
-----BEGIN CERTIFICATE-----
-----BEGIN CERTIFICATE-----

 

그리고 본인의 Nginx 설정 파일에

/etc/nginx/sites-available << 이런 곳에


요 설정을 추가해주면 된다.

ssl_certificate /etc/nginx/e-ensemble.kr-ssl/fullchain.crt;

 

Nginx 파일 수정했으니 -t 해주고 restart 해주고 아시죠?

🎉 결과

  • Android에서도 정상적으로 로그인 동작
  • SSL 체인 검증 오류 해결
  • FETCH_ERROR 해소

📝 배운 점

  1. FETCH_ERROR가 발생하면 SSL 인증서 체인도 의심해봐야 함
  2. Android와 iOS의 보안 정책 차이를 이해하는 것이 중요
  3. 개발/운영 서버의 설정 차이를 꼼꼼히 비교해봐야 함

이런 경험을 통해 플랫폼별 특성과 보안 정책의 중요성을 다시 한번 실감했습니다.....

특히 SSL 인증서 체인이 제대로 구성되어 있지 않으면 Android에서 문제가 발생할 수 있다는 점을 기억해두면 좋을 것 같습니다!