웹 브라우저에서 다른 출처(origin)의 리소스를 요청할 때 발생하는 보안 정책
웹 애플리케이션이 서버와 상호작용할 때 동일 출처 정책(Same-Origin Policy)가 적용되고, 이 정책은 스크립트 기반 요청이 동일한 출처에서만 허용되도록 제한함
CORS는 이 제한을 우회하기 위해 브라우저와 서버 간에 안전한 방식으로 교차 출처 요청을 처리할 수 있게 함
왜 굳이 교차 출처 요청을 기본적으로 제한하고, 이를 우회하도록 설정해야하는 걸까?
-> 보안 때문이다. 악의적인 요청으로부터 사용자의 데이터를 보호하기위한 핵심 원칙 중 하나.
만약, 이런 상황이 있다면 어떨까?
- 사용자가 A 웹사이트에 로그인함
- 동시에 사용자가 악의적인 B 웹사이트에 방문함
- B 웹사이트가 A 웹사이트로 요청을 보냄
- 브라우저는 자동으로 A 웹사이트에 대한 쿠키와 인증 정보를 포함하여 요청을 처리함
-> 사용자의 인증 정보를 악용해 공격자가 권한 없는 작업을 수행할 수 있음
동일 출처 정책은 이를 방지하기 위한 방법이다.
사용자 데이터 도난 방지
동일 출처 정책은 이러한 데이터 탈취 공격을 방지할 수 있다.
불필요한 자원 노출 방지
동일 출처 정책은 신뢰할 수 없는 출처로부터의 요청을 제한해 이런 문제를 방지한다.
XSS와의 연계 문제
CORS는 동일 출처 정책을 유지하면서도 특정 조건 하에 안전하게 교차 출처 요청을 허용할 수 있도록 설계되었다. 동일 출처 정책이 중요하면서도 CORS를 우회하도록 설계된 이유는 무엇일까?
현대 웹 애플리케이션의 분산 아키텍처
공유 API와 서비스
정당한 비즈니스 요청 지원
결론
동일 출처 정책은 웹의 기본 보안 매커니즘으로, 사용자를 악의적인 공격으로부터 보호하기 위해 설계되었음.
출처가 동일한 경우에만 데이터를 교환하도록 제한하여 웹 애플리케이션의 기본적인 보안 경계를 설정함.
CORS는 동일 출처 정책을 유지하면서도, 특정 조건에서 교차 출처 요청을 허용할 수 있도록 도와주는 매커니즘.
Content-Type이 text/plain, multipart/form-data, application/x-www-form-urlencoded 중 하나Authorization 헤더 사용 시, 사전 요청 발생Access-Control-Allow-Credentials 헤더로 명시적으로 허용해야 함브라우저가 실제 요청을 보내기 전에 서버에 요청할 권한이 있는지 확인하는 과정
특정 조건을 만족하는 HTTP 요청이 서버에 전송되기 전에 실행됨
동작 방식
서버는 응답에 아래와 같은 CORS 헤더를 추가해 브라우저와의 교차 출처 통신을 허용함
보안
성능
사용자가 신뢰하는 웹 애플리케이션을 대상으로 악의적인 요청을 전송하게 하는 보안 취약점
공격자는 사용자의 인증 정보를 악용해 사용자가 의도치 않은 작업을 수행하도록 유도함
위의 예시로 든 정상적인 웹사이트 A와 악의적인 웹사이트 B를 동시 접속했을 때, 사용자가 모르게 사용자의 인증 정보(쿠키)를 포함해 정상적인 요청인 것 처럼 요청을 보내는 행위 같은 것.
피해자가 로그인 상태일 경우 공격이 성공하며, 공격자는 사용자의 인증 정보를 알 필요가 없다. 브라우저가 자동으로 쿠키를 전송하기 때문에 발생한다.
서버에서 생성하여 클라이언트에게 제공하고, 요청마다 이를 포함하도록 요구함
동작 방식
요청의 Referer 또는 Origin 헤더를 확인하여 요청이 신뢰할 수 있는 출처에서 온 것인지 검증함
동작 방식
Referer 또는 Origin 값이 서버의 도메인과 일치하는 지 확인한다.제한: 일부 브라우저 또는 프록시 환경에서는 헤더가 제거될 수 있음
쿠키가 동일 출처 요청에서만 전송되도록 제한하는 속성을 설정함
동작 방식
SameSite=Strict: 쿠키는 동일한 출처에서 발생한 요청에만 포함SameSite=Lax: GET 요청 또는 특정 안전한 요청에서만 쿠키 포함CORS 설정을 통해 특정 출처에서만 서버 자원에 접근할 수 있도록 제한함
다만 CSRF 방지 목적으로만 사용하는 것은 권장되지 않는다
공격자가 악성 스크립트를 삽입하여 다른 사용자의 브라우저에서 실행되도록 만드는 공격 기법
주로 사용자가 신뢰하는 웹사이트를 대상으로 수행되며, 이를 통해 사용자의 개인 정보, 세션 쿠키, 브라우저 데이터 등을 탈취하거나 악성 동작을 수행할 수 있음
1. 입력값 검증(Input Validation)
2. 출력값 이스케이프(Output Escaping)
3. Content Security Policy (CSP)
4. HTTPOnly 쿠키 사용
HttpOnly 속성을 추가하여 자바스크립트에서 쿠키 접근을 방지5. 템플릿 엔진 사용
6. DOM 조작 주의
7. 라이브러리 활용
공격자가 웹 애플리케이션의 보안 취약점을 악용해 SQL 쿼리를 조작하는 공격 기법
공격자는 이를 통해 데이터베이스의 민감 정보를 조회하거나 조작하는 등 공격할 수 있음
웹 애플리케이션이 사용자 입력값을 제대로 검증하지 않고 SQL 쿼리에 직접 삽입할 때 발생함.
공격자는 입력값을 통해 SQL 쿼리를 조작하고 의도치 않은 동작을 수행할 수 있음.
ex)
정상 쿼리
SELECT * FROM users WHERE username = 'john' AND password = 'password123';
공격자가 입력값을 조작
입력값:
username = 'john' OR '1'='1
password = 'anything'
결과 쿼리
SELECT * FROM users WHERE username = 'john' OR '1'='1' AND password = 'anything';
1=1은 항상 참이므로 데이터베이스는 모든 사용자 정보를 반환하게 됨.
Classic SQL Injection
공격자가 입력값을 조작하여 SQL 쿼리를 직접 변경
입력값: admin' --
생성 쿼리:
SELECT * FROM users WHERE username = 'admin' --' AND password = 'password';
-- 이후는 주석처리 되어 비밀번호 조건을 우회할 수 있음
Blind SQL Injection
데이터베이스의 응답 내용을 직접 확인할 수 없는 경우, 참/거짓 응답을이용해 데이터를 추출
입력값: ' OR 1=1 --
참일 경우:
SELECT * FROM users WHERE id = 1 AND '1'='1'; -- 데이터 반환
거짓일 경우:
SELECT * FROM users WHERE id = 1 AND '1'='0'; -- 데이터 반환 없음
Union-based SQL Injection
UNION 명령어를 이용해 여러 쿼리의 결과를 결합
입력값: ' UNION SELECT username, password FROM admin --
결과 쿼리:
SELECT username, password FROM users WHERE id = '' UNION SELECT username, password FROM admin --;
Error-based SQL Injection
쿼리 실행 중 발생한 에러 메시지를 통해 데이터베이스 정보를 추출
입력값: 1' AND EXTRACTVALUE(1, CONCAT(0x3a, (SELECT @@version))) --
반환: 데이터베이스 버전 정보
Time-based Blind SQL Injection
데이터베이스의 응답 지연 시간을 이용해 참/거짓 조건 판단
입력값: ' OR IF(1=1, SLEEP(5), 0) --
참일 경우 서버 응답이 지연됨
Prepared Statements (Parameterized Queries)
사용자 입력값을 직접 사용하지 않고 플레이스홀더를 사용함
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();
Stored Procedures(저장 프로시져)
데이터베이스에서 미리 정의된 SQL 쿼리의 집합. 서버 측에서 실행되는 재사용 가능한 프로그램 단위로, 하나 이상의 SQL 문장을 포함하며, 매개변수를 받아 특정 작업을 수행할 수 있음
CREATE PROCEDURE GetUserByUsername
@username NVARCHAR(50)
AS
BEGIN
SELECT * FROM users WHERE username = @username;
END;
호출
CallableStatement stmt = connection.prepareCall("{call GetUserByUsername(?)}");
stmt.setString(1, "john");
ResultSet rs = stmt.executeQuery();
입력값인 john은 @username 파라미터에 바인딩되며 SQL Injection이 어려움
Input Validation(입력값 검증)
사용자 입력값을 철저히 검증하여 SQL에 사용될 수 없는 문자나 패턴을 필터링함
if (!input.matches("\\d+")) {
throw new IllegalArgumentException("Invalid input");
}
ORM 사용
Hibernate, JPA 같은 ORM(Object-Relational Mapping) 도구를 사용해 직접 SQL 작성 최소화
데이터베이스 권한 제한
Principle of Least Privilege(최소 권한 원칙)를 적용, 애플리케이션 계정은 필요한 작업만 수행할 수 있는 권한만 가지도록 함
에러 메시지 숨기기
사용자에게 자세한 SQL 에러 메시지를 노출하지 않음
ex) 잘못된 입력 시, "Invalid Request"와 같은 일반적인 메시지를 반환
웹 애플리케이션 방화벽(WAF) 활용
SQL Injection 공격 패턴을 탐지하고 차단하는 방화벽 도입
로그 모니터링