웹 보안 심화 완벽 가이드: Security Headers, RBAC vs ACL, 보안 이벤트 로깅까지

웹 보안 심화 완벽 가이드: Security Headers, RBAC vs ACL, 보안 이벤트 로깅까지

“CSP 헤더가 뭔가요?”, “RBAC과 ACL의 차이는?”, “보안 로그는 어떻게 관리하나요?” — 기본적인 인증/인가를 넘어, 보안 헤더·권한 모델·감사 로깅까지 이해해야 실무 수준의 보안 설계가 가능하다.


1. Security Headers

1.1 왜 필요한가

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
┌─────────────────────────────────────────────────────────────────────┐
│                 Security Headers의 필요성                             │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  HTTP 응답 헤더에 보안 정책을 설정하여                              │
│  브라우저 단에서 공격을 사전 차단한다.                              │
│                                                                     │
│  서버 로직만으로는 막기 어려운 공격:                                │
│  • XSS (Cross-Site Scripting) → 악성 스크립트 주입                 │
│  • Clickjacking → iframe으로 사용자 클릭 유도                      │
│  • MIME Sniffing → 파일 타입 위장 실행                             │
│  • Man-in-the-Middle → HTTP 도청                                   │
│                                                                     │
│  → 이 공격들은 브라우저가 응답을 어떻게 처리하느냐에 달려있음       │
│  → Security Headers로 브라우저의 동작을 제한                        │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

1.2 주요 Security Headers

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
┌─────────────────────────────────────────────────────────────────────┐
│                    주요 Security Headers                             │
├──────────────────────────────┬──────────────────────────────────────┤
│ 헤더                         │ 역할                                │
├──────────────────────────────┼──────────────────────────────────────┤
│ Content-Security-Policy      │ 허용된 리소스 출처를 지정            │
│ (CSP)                        │ → XSS 공격의 핵심 방어              │
├──────────────────────────────┼──────────────────────────────────────┤
│ X-Frame-Options              │ iframe 삽입 허용 여부               │
│                              │ → Clickjacking 방어                 │
├──────────────────────────────┼──────────────────────────────────────┤
│ X-Content-Type-Options       │ MIME 타입 스니핑 방지               │
│                              │ → nosniff로 타입 위장 차단          │
├──────────────────────────────┼──────────────────────────────────────┤
│ Strict-Transport-Security    │ HTTPS 강제 (HSTS)                   │
│ (HSTS)                       │ → HTTP 접근 시 자동 HTTPS 전환      │
├──────────────────────────────┼──────────────────────────────────────┤
│ X-XSS-Protection             │ 브라우저 내장 XSS 필터 활성화       │
│                              │ (최신 브라우저는 CSP로 대체)        │
├──────────────────────────────┼──────────────────────────────────────┤
│ Referrer-Policy              │ Referer 헤더 전송 범위 제한         │
│                              │ → 민감한 URL 정보 유출 방지         │
├──────────────────────────────┼──────────────────────────────────────┤
│ Permissions-Policy           │ 브라우저 기능(카메라, 위치 등)      │
│                              │ 사용 허용 범위 제한                 │
└──────────────────────────────┴──────────────────────────────────────┘

1.3 CSP (Content-Security-Policy) 상세

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
┌─────────────────────────────────────────────────────────────────────┐
│                    CSP 동작 원리                                     │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  CSP = 브라우저에게 "이 출처의 리소스만 실행/로드해라"고 지시       │
│                                                                     │
│  [CSP 없이]                                                         │
│  <script src="https://evil.com/xss.js"> → 실행됨!                  │
│                                                                     │
│  [CSP 적용]                                                         │
│  Content-Security-Policy: script-src 'self' https://cdn.example.com │
│  <script src="https://evil.com/xss.js"> → 차단됨!                  │
│                                                                     │
│  주요 디렉티브:                                                     │
│  ┌──────────────┬──────────────────────────────────────────────┐    │
│  │ default-src  │ 모든 리소스의 기본 정책                      │    │
│  │ script-src   │ JavaScript 출처 제한                         │    │
│  │ style-src    │ CSS 출처 제한                                │    │
│  │ img-src      │ 이미지 출처 제한                             │    │
│  │ connect-src  │ AJAX/WebSocket 연결 대상 제한                │    │
│  │ frame-src    │ iframe으로 삽입 가능한 출처 제한             │    │
│  └──────────────┴──────────────────────────────────────────────┘    │
│                                                                     │
│  값 예시:                                                           │
│  'self'        → 같은 출처만 허용                                   │
│  'none'        → 모두 차단                                          │
│  'unsafe-inline' → 인라인 스크립트 허용 (보안상 비권장)            │
│  https://cdn.example.com → 특정 도메인 허용                        │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

