Files
spring-boot-examples/docs/security/09_보안 강화.md
2025-04-08 19:56:24 +09:00

203 lines
8.3 KiB
Markdown

아래는 "스프링 시큐리티" 책의 9장에 포함될 "CSRF(Cross-Site Request Forgery) 방어", "XSS(Cross-Site Scripting)와 콘텐츠 보안 정책", 그리고 "보안 헤더 추가"에 대한 내용입니다. 각 보안 위협의 개념과 스프링부트에서의 대응 방법을 실습 가능하도록 설명했습니다.
---
### 9장. 보안 강화 기법
#### 9.1 CSRF(Cross-Site Request Forgery) 방어
**CSRF(Cross-Site Request Forgery)**는 사용자가 의도하지 않은 요청을 악의적인 웹사이트를 통해 서버로 전송하게 만드는 공격입니다. 예를 들어, 사용자가 로그인한 상태에서 악성 사이트의 링크를 클릭하면, 사용자의 인증 쿠키를 활용해 은행 계좌 이체 같은 요청이 실행될 수 있습니다.
##### CSRF 동작 원리
- 공격자는 피해자가 로그인한 상태를 가정.
- 피해자가 공격자의 사이트에서 숨겨진 폼(예: `<form action="http://bank.com/transfer" method="post">`)을 실행.
- 브라우저가 자동으로 인증 쿠키를 포함해 요청 전송.
##### 스프링 시큐리티의 CSRF 보호
스프링 시큐리티는 기본적으로 CSRF 보호를 활성화하며, POST, PUT, DELETE 같은 상태 변경 요청에 CSRF 토큰을 요구합니다.
###### 기본 설정
```java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.anyRequest().authenticated()
)
.formLogin();
return http.build();
}
}
```
- CSRF 토큰은 `<input type="hidden" name="_csrf" value="token">` 형태로 폼에 자동 추가(Thymeleaf 사용 시).
- 토큰은 세션마다 고유하며, 요청 시 서버가 이를 검증.
###### Thymeleaf에서 사용
```html
<form method="post" th:action="@{/update}">
<input type="text" name="data">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
<button type="submit">제출</button>
</form>
```
###### CSRF 비활성화
Stateless 인증(JWT 등)을 사용할 때는 CSRF 보호가 필요 없을 수 있습니다:
```java
http
.csrf(csrf -> csrf.disable())
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
```
##### 방어 팁
- **GET 요청 제한**: 상태 변경은 POST로만 처리.
- **토큰 확인**: 모든 변경 요청에 CSRF 토큰 포함.
- **SameSite 쿠키**: 세션 쿠키에 `SameSite=Strict` 설정 추가(아래 보안 헤더 참조).
#### 9.2 XSS(Cross-Site Scripting)와 콘텐츠 보안 정책
**XSS(Cross-Site Scripting)**는 공격자가 웹 페이지에 악성 스크립트를 삽입해 사용자의 브라우저에서 실행시키는 공격입니다. 이를 통해 세션 쿠키를 탈취하거나 페이지를 조작할 수 있습니다.
##### XSS 유형
- **Reflected XSS**: 악성 스크립트가 URL 파라미터 등으로 전달되어 즉시 실행.
- **Stored XSS**: 악성 스크립트가 데이터베이스에 저장되어 모든 사용자에게 노출.
- **DOM-based XSS**: 클라이언트 측 스크립트가 조작됨.
##### 스프링에서의 XSS 방어
1. **입력 검증**: 사용자 입력을 철저히 검증하고 sanitization 적용.
- `HtmlUtils.htmlEscape()`로 HTML 이스케이프:
```java
import org.springframework.web.util.HtmlUtils;
String safeInput = HtmlUtils.htmlEscape(userInput);
```
2. **템플릿 엔진**: Thymeleaf는 기본적으로 출력값을 이스케이프해 XSS 방어.
```html
<p th:text="${userInput}"></p> <!-- 자동 이스케이프 -->
<p th:utext="${userInput}"></p> <!-- 이스케이프 비활성화, 주의 필요 -->
```
##### 콘텐츠 보안 정책 (CSP)
**CSP(Content Security Policy)**는 브라우저가 허용된 소스에서만 리소스를 로드하도록 제한해 XSS를 방어합니다. HTTP 헤더로 설정합니다.
###### CSP 설정
```java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.header.writers.StaticHeadersWriter;
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.headers(headers -> headers
.addHeaderWriter(new StaticHeadersWriter("Content-Security-Policy",
"default-src 'self'; script-src 'self' https://trusted.cdn.com;"))
)
.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
.formLogin();
return http.build();
}
}
```
- `default-src 'self'`: 기본적으로 같은 출처에서만 리소스 로드.
- `script-src 'self' https://trusted.cdn.com`: 스크립트는 자체 및 신뢰된 CDN에서만 허용.
##### 방어 팁
- **출력 이스케이프**: 모든 동적 콘텐츠를 이스케이프 처리.
- **CSP 강화**: 외부 리소스 최소화 및 엄격한 정책 적용.
- **입력 제한**: 허용된 문자만 허용(예: 정규식 사용).
#### 9.3 보안 헤더 추가
보안 헤더는 브라우저의 기본 보안 기능을 강화해 다양한 공격을 방지합니다. 스프링 시큐리티는 이를 쉽게 추가할 수 있는 설정을 제공합니다.
##### 주요 보안 헤더
1. **X-Content-Type-Options**:
- `nosniff`: MIME 타입 스니핑 방지.
- 설정:
```java
http.headers(headers -> headers.contentTypeOptions());
```
2. **X-Frame-Options**:
- `DENY` 또는 `SAMEORIGIN`: 클릭재킹(Clickjacking) 방지.
- 설정:
```java
http.headers(headers -> headers.frameOptions(frame -> frame.sameOrigin()));
```
3. **X-XSS-Protection**:
- `1; mode=block`: 브라우저의 XSS 필터 활성화(구형 브라우저 지원).
- 설정:
```java
http.headers(headers -> headers.xssProtection());
```
4. **Strict-Transport-Security (HSTS)**:
- HTTPS 강제 적용 및 중간자 공격 방지.
- 설정:
```java
http.headers(headers -> headers.httpStrictTransportSecurity(hsts -> hsts.maxAgeInSeconds(31536000)));
```
5. **Content-Security-Policy**: 위 CSP 섹션 참조.
6. **Referrer-Policy**:
- 참조 정보 제한(예: `no-referrer-when-downgrade`).
- 설정:
```java
http.headers(headers -> headers.referrerPolicy(referrer -> referrer.policy(ReferrerPolicy.NO_REFERRER_WHEN_DOWNGRADE)));
```
##### 종합 설정 예제
```java
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.headers(headers -> headers
.contentTypeOptions()
.frameOptions(frame -> frame.deny())
.xssProtection()
.httpStrictTransportSecurity(hsts -> hsts.maxAgeInSeconds(31536000).includeSubDomains(true))
.addHeaderWriter(new StaticHeadersWriter("Referrer-Policy", "strict-origin-when-cross-origin"))
.addHeaderWriter(new StaticHeadersWriter("Content-Security-Policy", "default-src 'self'"))
)
.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
.formLogin();
return http.build();
}
}
```
##### SameSite 쿠키 설정
CSRF 방어를 강화하려면 세션 쿠키에 `SameSite` 속성 추가:
```properties
# application.properties
server.servlet.session.cookie.same-site=strict
```
- `Strict`: 타사 사이트에서 쿠키 전송 차단.
- `Lax`: GET 요청은 허용, POST 등은 차단.
##### 실습 예제
1. CSRF 토큰 없이 POST 요청 시 403 확인.
2. XSS 공격 시도(예: `<script>alert('xss')</script>`) 후 이스케이프 동작 확인.
3. 브라우저 개발자 도구에서 보안 헤더 적용 여부 점검.
---
위 내용은 CSRF, XSS, 보안 헤더의 개념과 스프링 시큐리티에서의 방어 방법을 실습 가능하도록 설명했습니다. 추가적인 예제나 세부 사항이 필요하면 말씀해 주세요!