API URL을 NDK로 숨겨 안드로이드 앱 보안 강화
목차
보안 강화하면서 NDK까지 끌어들인 날
앱 보안 점검하다가 결국 빌드 설정 전체를 갈아엎었음. 처음엔 ProGuard 규칙만 다듬으려고 했는데, API URL이 평문으로 코드에 박혀있는 걸 보고 결심함. 디컴파일 한 번이면 다 노출되는 구조였음.
API URL을 NDK로 옮긴 이유
문자열 상수로 두면 APK 디컴파일 도구 하나면 추출됨. 그래서 C++ 레이어로 내려보냄.
extern "C" JNIEXPORT jstring JNICALL
Java_app_security_ApiConfig_getEndpoint(JNIEnv* env, jobject) {
const char* parts[] = { "https://", "api.", "example", ".com" };
// 런타임 조립 후 반환
}
문자열 그대로 박지 않고 분할해서 런타임에 합치도록 했음. 정적 분석에서 한 번에 안 잡히게 함. 완벽한 방어는 아니지만 진입장벽은 확실히 올라감.
| 위치 | Before | After |
|---|---|---|
| API URL | Kotlin 상수 | NDK + 분할 조립 |
| 빌드 설정 | minifyEnabled false | release만 true |
| 네트워크 | cleartext 허용 | network_security_config |
| 키스토어 | gradle.properties | 환경변수 |
ProGuard 규칙 정리하면서 만난 함정
릴리즈 빌드 켰더니 앱이 즉사함. 직렬화 클래스가 난독화되면서 JSON 파싱이 다 깨짐. 결국 아래처럼 케이스별로 keep 규칙 추가.
- 모델 클래스:
-keep class app.domain.model.** { *; } - 리플렉션 쓰는 라이브러리:
-keepattributes Signature, *Annotation* - 콜백 인터페이스:
-keepclassmembers로 메서드만 보존 - Kotlin Coroutines: 공식 가이드 규칙 그대로 복붙
-printusage, -printseams 옵션 켜고 빌드 결과물 확인하면서 한 줄씩 검증함. 이거 안 하고 무작정 keep만 늘리면 난독화 의미 없어짐.
network_security_config 적용
매니페스트에 usesCleartextTraffic="false" 박고 별도 config XML 만듦. 개발 빌드만 로컬 IP 허용하도록 분기.
<domain-config cleartextTrafficPermitted="false">
<domain includeSubdomains="true">api.example.com</domain>
<pin-set expiration="2027-01-01">
<pin digest="SHA-256">...</pin>
</pin-set>
</domain-config>
여기서 핀 만료일 까먹으면 어느 날 갑자기 통신 다 끊김. 캘린더에 미리 알림 박아둠.
배운 것
- 보안은 한 곳만 막으면 의미 없음. 평문 URL, 키스토어, 네트워크 정책 전부 같이 봐야 함
- ProGuard는 "켜면 끝"이 아니라 "켜고 검증해야 끝". 첫 릴리즈 크래시는 거의 다 난독화 때문
- NDK 도입은 빌드 시간 늘어나는 비용 있음. abiFilters로 필요한 아키텍처만 남기는 게 필수
다음
댓글 0
첫 댓글 달아줘.