1.4 Spring Security에서 헤더 설정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .headers(headers -> headers
                // X-Frame-Options: DENY (Clickjacking 방어)
                .frameOptions(frame -> frame.deny())

                // Content-Security-Policy
                .contentSecurityPolicy(csp -> csp
                    .policyDirectives("default-src 'self'; " +
                        "script-src 'self' https://cdn.example.com; " +
                        "style-src 'self' 'unsafe-inline'; " +
                        "img-src 'self' data: https:; " +
                        "connect-src 'self' https://api.example.com"))

                // HSTS (HTTPS 강제)
                .httpStrictTransportSecurity(hsts -> hsts
                    .includeSubDomains(true)
                    .maxAgeInSeconds(31536000))  // 1년

                // X-Content-Type-Options: nosniff
                .contentTypeOptions(content -> {})

                // Referrer-Policy
                .referrerPolicy(referrer -> referrer
                    .policy(ReferrerPolicyHeaderWriter.ReferrerPolicy
                        .STRICT_ORIGIN_WHEN_CROSS_ORIGIN))
            );

        return http.build();
    }
}

2. CORS (Cross-Origin Resource Sharing) 심화

2.1 CORS 동작 흐름

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
┌─────────────────────────────────────────────────────────────────────┐
│                    CORS Preflight 요청 흐름                          │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  브라우저 (https://frontend.com)                                    │
│     │                                                               │
│     │ [1] Preflight 요청 (사전 확인)                                │
│     │ OPTIONS /api/users                                            │
│     │ Origin: https://frontend.com                                  │
│     │ Access-Control-Request-Method: POST                           │
│     │ Access-Control-Request-Headers: Content-Type, Authorization   │
│     │                                                               │
│     ▼                                                               │
│  서버 (https://api.example.com)                                     │
│     │                                                               │
│     │ [2] Preflight 응답                                            │
│     │ Access-Control-Allow-Origin: https://frontend.com             │
│     │ Access-Control-Allow-Methods: GET, POST, PUT                  │
│     │ Access-Control-Allow-Headers: Content-Type, Authorization     │
│     │ Access-Control-Max-Age: 3600  ← 1시간 캐시                   │
│     │                                                               │
│     ▼                                                               │
│  브라우저                                                           │
│     │                                                               │
│     │ [3] 실제 요청 (허용 확인됨)                                   │
│     │ POST /api/users                                               │
│     │ Origin: https://frontend.com                                  │
│     │ Content-Type: application/json                                │
│     │                                                               │
│     ▼                                                               │
│  서버 → 정상 응답                                                   │
│                                                                     │
│  ※ 단순 요청(Simple Request)은 Preflight 없이 바로 전송            │
│     조건: GET/HEAD/POST + 기본 헤더만 + Content-Type 제한           │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

2.2 Spring에서 CORS 설정

1
2
3
4
5
6
7
8
9
10
11
12
13
@Configuration
public class CorsConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
            .allowedOrigins("https://frontend.com")   // 허용 출처 (와일드카드 * 비권장)
            .allowedMethods("GET", "POST", "PUT", "DELETE")
            .allowedHeaders("Content-Type", "Authorization")
            .allowCredentials(true)   // 쿠키 포함 허용
            .maxAge(3600);            // Preflight 캐시 1시간
    }
}

주의: allowedOrigins("*")allowCredentials(true)는 동시에 사용할 수 없다. 쿠키를 포함하려면 반드시 출처를 명시해야 한다.


3. RBAC vs ACL vs ABAC

