Flutter Error : CERTIFICATE_VERIFY_FAILED: unable to get local issuer certificate

Flutter로 앱을 개발하던 중 외부 API와 통신하거나 로그인 기능을 구현할 때, 아래와 같은 에러 메시지를 마주친 경험이 있을 수 있습니다.

CERTIFICATE_VERIFY_FAILED: unable to get local issuer certificate

처음 마주했을 때는 왜 이런 문제가 발생했는지 감이 오지 않을 수 있지만, 이 에러는 Flutter뿐 아니라 Python, Node.js 등 다양한 언어 환경에서도 종종 발생하는 SSL 인증서 관련 공통 에러입니다.
이번 포스팅에서는 이 에러가 어떤 상황에서 발생하는지, 그리고 실무에서 사용할 수 있는 안전한 해결 방법들을 정리해보겠습니다.


1. 에러의 본질: SSL 인증서 검증 실패

해당 에러는 Dart 또는 Flutter의 HTTP 클라이언트가 HTTPS 서버로부터 받은 인증서를 검증하는 과정에서, 루트 인증 기관(CA, Certificate Authority)의 정보를 찾을 수 없을 때 발생합니다. 다시 말해, 서버와의 연결은 가능하지만 그 서버의 인증서가 신뢰할 수 있는 기관에서 발급된 것인지 확신할 수 없어 연결을 차단하는 것입니다.

주로 발생하는 상황은 다음과 같습니다:

  • 로컬 개발 서버 또는 사내망 서버가 **자체 서명된 인증서(self-signed certificate)**를 사용할 때
  • 테스트 또는 staging 서버가 유효하지 않거나 만료된 인증서를 사용할 때
  • Flutter 또는 Dart SDK가 시스템 인증서를 제대로 읽지 못할 때
  • 윈도우나 맥의 신뢰된 루트 인증서 저장소에 인증서가 등록되지 않았을 때

2. 개발 중이라면, 임시로 인증서 검증을 우회할 수 있다

가장 간단한 방법은 인증서를 강제로 무시하고 HTTP 요청을 보내는 것입니다. 이는 보안상 매우 위험하기 때문에, 반드시 로컬 개발 환경에서만 사용해야 합니다.

import 'dart:io';
class MyHttpOverrides extends HttpOverrides{
@override
HttpClient createHttpClient(SecurityContext? context){
return super.createHttpClient(context)
..badCertificateCallback = (X509Certificate cert, String host, int port)=> true;
}
}


void main() {
HttpOverrides.global = MyHttpOverrides();
runApp(const MyApp());
}


class MyApp extends StatelessWidget {
const MyApp({super.key});

@override
Widget build(BuildContext context) {
......

badCertificateCallback을 통해 모든 인증서를 신뢰하도록 설정하는 코드입니다. 테스트 환경에서는 유용하지만, 운영 환경에서 사용 시 보안 취약점이 발생할 수 있으니 절대 사용하지 마세요.


3. 운영 환경에서는 루트 인증서 문제를 근본적으로 해결해야 한다

Flutter는 내부적으로 Dart를 기반으로 하며, Dart는 SSL 통신 시 시스템의 루트 인증서 저장소를 참조합니다. 하지만 간혹 루트 인증서를 제대로 인식하지 못하는 경우가 있습니다. 이 경우 다음과 같은 방식으로 해결할 수 있습니다.

📍 macOS에서 루트 인증서 등록

  1. 터미널을 열고 다음 명령어를 실행합니다:
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain your_cert.pem
  1. your_cert.pem은 등록하려는 인증서 파일 경로입니다.

📍 Windows에서 루트 인증서 등록

  1. .crt 또는 .pem 인증서 파일을 더블 클릭
  2. “인증서 설치” 클릭
  3. “로컬 컴퓨터” 선택
  4. “신뢰할 수 있는 루트 인증 기관” 저장소에 설치

설치 후에는 시스템을 재시작하거나 Flutter 프로젝트를 재실행하는 것이 좋습니다.


4. Dart 환경에서 직접 루트 인증서 번들 지정하기

Dart SDK는 자체적으로 인증서 번들을 사용하지 않으며, OS 인증서를 참조합니다. 하지만 필요 시 인증서 번들을 직접 지정할 수도 있습니다.

export DART_VM_OPTIONS="--root-certs-file=/path/to/cacert.pem"

Flutter 실행 시 dart-define을 사용해 전달할 수도 있습니다:

flutter run --dart-define=DART_VM_OPTIONS=--root-certs-file=/path/to/cacert.pem

5. 자체 서명된 인증서를 사용하는 사내 서버 대응법

사내 백엔드 서버에서 자체 서명 인증서를 사용하고 있다면, 아래 중 하나를 선택해야 합니다:

  • 정식 CA 인증서로 교체 (가장 권장되는 방법)
  • 클라이언트 단에서 인증서 무시 설정 (개발 환경 한정)
  • 서버의 공개 키를 앱 내부에 내장하고, 직접 비교(SSL pinning)

6. 인증서 문제를 방치하면 어떤 문제가 생길까?

앱에서 인증서를 제대로 검증하지 않거나, 모든 인증서를 신뢰하도록 설정할 경우 다음과 같은 위험이 따릅니다:

  • 중간자 공격(MITM, Man-In-The-Middle) 가능성 증가
  • 사용자 개인정보 노출
  • 앱스토어 심사 거절 (특히 Apple의 경우 ATS 정책 위반 시 리젝)

따라서 개발 중에는 우회할 수 있지만, 운영 환경에서는 반드시 올바른 인증서를 사용해야 하며 인증서 검증 로직도 철저히 적용해야 합니다.


마무리하며

CERTIFICATE_VERIFY_FAILED 에러는 인증서 문제의 전형적인 사례입니다. Flutter에서 이 에러가 발생했을 때는 당황하지 말고, 아래 순서를 따라가며 해결을 시도해보세요:

  1. 로컬 개발이라면 badCertificateCallback을 이용한 임시 우회
  2. 운영 서버라면 CA 인증서를 확인하고 루트 인증 등록
  3. 필요시 인증서 번들을 직접 지정
  4. 궁극적으로는 인증서 체인을 신뢰할 수 있는 구조로 설정

Flutter는 점점 더 보안에 민감한 앱이 많아지고 있는 만큼, 인증 관련 설정도 확실히 이해하고 다뤄야 할 부분입니다.

댓글 남기기