2025-04-08T19:56:24
This commit is contained in:
203
docs/security/09_보안 강화.md
Normal file
203
docs/security/09_보안 강화.md
Normal file
@@ -0,0 +1,203 @@
|
||||
아래는 "스프링 시큐리티" 책의 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, 보안 헤더의 개념과 스프링 시큐리티에서의 방어 방법을 실습 가능하도록 설명했습니다. 추가적인 예제나 세부 사항이 필요하면 말씀해 주세요!
|
||||
Reference in New Issue
Block a user