본문 바로가기

개발기술/보안

브라우저 보안정책

CORS (Cross-Origin Resource Sharing)

  • CORS는 원래 브라우저가 서버의 데이터를 보호하기 위해 존재하는 정책입니다. 브라우저는 기본적으로 출처(Origin)가 다른 요청을 차단하여, 악의적인 웹사이트가 임의로 API를 호출하는 것을 막음.
    • Same-Origin Policy (SOP, 동일 출처 정책) :  브라우저는 현재 열려 있는 HTML 파일이 실행된 **출처(origin)**와 다른 출처에서 파일을 직접 가져오는 것을 제한합니다.
    • 출처(Origin)는 프로토콜 + 도메인 + 포트번호로 구성됩니다.
    • 보안 위협 예시: CORS가 없다면?
  • 브라우저가 출처(Origin)를 비교하는 방법
    • 브라우저는 fetch() 요청을 보낼 때, 현재 페이지의 **출처(Origin)**와 **요청 대상의 출처(Origin)**를 비교합니다
console.log(window.location.origin); // 현재 웹사이트의 출처 확인
window.location.origin = "https://frontend.com"
fetch("https://api.example.com/data")

 

  • 정적 파일(HTML, CSS, JS)을 받은 frontend.com과 fetch() 요청의 대상(api.example.com)의 "출처(Origin)"이 다르면, 브라우저가 CORS 정책에 따라 차단합니다 
  • 프론트엔드와 백엔드가 다른 출처(Origin)가 되는 이유
    • 1. 웹 서버와 앱 서버를 분리해서 운영하는 경우 (도메인 다름)
      • 프론트엔드: https://frontend.com
      • 백엔드(API 서버): https://api.example.com
    • 2. 개발 환경에서 포트가 다르게 설정된 경우 (포트 다름)
      • 프론트엔드 (React 개발 서버): http://localhost:3000
      • React 프로젝트를 개발할 때, create-react-app 등의 도구를 사용하면 개발용 서버를 실행할 수 있습니다. 이 서버는 정적 파일(HTML, CSS, JS)을 제공하고, 코드 변경을 실시간 반영하는 Hot Reloading 기능을 지원합니다.
      • 백엔드 (Spring Boot, Express 서버): http://localhost:8080
    • 3. HTTP와 HTTPS가 다를 때 (프로토콜 다름)
      • 프론트엔드: https://example.com
      • 백엔드(API 서버): http://example.com
    • 4.  API 서버가 다른 도메인의 백엔드를 호출하는 경우 (프록시 서버 문제)
  • 해결책 : 서버에서 Access-Control-Allow-Origin 헤더 설정
    • 브라우저가 응답 헤더를 검사
      • 만약 Access-Control-Allow-Origin이 없으면 브라우저는 요청을 차단 (CORS 오류 발생).
      • 하지만 Access-Control-Allow-Origin: https://frontend.com이 포함되어 있으면 요청을 허용하고 응답을 정상 처리.
@CrossOrigin(origins = "http://localhost:3000") // 특정 출처 허용
@RestController
public class ApiController {
    @GetMapping("/api/data")
    public ResponseEntity<String> getData() {
        return ResponseEntity.ok("CORS 해결됨!");
    }
}

CSRF (Cross-Site Request Forgery)

  • **CSRF(크로스 사이트 요청 위조)**는 공격자가 사용자의 세션을 악용하여 원치 않는 요청을 서버에 보내도록 유도하는 웹 보안 공격 기법입니다. 즉, 사용자가 자신도 모르게 인증된 상태에서 공격자가 의도한 요청을 실행하게 만드는 공격입니다.
  • 공격 시나리오
    • 1. 사용자가 example.com에 로그인하여 인증 쿠키가 저장됨. (httpOnly가 아니면 JavaScript로 접근 가능)
    • 2. 공격자가 CSRF 공격을 수행하는 **악성 웹사이트(evil.com)**를 운영.
    • 3. 사용자가 evil.com을 방문하면, 공격자는 자동으로 example.com으로 특정 요청을 보냄. 
    • 4. <img src="https://example.com/api/transfer?amount=10000&to=attacker-account" />
    • 5. 브라우저는 example.com에 로그인된 상태이므로 자동으로 인증된 쿠키를 포함하여 요청을 보냄.
    • 6. 서버는 정상적인 사용자의 요청인 줄 알고 공격자의 계좌로 돈을 송금하는 등의 행동을 수행.
    • 즉, 사용자는 example.com에서 아무런 조작을 하지 않았지만, 공격자가 원치 않는 요청을 실행하도록 유도하는 것이 CSRF의 핵심.
  • CORS가 CSRF 공격을 방어할 수 있을까?
    • CORS는 브라우저가 "다른 출처(Origin)"에서 JavaScript로 API를 호출하는 것을 차단하는 보안 정책입니다. 반면, CSRF는 브라우저의 "자동 쿠키 포함" 기능을 악용하는 공격이므로 CORS와 관계없이 발생할 수 있습니다. 즉, CORS는 fetch()나 XMLHttpRequest 같은 JavaScript 요청을 차단하지만, CSRF는 img, form 같은 HTML 요소를 이용하므로 차단되지 않습니다.
  •  해결책
    • 1.  CSRF Token 사용 (가장 일반적인 방법)
      • CSRF 토큰은 서버가 요청할 때마다 생성하는 임의의 값으로, 클라이언트가 요청 시 함께 전송해야 함. 브라우저는 자동으로 CSRF 토큰을 포함할 수 없으므로, CSRF 공격이 방지됨.
        • 일반적으로 클라이언트는 GET /csrf-token을 먼저 호출하여 토큰을 받아온 후, API 요청에 X-CSRF-Token을 포함하여 전송함.
      • 그렇다면, 공격자가 CSRF 토큰 요청을 포함하는 HTML 태그를 추가할 수 있을까?
        • 공격자가 HTML 태그(<img>, <script>, <form> 등)를 이용해 CSRF 토큰을 요청할 수는 있지만, 해당 값을 활용하려면 JavaScript가 필요함. 그러나 JavaScript가 CORS 정책(Same-Origin Policy)에 의해 다른 출처의 응답을 읽을 수 없으므로, 공격자가 CSRF 토큰을 알아낼 수 없음.
    • 2. SameSite Cookie 설정
    • 3. Referrer 검증
    • 4. CORS(Cross-Origin Resource Sharing) 정책 강화

'개발기술 > 보안' 카테고리의 다른 글