
XSS(Cross Site Scripting)
크로스 사이트 스크립팅 이란 웹사이트에서 사용자 입력을 제대로 검증하지 않을 때 발생하는 취약점을 악용하는 공격이다
- 공격자가 악성 스크립트를 삽입해 사용자의 브라우저에서 실행되도록 한다
- 일반적으로 JavaScript를 사용하여 공격이 이루어지며, 이를 통해 민감한 데이터를 훔치거나, 사용자를 악성 사이트로 리디렉션하는 등의 행동을 할 수 있다
XSS 공격 유형
1. Stored XSS
- 악성 스크립트가 서버에 영구적으로 저장됨
- 게시판, 댓글, 프로필 등 사용자가 입력한 내용이 다른 사용장게 표시될 때 공격이 발생
- 예
- 공격자가 댓글로 <script>alert('Hacked!')</script>를 작성.
- 서버가 이 입력값을 필터링 없이 저장.
- 다른 사용자가 해당 페이지를 열 때 스크립트가 실행.
2. Reflected XSS
- 악성 스크립트가 즉시 반영됨
- 보통 URL 쿼리 파라미터나 폼 데이터에서 전달된 입력값이 검증 없이 페이지에 출력될 때 발생
- 예:
- 공격자가 http://example.com?q=<script>alert('Hacked!')</script> 링크를 피해자에게 보냄.
- 피해자가 링크를 클릭하면 스크립트가 실행.
3. DOM-based XSS
- 클라이언트 사이드(JavaScript)에서 발생하는 XSS입니다.
- 서버가 아닌 브라우저에서 DOM 조작을 통해 악성 스크립트가 실행됩니다.
- 예:
- JavaScript 코드가 URL의 파라미터 값을 DOM에 삽입할 때 발생.
- <script>document.body.innerHTML = location.search</script>
XSS 방어 방법
1. 입력 데이터 검증 및 필터링
- 사용자 입력값을 검증(Validation)하고, 허용된 값만 통과시키도록 제한한다
- HTML 태그나 JavaScript 코드를 차단하기 위해 특수 문자 인코딩을 적용한다
예: < → <, > → >, " → ", ' → '
2. 출력 시 컨텍스트에 따라 이스케이프
- HTML 컨텍스트: HTML 엔티티 인코딩 적용
- JavaScript 컨텍스트: 값에 이스케이프 처리 (예: JSON.stringify())
- CSS 컨텍스트: CSS 이스케이프 처리
- URL 컨텍스트: encodeURIComponent() 사용
3. HTTP 응답 헤더 설정
- 브라우저에서 XSS 공격을 방지하기 위해 HTTP 헤더를 설정
- Content-Security-Policy (CSP): 스크립트 소스 제한
Content-Security-Policy: default-src 'self'; script-src 'self'
- X-XSS-Protection: 브라우저의 기본 XSS 방지 기능 활성화
X-XSS-Protection: 1; mode=block
4. 사용자 입력에 대해 WAF(Web Application Firewall) 적용
- WAF를 사용하면 XSS 공격 패턴을 사전에 탐지하고 차단할 수 있다
5. 라이브러리 및 프레임워크의 보안 기능 활용
- 안전한 출력 처리를 제공하는 템플릿 엔진이나 라이브러리를 사용
- Java: Jsoup
- Python: bleach
- JavaScript: DOMPurify
6. 신뢰할 수 없는 스크립트 포함 금지
- 외부 스크립트나 사용자 제공 콘텐츠를 직접 실행하지 않도록 해야 한다
- 예:
<script>eval(userInput)</script> 사용 금지
CSRF(Cross-Site Request Forgecy)
공격자가 사용자의 인증된 상태를 악용해 원하지 않은 작업을 수행하도록 유도하는 공격
- 사용자가 로그인한 상태라는 점을 악용
- 공격자가 의도한 요청이 사용자의 브라우저를 통해 서버로 전달
- 예를 들어, 사용자가 은행에 로그인한 상태에서 공격자의 링크를 클릭하면, 공격자의 계좌로 송금 요청이 이루어질 수 있음
CSRF 공격 시나리오
- 사용자가 은행 사이트(example.com)에 로그인하여 세션이 유지된 상태
- 공격자가 악성 스크립트를 포함된 페이지를 제작하고 사용자를 유도
<img src="http://example.com/transfer?amount=1000&to=attacker_account" />
- 사용자가 공격자의 페이지를 방문하면, 브라우저는 은행 사이트에 인증된 상태로 요청을 보냄
- 서버는 요청이 정상적인 사용자 요청으로 간주하고 처리
- 결과적으로, 공격자가 의도한 작업(예: 송금)이 수행됨
CSRF 방어 방법
1. CSRF 토큰 사용
- 서버는 사용자가 요청할 때마다 CSRF토큰을 발급하여 폼이나 URL에 포함
- 토큰은 서버에서만 검증 가능하며, 클라이언트는 이를 예측할 수 없음
- 서버는 요청 시 제공된 CSRF토큰이 유효한지 확인
<form action="/transfer" method="POST">
<input type="hidden" name="csrfToken" value="randomGeneratedToken123">
<input type="text" name="amount">
<button type="submit">Send</button>
</form>
2. SameSite 속성 설정된 쿠키 사용
SameSite속성을 strict또는 Lax로 설정하여 브라우저가 타 사이트에서 쿠키를 전송하지 못하도록 함
Set-Cookie: sessionId=abc123; SameSite=Strict; HttpOnly; Secure;
- 서버는 요청의
Referer 헤더를 확인해 요청이 적합한 출처에서 발생했는지 확인.
- 단, 일부 브라우저나 프록시가
Referer를 제거할 수 있으므로 보조적인 방법으로 사용.
4. POST 요청만 허용
- 민감한 작업(예: 데이터 변경, 결제 등)은 반드시 POST 요청으로 처리.
- GET 요청을 통해 데이터 변경을 허용하지 않도록 설정.
5. 쿠키의 HttpOnly 및 Secure 속성
- HttpOnly: JavaScript로 쿠키를 읽을 수 없도록 설정.
- Secure: HTTPS 연결에서만 쿠키 전송.
Set-Cookie: sessionId=abc123; HttpOnly; Secure;
6. CORS 정책 설정
- 서버는 CORS(Cross-Origin Resource Sharing) 정책을 설정하여 허용된 출처에서만 요청을 처리.
- 예: 특정 도메인만 허용.
Access-Control-Allow-Origin: https://trusted-site.com
7. 사용자 입력 확인
- 요청 파라미터를 엄격히 검증하여 의도하지 않은 값이 처리되지 않도록 함.
- 예: 금액 범위, 계좌 번호 형식 등을 검증.
CSRF 방어 전략의 조합
- 모든 민감한 요청에 CSRF 토큰을 포함.
- 쿠키에 HttpOnly, Secure, SameSite 속성을 설정.
- CORS 정책 및 Referer 검증을 병행.
- GET 요청을 통해 데이터 변경 금지.
SQL Injection
SQL Injection은 공격자가 애플리케이션의 입력 필드나 URL 쿼리 파라미터에 악의적인 SQL 코드를 삽입해 데이터베이스를 조작하거나 민감한 정보를 탈취하는 공격 기법이다
- SQL 쿼리가 외부 입력에 의해 조작되어 실행됨
- 공격자는 인증 우회, 데이터 조작, 테이블 삭제 등 다양한 악성 행위를 수행할 수 있음
SQL Injection 공격 시나리오
1. 취약한 코드 예시
String query = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'";
- 사용자가 입력한 값이 바로 SQL 쿼리에 삽입됨
2. 공격자가 입력하는 값
username 필드에 'admin' OR '1'='1'을 입력
- 쿼리는 다음과 같이 변환됨
SELECT * FROM users WHERE username = 'admin' OR '1'='1' AND password = '';
3. 결과
- 공격자는 관리자 계정으로 로그인하거나, 다른 데이터베이스 정보를 조회/조작 가능
SQL Injection 방어 방법
1. Prepared Statement사용
- Prepared Statement는 SQL쿼리와 데이터를 분리해 삽입 공격을 방지
String query = "SELECT * FROM users WHERE username = ? AND password = ?";
PreparedStatement pstmt = connection.prepareStatement(query);
pstmt.setString(1, username);
pstmt.setString(2, password);
ResultSet rs = pstmt.executeQuery();
2. ORM(Object-Relational Mapping) 사용
- JPA, Hibernate 등 ORM 프레임워크를 사용하면 SQL쿼리를 직접 작성하지 않아도 되므로 안정성이 향상
3. 입력값 검증 및 제한
- 사용자 입력값의 길이, 형식, 허용 문자 등을 엄격히 검증
- 예: 이메일 형식, 숫자 범위, 특수문자 제한 등
- 서버에서 입력 검증
if (!username.matches("[a-zA-Z0-9_]+")) {
throw new IllegalArgumentException("Invalid username");
}
4. 데이터베이스 권한 최소화
- 데이터베이스 계정은 최소 권한 원칙에 따라 설정
- 예: SELECT만 필요한 경우 INSERT, UPDATE, DELETE 권한 제거
5. Stored Procedure 사용
- 미리 정의된 SQL쿼리만을 사용하도록 제한
- 예: MySql에서 Stored Procedure 작성
CREATE PROCEDURE GetUser(IN username VARCHAR(50), IN password VARCHAR(50))
BEGIN
SELECT * FROM users WHERE username = username AND password = password;
END;
6. SQL 쿼리 로그 모니터링
- 비정상적인 쿼리 실행 시 경고하거나 차단.
- SQL Injection 시도 패턴 탐지:
- ' OR '1'='1
- -- (주석 처리)
- ; DROP TABLE
7. 웹 방화벽(WAF) 사용
- SQL Injection 패턴을 탐지하고 요청을 차단.
- WAF 솔루션: ModSecurity, AWS WAF 등.
8. 에러 메시지 숨김
- 공격자가 SQL 쿼리 구조를 유추하지 못하도록 자세한 에러 메시지를 사용자에게 노출하지 않음.
try {
} catch (SQLException e) {
logger.error("Database error occurred", e);
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "An unexpected error occurred.");
}
SQL Injection 방어 전략 요약
| 방법 | 설명 |
|---|
| Prepared Statement | 쿼리와 데이터를 분리해 SQL 구문을 안전하게 실행. |
| 입력값 검증 | 입력값의 허용 범위를 명확히 정의하고 검증. |
| ORM 사용 | JPA와 같은 ORM을 통해 쿼리 작성 최소화. |
| DB 권한 최소화 | DB 계정의 권한을 최소화하여 피해를 줄임. |
| Stored Procedure | 사전 정의된 쿼리만 실행하도록 제한. |
| 에러 메시지 숨김 | SQL 에러 메시지를 사용자에게 노출하지 않음. |
| WAF 사용 | SQL Injection 패턴 탐지 및 차단. |