3.1 권한 모델 비교

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
┌─────────────────────────────────────────────────────────────────────┐
│                    권한 모델 3종 비교                                 │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  [RBAC - Role-Based Access Control] 역할 기반                       │
│                                                                     │
│  사용자 → 역할(Role) → 권한(Permission)                            │
│                                                                     │
│  예:                                                                │
│  김개발 → ADMIN  → [사용자관리, 게시글관리, 통계조회]              │
│  이인턴 → VIEWER → [게시글조회]                                    │
│                                                                     │
│  장점: 관리가 간단, 대부분의 서비스에 적합                          │
│  단점: 세밀한 리소스별 제어 어려움                                  │
│  사용: Spring Security @PreAuthorize("hasRole('ADMIN')")            │
│                                                                     │
│  ─────────────────────────────────────────────────                  │
│                                                                     │
│  [ACL - Access Control List] 접근 제어 목록                         │
│                                                                     │
│  리소스마다 누가 어떤 작업을 할 수 있는지 목록으로 관리              │
│                                                                     │
│  예:                                                                │
│  게시글 #42                                                         │
│  │ 김개발 → READ, WRITE, DELETE                                    │
│  │ 이인턴 → READ                                                   │
│  │ 박매니저 → READ, WRITE                                          │
│                                                                     │
│  장점: 리소스 단위로 세밀한 제어                                    │
│  단점: 리소스가 많아지면 관리 복잡                                  │
│  사용: 파일 시스템 권한, Google Docs 공유 설정                      │
│                                                                     │
│  ─────────────────────────────────────────────────                  │
│                                                                     │
│  [ABAC - Attribute-Based Access Control] 속성 기반                  │
│                                                                     │
│  사용자/리소스/환경의 속성(Attribute)을 조합해서 판단                │
│                                                                     │
│  예:                                                                │
│  IF user.department == "개발팀"                                     │
│     AND resource.classification != "기밀"                           │
│     AND time.hour BETWEEN 9 AND 18                                  │
│  THEN ALLOW                                                         │
│                                                                     │
│  장점: 가장 유연하고 세밀한 제어                                    │
│  단점: 정책 설계/관리가 복잡                                        │
│  사용: 대규모 엔터프라이즈, 정부 시스템                             │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

3.2 비교표

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
┌─────────────────────────────────────────────────────────────────────┐
│              RBAC vs ACL vs ABAC 비교                                │
├──────────────┬──────────────┬──────────────┬─────────────────────────┤
│ 항목         │ RBAC         │ ACL          │ ABAC                    │
├──────────────┼──────────────┼──────────────┼─────────────────────────┤
│ 제어 단위    │ 역할 (Role)  │ 리소스별     │ 속성 조합               │
├──────────────┼──────────────┼──────────────┼─────────────────────────┤
│ 관리 복잡도  │ 낮음         │ 중간         │ 높음                    │
├──────────────┼──────────────┼──────────────┼─────────────────────────┤
│ 유연성       │ 보통         │ 높음         │ 매우 높음               │
├──────────────┼──────────────┼──────────────┼─────────────────────────┤
│ 적합한 규모  │ 중소규모     │ 중규모       │ 대규모/엔터프라이즈     │
├──────────────┼──────────────┼──────────────┼─────────────────────────┤
│ 대표 사례    │ 웹 서비스    │ 파일 시스템  │ AWS IAM Policy          │
│              │ 관리자 패널  │ 문서 공유    │ 정부 시스템             │
├──────────────┼──────────────┼──────────────┼─────────────────────────┤
│ Spring 지원  │ @PreAuthorize│ Spring ACL   │ 커스텀 구현 필요        │
│              │ hasRole()    │ 모듈         │                         │
└──────────────┴──────────────┴──────────────┴─────────────────────────┘

3.3 Spring Security RBAC 구현

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 1. 역할 정의
public enum Role {
    ADMIN, MANAGER, USER, VIEWER
}

// 2. 컨트롤러에서 역할 기반 접근 제어
@RestController
@RequestMapping("/api/admin")
public class AdminController {

    @PreAuthorize("hasRole('ADMIN')")
    @GetMapping("/users")
    public List<UserDto> getAllUsers() { ... }

    @PreAuthorize("hasAnyRole('ADMIN', 'MANAGER')")
    @PutMapping("/users/{id}")
    public UserDto updateUser(@PathVariable Long id) { ... }

