클라이언트 보안의 핵심은 앱을 신뢰 경계로 착각하지 않는 것이다. 브라우저 코드와 모바일 앱은 사용자가 분석하거나 변조할 수 있다. 최종 인증과 권한 판단은 항상 서버에서 수행해야 한다.
중요도: 🔴 필수 · 🟡 강력 권장 · 🔵 구조에 따라 적용
1. 인증 정보 저장
웹
- 🔴 장기 토큰을
localStorage에 저장하지 않는다. XSS가 발생하면 JavaScript로 읽힐 수 있다. - 🔴 가능하면 BFF가 세션을 관리하고 브라우저에는
Secure,HttpOnly,SameSite쿠키만 전달한다. - 🟡 SPA가 직접 Access Token을 사용한다면 수명을 짧게 하고 메모리에 보관한다. 새로고침과 다중 탭 동작까지 설계해야 한다.
- 🔴 로그아웃 시 로컬 상태만 지우지 말고 서버 세션이나 Refresh Token도 폐기한다.
- 🔵 쿠키 인증은 SameSite 정책과 함께 CSRF Token 또는 Origin 검증을 적용한다.
HttpOnly는 JavaScript의 쿠키 읽기를 막지만 요청 자체는 자동 전송된다. 따라서 XSS와 CSRF를 각각 별도로 방어해야 한다.
모바일 앱
- 🔴 iOS Keychain과 Android Keystore 기반 Secure Storage에 토큰을 저장한다.
- 🔴 비밀번호, Secret Key, 서버 관리자 자격증명을 앱에 포함하지 않는다.
- 🟡 로그아웃 시 Secure Storage와 서버 세션을 함께 폐기한다.
- 🟡 고위험 작업에는 생체 인증과 서버 재인증을 구분해 적용한다.
2. 인증과 권한
- 🔴 보호 화면 진입 여부를 확인하되 이것을 보안 경계로 간주하지 않는다.
- 🔴 모든 API에서 사용자, 조직, 역할, 대상 리소스의 권한을 서버가 다시 검사한다.
- 🔴 ID만 바꿔 다른 사용자의 데이터를 조회할 수 없는지 테스트한다.
- 🟡 관리자 UI는 분리하되 API 권한 검사도 동일하게 유지한다.
- 🟡 로그인·비밀번호 변경·결제 같은 이벤트를 감사 로그로 남긴다.
3. 입력과 파일
- 🔴 클라이언트 검증은 사용자 경험을 위한 것이고 서버 검증이 최종 기준이다.
- 🔴 문자열 길이, 숫자 범위, 날짜, Enum, 이메일 등 허용 규칙을 명시한다.
- 🔴 출력 위치에 맞는 escaping을 사용하고 SQL은 Parameter Binding으로 실행한다.
- 🔴 파일은 크기, 확장자, 선언 MIME, 실제 콘텐츠를 함께 확인한다.
- 🟡 업로드 파일을 임의 이름으로 저장하고 실행 권한을 제거하며 필요하면 악성코드 검사를 거친다.
4. API 통신과 토큰 갱신
- 🔴 운영 환경에서는 HTTPS만 사용한다.
- 🔴 인증이 필요한 요청에만 자격증명을 첨부하며 외부 URL로 전달하지 않는다.
- 🔴 토큰 갱신 요청이 동시에 몰리지 않도록 한 번의 갱신으로 대기 요청을 처리한다.
- 🔴 Refresh 실패 시 인증 상태를 정리하고 로그인 화면으로 이동한다.
- 🟡 네트워크 재시도에는 지수 백오프와 횟수 제한을 둔다.
401이 모두 Access Token 만료를 뜻하는 것은 아니다. 서버가 오류 코드나 명확한 인증 규약을 제공해야 무한 갱신 루프를 피할 수 있다.
5. 민감정보
- 🔴 공개 클라이언트 번들에 들어간 값은 환경 변수여도 비밀이 아니다.
- 🔴 비밀 API Key가 필요하면 서버가 대신 호출하도록 구성한다.
- 🔴 Analytics, Crash Report, Console 로그에 토큰과 개인정보를 남기지 않는다.
- 🟡 개발·스테이징·운영 프로젝트와 자격증명을 분리한다.
6. XSS, CSRF와 콘텐츠 보안
- 🔴 React의 기본 escaping을 유지하고
dangerouslySetInnerHTML사용을 최소화한다. - 🔴 사용자 HTML을 출력해야 한다면 검증된 Sanitizer와 엄격한 허용 목록을 사용한다.
- 🟡 nonce 또는 hash 기반 CSP를 단계적으로 적용한다.
- 🟡 클릭재킹 방지를 위해
frame-ancestors정책을 설정한다. - 🟡 쿠키 인증 요청은 CSRF 방어를 적용한다.
정적 Markdown처럼 저장소 관리자가 직접 작성한 신뢰 콘텐츠와 사용자가 입력한 HTML은 위험 수준이 다르다. 사용자 입력이 섞이는 순간 Sanitizing이 필요하다.
7. 모바일 앱 강화
- 🔴 Release 빌드에서 Debug 메뉴, 테스트 서버, 상세 네트워크 로그를 제거한다.
- 🔴 Android 백업과 화면 캡처 허용 여부, iOS 데이터 보호 등 플랫폼 설정을 확인한다.
- 🟡 Android는 R8, iOS는 심볼 관리 등으로 분석 비용을 높인다.
- 🟡 루팅·탈옥 탐지는 우회될 수 있으므로 보조 신호로만 사용한다.
- 🟡 인증서 고정은 운영 복잡성과 장애 위험을 검토한 뒤 고위험 앱에 선택적으로 적용한다.
난독화와 루팅 탐지는 서버 권한 검사를 대신하지 않는다.
8. 오류 처리
- 🔴 사용자에게 Stack Trace, 내부 URL, SQL, 파일 경로를 노출하지 않는다.
- 🔴 예상 가능한 오류와 네트워크 실패를 처리해 안전한 상태로 복귀시킨다.
- 🔴 오류 추적 서비스로 장애를 수집하되 개인정보를 마스킹한다.
- 🟡 운영 빌드에서는 불필요한
console.log를 제거하거나 로그 레벨을 제한한다.
9. 라이브러리와 공급망
- 🔴 Lockfile을 커밋하고 CI에서
npm audit,pnpm audit또는 동등한 검사를 수행한다. - 🔴 알려진 취약점은 영향 범위와 실제 노출 여부를 확인하고 신속히 업데이트한다.
- 🟡 사용하지 않는 패키지와 권한을 제거한다.
- 🟡 Dependabot이나 Renovate로 업데이트 PR을 자동화한다.
- 🟡 배포 산출물과 모바일 서명 키의 접근 권한을 제한한다.
출시 전 점검
- 인증 우회와 IDOR 테스트를 수행했다.
- 웹과 앱 어디에도 비밀 키가 포함되지 않았다.
- 로그와 오류 수집 데이터에서 개인정보를 제거했다.
- 토큰 만료·회전·로그아웃·동시 갱신을 테스트했다.
- 사용자 HTML과 파일 업로드 경로를 검증했다.
- Release 빌드 설정과 의존성 취약점을 확인했다.