Kotlin 미드레벨 보안 기술면접
새 면접Q. Spring Security를 사용하는 Kotlin 프로젝트에서 JWT 기반 인증을 구현할 때, Access Token과 Refresh Token을 분리해서 사용하는 이유와 각각의 적절한 만료 시간 설정 전략을 설명해주세요.
토큰 탈취 시나리오와 사용자 경험 사이의 트레이드오프를 고려해보세요.
Access Token과 Refresh Token을 분리하는 주요 이유는 보안과 사용자 경험의 균형입니다. Access Token은 짧은 만료 시간(15분~1시간)을 설정하여 토큰 탈취 시 피해를 최소화하고, Refresh Token은 긴 만료 시간(1주~2주)으로 설정하여 사용자가 자주 로그인하지 않아도 되게 합니다. Refresh Token은 HttpOnly 쿠키에 저장하고 서버 측 검증을 강화하며, Access Token은 메모리에만 보관하여 XSS 공격으로부터 보호합니다. Refresh Token Rotation 기법을 적용하면 재사용 공격을 탐지할 수 있으며, Redis 등에 Refresh Token을 저장하여 강제 로그아웃 기능도 구현할 수 있습니다.
- • Access Token은 짧은 만료 시간으로 탈취 위험 최소화
- • Refresh Token은 긴 만료 시간으로 사용자 편의성 제공
- • Refresh Token은 HttpOnly 쿠키에 저장하고 서버 측 검증 강화
- • Refresh Token Rotation으로 재사용 공격 방지
모바일 앱이나 SPA에서 사용자가 매번 로그인하지 않으면서도 보안을 유지해야 할 때 사용됩니다.
Refresh Token이 탈취되었을 때 이를 탐지하고 대응할 수 있는 방법은 무엇인가요?
Q. Kotlin Spring 애플리케이션에서 사용자 입력을 받아 데이터베이스 조회를 수행할 때 SQL Injection을 방지하기 위한 구체적인 방법들을 설명하고, JPA를 사용할 때도 SQL Injection이 발생할 수 있는 경우를 말씀해주세요.
Prepared Statement와 동적 쿼리 생성 방식의 차이를 생각해보세요.
SQL Injection 방지의 기본은 Prepared Statement를 사용하여 SQL 쿼리와 데이터를 분리하는 것입니다. JPA의 경우 기본적으로 Parameterized Query를 사용하므로 안전하지만, @Query 어노테이션에서 문자열 연결로 동적 쿼리를 만들거나 native query에서 사용자 입력을 직접 삽입하면 취약점이 발생합니다. JPQL에서도 createQuery 메서드에 문자열 연결을 사용하면 위험하며, 반드시 named parameter나 positional parameter를 사용해야 합니다. 추가로 입력값 검증, 화이트리스트 방식의 필터링, 최소 권한 원칙을 적용한 DB 계정 사용도 중요합니다.
- • Prepared Statement와 Parameterized Query 사용
- • JPA에서도 동적 쿼리 문자열 연결 시 취약점 발생 가능
- • Native Query 사용 시 특히 주의 필요
- • 입력값 검증과 화이트리스트 필터링 병행
검색 기능, 필터링, 정렬 등 사용자 입력을 기반으로 데이터베이스 쿼리를 수행하는 모든 기능에서 필수적입니다.
동적으로 정렬 기준이나 테이블명을 변경해야 하는 경우, 안전하게 구현하는 방법은 무엇인가요?
Q. 프로덕션 환경에서 특정 API 엔드포인트에 CSRF 공격으로 의심되는 요청이 들어오고 있습니다. Kotlin Spring 애플리케이션에서 CSRF 방어를 구현할 때, SPA(Single Page Application)와 일반 서버 사이드 렌더링 환경에서 각각 어떻게 다르게 접근해야 하는지 설명해주세요.
SPA는 stateless한 API 통신을 하며, 쿠키 기반 세션 방식과 다른 인증 방식을 사용합니다.
서버 사이드 렌더링 환경에서는 Spring Security의 기본 CSRF 토큰 방식을 사용하여 폼 제출 시마다 서버에서 생성한 토큰을 검증합니다. SPA 환경에서는 JWT를 로컬 스토리지에 저장하고 Authorization 헤더로 전송하는 경우 CSRF 공격에 안전하지만, 쿠키 기반 인증을 사용한다면 SameSite 쿠키 속성을 Strict나 Lax로 설정하고 Double Submit Cookie 패턴을 적용해야 합니다. SPA에서 CSRF 토큰을 사용할 경우 초기 로딩 시 토큰을 받아 모든 변경 요청에 커스텀 헤더로 포함시키는 방식을 사용합니다. 또한 중요한 작업에는 재인증을 요구하고, Referer 헤더 검증을 추가 방어층으로 구현하는 것이 좋습니다.
- • 서버 사이드 렌더링은 기본 CSRF 토큰 방식 사용
- • SPA에서 JWT는 Authorization 헤더 사용 시 CSRF 안전
- • 쿠키 기반 인증 시 SameSite 속성과 Double Submit Cookie 패턴 적용
- • 중요 작업에는 재인증 요구
사용자의 권한으로 수행되는 중요한 작업(결제, 정보 수정, 삭제 등)을 보호해야 할 때 필수적입니다.
SameSite=Strict와 Lax의 차이점과 각각 어떤 상황에서 사용해야 하는지 설명해주세요.
Q. Kotlin 애플리케이션에서 사용자 비밀번호를 저장할 때 BCrypt를 사용하는 이유와, 단순 SHA-256 해싱과의 차이점을 설명해주세요. 또한 BCrypt의 work factor(cost)를 어떻게 설정해야 하는지 설명해주세요.
레인보우 테이블 공격과 brute force 공격에 대한 방어 메커니즘을 생각해보세요.
BCrypt는 salt를 자동으로 생성하고 포함시켜 같은 비밀번호라도 다른 해시값을 생성하므로 레인보우 테이블 공격을 방어합니다. SHA-256은 빠른 해싱 알고리즘이라 brute force 공격에 취약하지만, BCrypt는 의도적으로 느린 key derivation function으로 설계되어 공격자의 시도 속도를 늦춥니다. Work factor는 해싱 라운드 수를 결정하며, 값이 1 증가할 때마다 계산 시간이 2배가 되므로 현재 하드웨어 성능을 고려하여 10~12 정도로 설정하는 것이 일반적입니다. 로그인 응답 시간이 100~300ms 정도 소요되도록 조정하며, 시간이 지나면서 하드웨어 성능이 향상되면 work factor를 점진적으로 증가시켜야 합니다.
- • BCrypt는 자동 salt 생성으로 레인보우 테이블 공격 방어
- • 의도적으로 느린 알고리즘으로 brute force 공격 방어
- • Work factor는 10~12 권장, 하드웨어 성능에 따라 조정
- • 시간이 지나면 work factor 증가 필요
사용자 인증 시스템에서 비밀번호를 안전하게 저장하고 데이터 유출 시에도 원본 비밀번호를 보호해야 할 때 사용됩니다.
기존 사용자들의 비밀번호 해시 알고리즘을 SHA-256에서 BCrypt로 마이그레이션하려면 어떻게 해야 하나요?
Q. Kotlin 애플리케이션에서 주민등록번호나 신용카드 번호 같은 민감한 개인정보를 데이터베이스에 저장해야 할 때, 암호화 방식 선택과 키 관리 전략을 어떻게 수립하시겠습니까?
암호화 키가 소스 코드나 설정 파일에 하드코딩되면 안 되는 이유를 생각해보세요.
민감한 개인정보는 AES-256 같은 대칭키 암호화를 사용하여 암호화하며, 단방향 해싱이 아닌 양방향 암호화를 사용해야 필요 시 복호화할 수 있습니다. 암호화 키는 절대 소스 코드나 application.yml에 하드코딩하지 않고, AWS KMS, HashiCorp Vault, Azure Key Vault 같은 전문 키 관리 서비스를 사용해야 합니다. 데이터 암호화 키(DEK)와 키 암호화 키(KEK)를 분리하는 envelope encryption 방식을 적용하여 키 로테이션을 용이하게 합니다. 또한 암호화된 데이터는 별도 컬럼이나 테이블로 분리하고, 접근 로그를 남기며, 애플리케이션 레벨에서도 최소 권한 원칙을 적용하여 필요한 경우에만 복호화합니다.
- • AES-256 같은 대칭키 암호화 사용
- • 키 관리 서비스(KMS, Vault) 활용으로 키 하드코딩 방지
- • Envelope Encryption으로 키 로테이션 용이
- • 최소 권한 원칙과 접근 로그 기록
금융, 의료, 전자상거래 등 개인정보보호법과 정보통신망법을 준수해야 하는 서비스에서 필수적입니다.
암호화 키를 주기적으로 교체(rotation)해야 할 때, 기존에 암호화된 데이터는 어떻게 처리해야 하나요?
Q. Kotlin Spring 애플리케이션에서 사용자가 입력한 HTML을 저장하고 다시 보여줘야 하는 게시판 기능을 구현할 때, XSS 공격을 방지하면서도 필요한 HTML 태그는 허용하는 방법을 설명해주세요. 또한 CSP(Content Security Policy) 헤더를 어떻게 설정해야 하는지도 포함해주세요.
모든 HTML을 차단하는 것과 화이트리스트 방식의 sanitization을 비교해보세요.
사용자 입력 HTML은 OWASP Java HTML Sanitizer나 JSoup 같은 검증된 라이브러리를 사용하여 화이트리스트 방식으로 sanitize해야 합니다. 허용할 태그(p, b, i, a 등)와 속성(href, class 등)을 명시적으로 지정하고, script, iframe, on* 이벤트 핸들러는 모두 제거합니다. 출력 시에도 Thymeleaf의 경우 th:utext 대신 th:text를 기본으로 사용하고, 신뢰할 수 있는 sanitize된 콘텐츠만 선택적으로 utext를 사용합니다. CSP 헤더는 script-src 'self'로 설정하여 inline script를 차단하고, nonce나 hash 기반 방식으로 필요한 스크립트만 허용하며, unsafe-inline과 unsafe-eval은 절대 사용하지 않습니다. 추가로 X-XSS-Protection, X-Content-Type-Options 헤더도 함께 설정합니다.
- • OWASP HTML Sanitizer로 화이트리스트 방식 sanitization
- • 허용 태그와 속성을 명시적으로 지정
- • CSP 헤더로 inline script 차단 및 nonce 기반 허용
- • 출력 시에도 기본적으로 escape 처리
사용자가 작성한 게시글, 댓글, 리뷰 등을 다른 사용자에게 보여주는 모든 커뮤니티 기능에서 필수적입니다.
Stored XSS와 Reflected XSS의 차이점과 각각의 방어 전략은 어떻게 다른가요?
Q. 마이크로서비스 아키텍처 환경에서 Kotlin으로 구현된 여러 서비스 간 인증/인가를 처리할 때, API Gateway에서의 인증 처리와 각 서비스에서의 인가 처리를 어떻게 설계하시겠습니까? 서비스 간 통신의 보안도 함께 고려해주세요.
중앙화된 인증과 분산된 인가, 그리고 서비스 간 신뢰 관계를 어떻게 구축할지 생각해보세요.
API Gateway에서 JWT 기반 인증을 수행하여 유효한 토큰인지 검증하고, 검증된 사용자 정보를 커스텀 헤더로 downstream 서비스에 전달합니다. 각 마이크로서비스는 전달받은 사용자 정보를 기반으로 자체적인 인가 로직을 수행하며, Spring Security의 Method Security를 활용하여 세밀한 권한 제어를 구현합니다. 서비스 간 통신은 mTLS(mutual TLS)를 사용하여 양방향 인증을 수행하거나, Service Mesh(Istio 등)를 도입하여 자동화된 보안 통신을 구현합니다. 서비스 간 호출 시에는 별도의 서비스 토큰을 사용하거나 JWT의 audience claim을 검증하여 토큰 재사용 공격을 방지하고, API Gateway를 우회한 직접 호출을 차단하기 위해 네트워크 레벨 격리도 함께 적용합니다.
- • API Gateway에서 중앙화된 인증 처리
- • 각 서비스는 자체 인가 로직 수행
- • 서비스 간 통신은 mTLS나 Service Mesh 사용
- • 서비스 토큰과 네트워크 격리로 우회 호출 방지
MSA 환경에서 여러 서비스가 협력하여 비즈니스 로직을 처리하면서도 보안을 유지해야 할 때 사용됩니다.
OAuth2의 Client Credentials Grant를 서비스 간 인증에 사용할 때의 장단점은 무엇인가요?
아직 댓글이 없습니다. 첫 번째 댓글을 남겨보세요!