    // 메서드 레벨 권한 체크 (SpEL 활용)
    @PreAuthorize("hasRole('ADMIN') or #userId == authentication.principal.id")
    @GetMapping("/users/{userId}/detail")
    public UserDetailDto getUserDetail(@PathVariable Long userId) { ... }
}

// 3. Security 설정에서 URL 기반 제어
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests(auth -> auth
        .requestMatchers("/api/admin/**").hasRole("ADMIN")
        .requestMatchers("/api/manager/**").hasAnyRole("ADMIN", "MANAGER")
        .requestMatchers("/api/user/**").authenticated()
        .requestMatchers("/api/public/**").permitAll()
    );
    return http.build();
}

4. 보안 이벤트 로깅 (Audit Logging)

4.1 왜 필요한가

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
┌─────────────────────────────────────────────────────────────────────┐
│                    감사 로깅의 필요성                                 │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  보안 이벤트 로깅 = 누가, 언제, 무엇을, 어떻게 했는지 기록         │
│                                                                     │
│  필요한 이유:                                                       │
│  1. 보안 사고 발생 시 원인 추적 (포렌식)                            │
│  2. 이상 행동 탐지 (비정상 로그인 시도 등)                          │
│  3. 규정 준수 (개인정보보호법, 정보통신망법)                        │
│  4. 감사(Audit) 요구사항 충족                                       │
│                                                                     │
│  기록해야 할 이벤트:                                                │
│  ┌──────────────────────────────────────────────────────────────┐   │
│  │ • 로그인 성공/실패                                           │   │
│  │ • 권한 변경 (역할 부여/회수)                                 │   │
│  │ • 민감 데이터 접근 (개인정보 조회)                           │   │
│  │ • 관리자 작업 (사용자 삭제, 설정 변경)                       │   │
│  │ • API 호출 (특히 쓰기 작업)                                  │   │
│  │ • 인가 실패 (권한 없는 접근 시도)                            │   │
│  └──────────────────────────────────────────────────────────────┘   │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

4.2 감사 로그 설계

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
┌─────────────────────────────────────────────────────────────────────┐
│                    감사 로그 구조                                     │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  필수 포함 정보:                                                    │
│  ┌──────────────┬──────────────────────────────────────────────┐    │
│  │ timestamp    │ 이벤트 발생 시각 (UTC)                       │    │
│  │ userId       │ 행위자 식별 (누가)                           │    │
│  │ action       │ 수행한 작업 (무엇을)                         │    │
│  │ resource     │ 대상 리소스 (어디에)                         │    │
│  │ result       │ 성공/실패                                    │    │
│  │ ipAddress    │ 요청 IP                                      │    │
│  │ userAgent    │ 클라이언트 정보                              │    │
│  │ details      │ 추가 정보 (변경 전/후 값 등)                 │    │
│  └──────────────┴──────────────────────────────────────────────┘    │
│                                                                     │
│  예시 로그:                                                         │
│  {                                                                  │
│    "timestamp": "2026-03-29T10:30:00Z",                            │
│    "userId": "user-123",                                            │
│    "action": "UPDATE_USER_ROLE",                                    │
│    "resource": "user/456",                                          │
│    "result": "SUCCESS",                                             │
│    "ip": "211.xxx.xxx.xxx",                                        │
│    "details": { "before": "USER", "after": "ADMIN" }              │
│  }                                                                  │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

4.3 Spring에서 감사 로깅 구현

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// 1. 감사 이벤트 AOP
@Aspect
@Component
@RequiredArgsConstructor
public class AuditLogAspect {

    private final AuditLogRepository auditLogRepository;

    @Around("@annotation(auditable)")
    public Object audit(ProceedingJoinPoint joinPoint, Auditable auditable) throws Throwable {
        String userId = SecurityContextHolder.getContext()
            .getAuthentication().getName();
        String action = auditable.action();

        try {
            Object result = joinPoint.proceed();
            saveLog(userId, action, "SUCCESS", null);
            return result;
        } catch (Exception e) {
            saveLog(userId, action, "FAILURE", e.getMessage());
            throw e;
        }
    }

