결제 키를 네이티브 영역으로 옮겨 APK 노출 차단
목차
왜 NDK까지 끌고 갔나
전엔 빌드 상수에 API Key 박아놨는데, APK 뜯어보면 그대로 노출됨. 디컴파일러 한 번이면 끝나는 구조라 v3.1에서 손봤음.
- 정적 문자열 리소스 → 키 추출 30초 컷
- 빌드 상수도 마찬가지, 난독화 무관하게 readable
- 결제 플랫폼 호출 키라 그냥 두기 부담스러웠음
native 쪽으로 옮긴 방식
JNI 통해서 C++ 함수가 키를 돌려주게 함. .so 파일은 디컴파일 난이도가 다른 차원이고, 거기에 XOR 마스킹까지 얹어서 strings 명령으로도 바로 못 뽑게 했음.
// 런타임에 마스킹 풀어서 반환
JNIEXPORT jstring JNICALL
nativeGetKey(JNIEnv* env, jclass) {
unsigned char buf[KEY_LEN];
for (int i = 0; i < KEY_LEN; ++i)
buf[i] = ENC[i] ^ MASK[i % MASK_LEN];
return env->NewStringUTF((const char*)buf);
}
CMakeLists.txt에 타겟 추가하고, gradle 쪽 externalNativeBuild 블록만 연결하면 끝남. 생각보다 붙이는 건 어렵지 않았음.
ProGuard로 바깥쪽 정리
- JNI 진입 함수명은
-keep으로 보존. 안 그러면 native에서 못 찾고 죽음 - 그 외 클래스/메서드명 전부 난독화
- 디버그 빌드는 풀어두고 릴리즈만 적용
무결성 검증 같이 넣음
이커머스 앱 재서명해서 광고 박아 재배포하는 사례 본 적 있어서, 서명 해시 화이트리스트 비교 로직을 native 쪽에 같이 넣었음. 위변조면 키 반환 자체를 안 함.
| 항목 | 이전 | 이후 |
|---|---|---|
| 키 위치 | 빌드 상수 | native .so + XOR |
| 난독화 | 없음 | 릴리즈만 적용 |
| 재서명 탐지 | 없음 | 서명 해시 검증 |
| 빌드 시간 | 30초 | 1분 10초 |
한계는 솔직히 인정
- 메모리 덤프로 native에서 키 뽑는 시도는 못 막음
- frida 같은 후킹 툴 앞에선 한 단계 늦추는 정도
- 진짜 중요한 결제대행사 키는 서버 경유 단기 토큰으로 가는 게 정답
이번 작업은 "캐주얼한 공격자 한 명 더 거르기" 가 목표였고 그 정도는 됐다고 봄. 빌드 느려진 건 ccache 붙여서 회복했고, 로컬은 그냥 감수하는 중. 결제 진입점 쪽 키도 동일 방식으로 옮길 예정.
다음
댓글 0
첫 댓글 달아줘.