13 KiB
13 KiB
KINTEX AI 모바일 앱 + 백엔드 보안 감사 결과
감사일: 2026-07-12 · 감사자: 통합 QA(reviewer) · 방식: 정적 소스 감사(코드 미수정, 읽기전용) 체크리스트:
mobile-security-checklist.md· 개선 백로그: 본 문서 §하단 증거는파일:라인표기.mobile/는 다른 에이전트 편집 중이라 스냅샷 기준.
종합 판정
| 영역 | PASS | FAIL | N-A | 총평 |
|---|---|---|---|---|
| 1. 위변조 방지 | 1 | 5 | 0 | 최대 약점 — 루트/디버거/무결성 탐지·난독화 선언 부재 |
| 2. 데이터 보호 | 5 | 2 | 0 | SecureStore·로그 청결 우수 / FLAG_SECURE·백그라운드 마스킹 부재 |
| 3. 통신 보안 | 2 | 2 | 0 | HTTPS 강제 / cleartext 차단 선언·피닝 부재 |
| 4. 인증·세션 | 4 | 1 | 0 | 2FA·잠금·RBAC 견고 / 생체 로컬잠금 부재 |
| 5. 시큐어 코딩 | 7 | 0 | 0 | 모범 — SQLi 가드·CSPRNG·스택트레이스 차단 완비 |
| 6. 빌드·배포 | 4 | 2 | 0 | env 시크릿·오류응답 청결 / 권한 최소화·CORS 명시 보강 |
전반: 백엔드·시큐어코딩은 공공 기준 대비 견고. 모바일 클라이언트 측 위변조 방지(anti-tampering)와 화면보호가 최대 갭 — 공공 대민 앱 기준(KISA 위변조 항목군)에서 필수 보강 대상.
1. 앱 위변조 방지
| ID | 판정 | 위험도 | 증거 / 근거 |
|---|---|---|---|
| AT-1 루트/탈옥 탐지 | FAIL | High | package.json 의존성에 jail-monkey·freerasp·root 탐지 라이브러리 없음(mobile/package.json 13~33). 코드 내 탐지 로직 0건. |
| AT-2 서명/무결성 | FAIL | High | Play Integrity/App Attest·서명 지문 검증 코드 없음. 리패키징 탐지 부재. |
| AT-3 코드 난독화 | FAIL | Med | app.json에 jsEngine: "hermes" 미선언(mobile/app.json 2~53) — RN 0.74 기본 Hermes지만 명시·R8 설정 없음. expo-build-properties(R8/ProGuard) 미설치. |
| AT-4 디버거/에뮬레이터 | FAIL | Med | Frida/디버거/에뮬레이터 탐지 없음. |
| AT-5 디버그 비활성 | 부분 PASS | Low | eas.json production=app-bundle(비디버그 기본, eas.json 18~22). 단 android:debuggable=false 명시·__DEV__ 스트립 확인 필요. |
| AT-6 개발자모드/USB | FAIL | Med | 개발자옵션·USB 디버깅 감지 없음. |
2. 데이터 보호
| ID | 판정 | 위험도 | 증거 / 근거 |
|---|---|---|---|
| DP-1 토큰 안전저장 | PASS | — | JWT를 expo-secure-store(Keychain/Keystore)에 저장(mobile/lib/secureStore.ts 10~25, mobile/context/AuthContext.tsx 62~66). web만 AsyncStorage 폴백(비네이티브). |
| DP-2 하드코딩 비밀 0 | PASS | — | 시크릿/키/토큰 grep 0건(mobile 전체). config.ts는 base URL만(공개, mobile/lib/config.ts 10~11). |
| DP-3 비밀번호 미저장 | PASS | — | 비밀번호는 요청 body로만 전송, 저장 로직 없음(mobile/lib/auth.ts 26~33, login.tsx 주석 4). |
| DP-4 화면캡처/오버레이 | FAIL | Med | 2FA/티켓QR 등 민감화면에 FLAG_SECURE 미적용(grep 0건). expo-screen-capture 미설치. |
| DP-5 백그라운드 스냅샷 | FAIL | Med | 앱 전환 시 민감화면 마스킹 로직 없음. |
| DP-6 클립보드 보호 | PASS(경미) | Low | 클립보드 강제복사 코드 없음. 티켓/개인정보 필드 클립보드 제어는 별도 검토 권장. |
| DP-7 로그 민감정보 0 | PASS | — | console.log/warn/error grep 0건(mobile 전체). 토큰·비밀번호 로깅 없음. |
3. 통신 보안
| ID | 판정 | 위험도 | 증거 / 근거 |
|---|---|---|---|
| NW-1 TLS 강제 | 부분 PASS | Med | base URL https://kintex.zioinfo.co.kr(mobile/app.json 45, config.ts 11). cleartext http:// grep 0건. 단 Android usesCleartextTraffic=false·networkSecurityConfig 미선언(기본 SDK34 false지만 명시 권장). |
| NW-2 인증서 검증 | PASS | — | 표준 fetch(트러스트스토어 검증), 검증 우회 코드 없음(mobile/lib/api.ts 56~61). |
| NW-3 인증서 피닝 | FAIL | Med | 인증서/공개키 피닝 미적용. 공공 대민 앱 권고사항. |
| NW-4 API 키 미노출 | PASS | — | 통신에 클라이언트 API 키 없음. Authorization은 런타임 발급 JWT만(mobile/lib/api.ts 52). |
4. 인증·세션
| ID | 판정 | 위험도 | 증거 / 근거 |
|---|---|---|---|
| AU-1 2FA(TOTP) | PASS | — | OTP_REQUIRED→재제출 흐름(mobile/lib/auth.ts 26~38, login.tsx 62~84). 서버 TotpService(HS 라이브러리 codeVerifier, backend security/TotpService.java 46~57). |
| AU-2 생체·로컬잠금 | FAIL | Med | expo-local-authentication 미설치, 로컬 재잠금 없음(진행 중 내정보 트랙과 연계). |
| AU-3 세션 만료·재발급 | PASS | — | 401 시 메모리 토큰 정리·재로그인 유도(mobile/lib/api.ts 67~70). 서버 TTL env(backend application.yml 67, JwtService 만료 66). |
| AU-4 로그인 실패 잠금 | PASS | — | 임계 초과 시 locked_until 설정, 계정열거 방지 일반화 메시지(backend security/LoginAttemptService.java 34~57). 클라 ACCOUNT_LOCKED 처리(login.tsx 74~75). |
| AU-5 서버측 RBAC | PASS | — | 행사 단위 RBAC를 JWT roles 클레임+컨트롤러 가드로 서버 강제(backend JwtService.java 47~69, SecurityConfig.java 48). |
5. 시큐어 코딩
| ID | 판정 | 위험도 | 증거 / 근거 |
|---|---|---|---|
| SC-1 입력검증 | PASS | — | 클라 검증(login.tsx 53~60, register.tsx 27~30) + 서버 @Valid(GlobalExceptionHandler MethodArgumentNotValid 39~47). |
| SC-2 SQLi 방지 | PASS | — | MyBatis 동적 ${}는 NlQueryReadMapper.xml ${sql} 단 1곳, 결정론적 화이트리스트 가드 통과분만 실행(backend NlQuerySqlGuard.java: 단일SELECT·테이블 화이트리스트·주석/다중문/PII/LIMIT 검증 56~130). 그 외 전부 #{} 바인딩. |
| SC-3 안전한 난수 | PASS | — | OTP IV SecureRandom(backend SecretCipher.java 31,44), OTP secret은 totp SecretGenerator(CSPRNG), 웹훅 HmacSHA256. |
| SC-4 웹뷰 안전설정 | N-A→PASS | — | WebView 미사용(mobile grep 0건). 공격면 없음. |
| SC-5 딥링크 검증 | 부분 PASS | Low | scheme kintex(app.json 6). expo-router 라우팅. 딥링크 파라미터 명시 검증 로직 추가 권장(현재 인증가드로 보호). |
| SC-6 스택트레이스 미노출 | PASS | — | 서버 include-stacktrace/message: never(application.yml 13~14), GlobalExceptionHandler 요약만 반환·스택 미저장(51~62), 401 봉투(SecurityConfig 49~57). 클라 요약 메시지만 노출(api.ts 82~89). |
| SC-7 암호화 저장 | PASS | — | OTP secret AES-256-GCM 봉인(backend SecretCipher.java seal/open 39~77, enc:v1: 포맷). |
6. 빌드·배포
| ID | 판정 | 위험도 | 증거 / 근거 |
|---|---|---|---|
| BD-1 릴리스 로그/소스맵 | PASS(경미) | Low | console 로그 0건. 소스맵 배포 제외·__DEV__ 스트립은 빌드시 확인 권장. |
| BD-2 최소 권한 | 부분 PASS | Med | app.json에 명시 권한 선언 없음(기본 Expo 권한만). expo-image-picker 사용(package.json 19) → 카메라/사진 권한 발생. blockedPermissions로 불필요 권한 제거 권장. |
| BD-3 서드파티 취약점 | 미검증 | Med | npm audit 미실행(감사 범위상 코드 실행 배제). 의존성 버전은 SDK51 정렬 최신(package.json). CI에 audit 게이트 권장. |
| BD-4 서버 오류응답 | PASS | — | IP/SSH/비밀번호/스택트레이스 응답 미노출(GlobalExceptionHandler, ServerOut 스키마 규칙 준수). error_log 요약 1줄만(51~59). |
| BD-5 CORS/헤더 | 부분 PASS | Med | .cors(cors -> {})에 CorsConfigurationSource 빈 미정의(backend SecurityConfig.java 35) → 명시 허용출처 없음(와일드카드 *도 없음). 운영 도메인 명시 권장. |
| BD-6 시크릿 주입 | PASS(주의) | Med | JWT_SECRET·KINTEX_OTP_ENC_KEY·DB/SMTP/ADMIN 비번 전부 env 주입(application.yml 27,43,66,76,83). 주의: JWT_SECRET 개발 폴백 CHANGE_ME_DEV_ONLY...(66), OTP 키 미주입 시 개발 파생키 경고(SecretCipher 88) → 운영 env 주입 필수. GEMINI_API_KEY는 워커 전용, 백엔드 미사용(9). |
추가 관찰(비차단, 운영 하드닝)
- 로그 레벨:
logging.level.com.zioinfo.kintex: DEBUG(application.yml 106) — 운영 프로파일에서 INFO로 하향 권장(민감 파라미터 verbose 노출 위험). - JWT 저장 위치: 웹 타깃만 AsyncStorage 폴백(secureStore.ts 10) — 관람객 B2C 웹뷰/PWA 배포 시 XSS 토큰 탈취면 존재. 네이티브 앱은 무관.
- 웹훅 인증:
/api/webhooks/in/**permitAll이나 토큰+HMAC-SHA256 이중검증·상수시간 비교(WebhookSignature.javaMessageDigest.isEqual58) — 양호.
개선 백로그 (FAIL/취약 → 구현 작업 전환)
담당: 위변조·SecureStore·화면보호·network config = kintex-mobile-dev / 백엔드 = kintex-backend-dev / EAS·빌드설정 = kintex-devops-dev 코드 편집은 각 담당 에이전트가 수행(본 QA는 문서·감사만). SDK51 호환 주의 병기.
P0 (High · 공공 대민 필수)
| # | 항목 | 담당 | 권장 구현 | SDK51 주의 |
|---|---|---|---|---|
| B1 | AT-1 루트/탈옥 탐지 | mobile-dev | jail-monkey(isJailBroken/canMockLocation) 또는 freerasp-react-native 도입 → 루팅 단말 경고·민감기능 제한. config plugin으로 prebuild 반영 |
jail-monkey는 config plugin 필요(bare/prebuild). Expo 관리형은 expo prebuild 또는 dev-client 필요 — devops와 협의 |
| B2 | AT-2 앱 무결성 검증 | mobile-dev + backend-dev | Android Play Integrity API·iOS App Attest 토큰을 로그인/민감 API에 첨부 → backend에서 검증. 서명 지문 확인 | Expo 관리형은 네이티브 모듈 → dev-client/prebuild. 서버 검증 엔드포인트 신설 |
| B3 | AT-3 난독화 선언 | devops-dev | app.json android.jsEngine:"hermes"(또는 최상위) 명시 + expo-build-properties로 android.enableProguardInReleaseBuilds:true·enableShrinkResources:true |
expo-build-properties 플러그인 추가·prebuild 필요. Hermes는 RN0.74 기본이나 명시로 회귀 방지 |
| B4 | DP-4 화면캡처 방지 | mobile-dev | expo-screen-capture preventScreenCaptureAsync()를 2FA·티켓QR·개인정보 화면 focus 시 적용 |
expo-screen-capture SDK51 호환. iOS는 캡처 감지만·Android는 FLAG_SECURE 차단 |
P1 (Med)
| # | 항목 | 담당 | 권장 구현 | SDK51 주의 |
|---|---|---|---|---|
| B5 | NW-1 cleartext 차단 명시 | devops-dev | expo-build-properties로 android.usesCleartextTraffic:false 명시 + iOS ATS 유지 확인 |
prebuild 반영 |
| B6 | NW-3 인증서 피닝 | mobile-dev + devops-dev | react-native-ssl-pinning 또는 build-properties networkSecurityConfig pin-set(SPKI 해시). 백엔드 인증서 갱신 로테이션 절차 병행 |
피닝 만료/로테이션 운영 리스크 — 백업 핀 2개 이상 |
| B7 | DP-5 백그라운드 마스킹 | mobile-dev | AppState 'inactive/background' 시 오버레이 블러/스플래시 표시(민감 화면) | RN AppState + 조건부 렌더 |
| B8 | AU-2 생체 로컬잠금 | mobile-dev | expo-local-authentication으로 앱 재진입·민감작업 시 생체 재인증(내정보 트랙과 통합) |
SDK51 호환. 폴백(PIN) 설계 |
| B9 | AT-4/AT-6 디버거·개발자모드 탐지 | mobile-dev | freerasp의 debugger/emulator/hooking 탐지 콜백 활용(B1과 통합) | B1과 동일 도입 경로 |
| B10 | BD-2 권한 최소화 | devops-dev | app.json android.permissions 명시 + blockedPermissions로 불필요 권한 제거. image-picker 권한 문자열(카메라/사진) 최소화 |
image-picker는 권한 필요 — 사용처만 요청 |
| B11 | BD-5 CORS 명시 | backend-dev | CorsConfigurationSource 빈으로 운영 도메인(kintex.zioinfo.co.kr·kintex.wise.ai.kr)만 allowedOrigins 명시, 와일드카드 금지 |
Spring Security 6 lambda DSL |
| B12 | BD-6 운영 시크릿 강제 | devops-dev + backend-dev | 운영 배포 시 JWT_SECRET·KINTEX_OTP_ENC_KEY env 주입 검증(부재 시 기동 실패 fail-fast 검토), DEBUG 로그 INFO 하향 | 개발 폴백 값 운영 유입 차단 |
| B13 | BD-3 의존성 CVE | devops-dev | CI에 npm audit --audit-level=high(mobile) + gradle 의존성 스캔 게이트 추가 |
정기 스캔 |
P2 (Low · 하드닝)
| # | 항목 | 담당 | 권장 구현 |
|---|---|---|---|
| B14 | AT-5 debuggable 검증 | devops-dev | 릴리스 빌드 android:debuggable=false·소스맵 미배포·__DEV__ 스트립 CI 검증 |
| B15 | SC-5 딥링크 검증 | mobile-dev | kintex:// 딥링크 진입 파라미터 스키마 검증·화이트리스트 라우팅 |
| B16 | DP-6 클립보드 정책 | mobile-dev | 티켓QR·개인정보 필드 contextMenuHidden·민감 복사 자동클리어 |
재검증 게이트(구현 후)
mobile/: jail-monkey/freerasp 도입·screen-capture 적용·build-properties(hermes/R8/cleartext) 반영 → 본 문서 AT-*/DP-4/NW-1 재판정.backend: CorsConfigurationSource 빈·운영 로그레벨·시크릿 fail-fast → BD-5/6 재판정.- 빌드 회귀:
mobile tsc --noEmitEXIT 0,./gradlew build통과 확인.