    private void saveLog(String userId, String action, String result, String detail) {
        AuditLog log = AuditLog.builder()
            .userId(userId)
            .action(action)
            .result(result)
            .detail(detail)
            .ipAddress(getClientIp())
            .timestamp(LocalDateTime.now())
            .build();
        auditLogRepository.save(log);
    }
}

// 2. 커스텀 어노테이션
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Auditable {
    String action();
}

// 3. 사용
@Auditable(action = "DELETE_USER")
@DeleteMapping("/users/{id}")
public void deleteUser(@PathVariable Long id) { ... }

5. Rate Limiting

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
┌─────────────────────────────────────────────────────────────────────┐
│                    Rate Limiting 전략                                 │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  Rate Limiting = API 호출 횟수 제한으로 남용/DDoS 방어              │
│                                                                     │
│  주요 알고리즘:                                                     │
│  ┌──────────────────┬──────────────────────────────────────────┐    │
│  │ Fixed Window     │ 고정 시간 윈도우마다 카운트 초기화       │    │
│  │                  │ 예: 1분에 100회                          │    │
│  │                  │ 단점: 윈도우 경계에서 2배 요청 가능      │    │
│  ├──────────────────┼──────────────────────────────────────────┤    │
│  │ Sliding Window   │ 현재 시점 기준 이전 N초 내 카운트       │    │
│  │                  │ Fixed Window의 경계 문제 해결            │    │
│  ├──────────────────┼──────────────────────────────────────────┤    │
│  │ Token Bucket     │ 일정 속도로 토큰 충전, 요청 시 소비     │    │
│  │                  │ 버스트 허용 + 평균 속도 제한             │    │
│  ├──────────────────┼──────────────────────────────────────────┤    │
│  │ Leaky Bucket     │ 일정 속도로만 요청 처리 (큐에 대기)     │    │
│  │                  │ 출력 속도 일정                           │    │
│  └──────────────────┴──────────────────────────────────────────┘    │
│                                                                     │
│  구현 방법:                                                         │
│  • Redis INCR + EXPIRE (분산 환경)                                  │
│  • Bucket4j (Java 라이브러리)                                       │
│  • API Gateway 단에서 처리 (AWS API Gateway, Kong)                  │
│  • Nginx limit_req 모듈                                             │
│                                                                     │
│  응답 헤더 (표준):                                                  │
│  X-RateLimit-Limit: 100       ← 전체 허용 횟수                     │
│  X-RateLimit-Remaining: 42    ← 남은 횟수                          │
│  X-RateLimit-Reset: 1711700000 ← 초기화 시각                       │
│  → 초과 시 429 Too Many Requests 응답                               │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

6. 보안 체크리스트 종합

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
┌─────────────────────────────────────────────────────────────────────┐
│              백엔드 보안 체크리스트                                    │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  [전송 계층]                                                        │
│  ☐ HTTPS 강제 (HSTS 헤더)                                          │
│  ☐ TLS 1.2 이상만 허용                                             │
│  ☐ 안전한 Cipher Suite 설정                                        │
│                                                                     │
│  [응답 헤더]                                                        │
│  ☐ Content-Security-Policy 설정                                    │
│  ☐ X-Frame-Options: DENY                                           │
│  ☐ X-Content-Type-Options: nosniff                                 │
│  ☐ Referrer-Policy 설정                                             │
│                                                                     │
│  [인증/인가]                                                        │
│  ☐ 비밀번호 BCrypt 해싱                                             │
│  ☐ JWT 안전한 저장 (HttpOnly Cookie)                                │
│  ☐ RBAC 또는 적절한 권한 모델 적용                                  │
│  ☐ 최소 권한 원칙                                                   │
│                                                                     │
│  [입력 검증]                                                        │
│  ☐ SQL Injection 방지 (Prepared Statement)                         │
│  ☐ XSS 방지 (출력 이스케이프 + CSP)                                │
│  ☐ CSRF 토큰 적용                                                   │
│  ☐ 입력값 화이트리스트 검증                                         │
│                                                                     │
│  [API 보호]                                                         │
│  ☐ Rate Limiting 적용                                               │
│  ☐ CORS 출처 제한 (와일드카드 * 금지)                              │
│  ☐ 민감 정보 응답에서 제외                                          │
│                                                                     │
│  [로깅/모니터링]                                                    │
│  ☐ 보안 이벤트 감사 로깅                                            │
│  ☐ 로그인 실패 횟수 제한 + 알림                                     │
│  ☐ 비정상 패턴 탐지 (같은 IP에서 대량 실패 등)                     │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

면접에서 자주 묻는 질문

Q1. CSP(Content-Security-Policy)란 무엇이고 왜 필요한가요?

CSP는 브라우저에게 허용된 리소스 출처를 알려주는 HTTP 응답 헤더입니다. script-src 'self'로 설정하면 같은 출처의 스크립트만 실행되고, 외부에서 주입된 악성 스크립트는 차단됩니다. XSS 공격의 핵심 방어 수단으로, 서버 측 입력 검증과 함께 사용하면 다중 방어층을 구성할 수 있습니다.

Q2. RBAC과 ACL의 차이는?

RBAC은 사용자에게 역할(ADMIN, USER 등)을 부여하고 역할에 권한을 매핑합니다. 관리가 간단하여 대부분의 웹 서비스에 적합합니다. ACL은 리소스마다 개별적으로 접근 권한 목록을 관리합니다. 파일 시스템이나 Google Docs처럼 리소스별 세밀한 권한 제어가 필요할 때 사용합니다. 일반적인 웹 서비스에는 RBAC이, 리소스 단위 공유가 필요하면 ACL이 적합합니다.

Q3. HSTS가 뭔가요?

HTTP Strict Transport Security의 약자로, 브라우저에게 해당 도메인은 항상 HTTPS로만 접속하라고 지시하는 헤더입니다. Strict-Transport-Security: max-age=31536000; includeSubDomains으로 설정하면, 사용자가 HTTP로 접속해도 브라우저가 자동으로 HTTPS로 전환합니다. 중간자 공격(MITM)에서 HTTP 다운그레이드를 방지합니다.

Q4. 보안 이벤트 로깅에서 반드시 기록해야 할 항목은?

로그인 성공/실패, 권한 변경, 민감 데이터 접근, 관리자 작업, 인가 실패는 반드시 기록해야 합니다. 각 로그에는 타임스탬프, 사용자 ID, 수행 작업, 대상 리소스, 결과(성공/실패), IP 주소를 포함합니다. 보안 사고 시 원인 추적(포렌식)과 이상 행동 탐지의 기반이 됩니다.

Q5. Rate Limiting은 왜 필요하고 어떻게 구현하나요?

API 남용, 브루트포스 공격, DDoS를 방지하기 위해 일정 시간 내 API 호출 횟수를 제한합니다. 분산 환경에서는 Redis의 INCR + EXPIRE로 구현하며, Token Bucket 알고리즘이 일반적입니다. 초과 시 429 Too Many Requests를 응답하고, X-RateLimit-Remaining 헤더로 남은 횟수를 알려줍니다.

Q6. Clickjacking이 뭐고 어떻게 방어하나요?

공격자가 투명한 iframe으로 정상 사이트를 겹쳐놓고, 사용자가 보이는 버튼을 클릭하면 실제로는 iframe 안의 다른 동작을 실행하게 하는 공격입니다. X-Frame-Options: DENY 또는 SAMEORIGIN 헤더를 설정하면 해당 페이지가 iframe에 삽입되는 것을 차단합니다. CSP의 frame-ancestors 디렉티브로도 방어 가능합니다.

Q7. CORS에서 Preflight 요청은 언제 발생하나요?

단순 요청(Simple Request) 조건을 벗어날 때 Preflight(OPTIONS) 요청이 발생합니다. 단순 요청 조건은 메서드가 GET/HEAD/POST이고, Content-Type이 application/x-www-form-urlencoded, multipart/form-data, text/plain 중 하나이며, 커스텀 헤더가 없는 경우입니다. Authorization 헤더를 사용하거나, Content-Type이 application/json이면 Preflight가 발생합니다.


핵심 정리: Security Headers(CSP, HSTS, X-Frame-Options)로 브라우저 단 방어를 구축하고, RBAC으로 역할 기반 인가를 구현하고, 감사 로깅으로 보안 이벤트를 추적하고, Rate Limiting으로 API 남용을 방지하는 것이 실무 보안의 핵심이다.