CORS 자격 증명 허용 시 동적 Origin

CORS Misconfiguration (Credentials + Dynamic Origin)

설명

서버가 Access-Control-Allow-Credentials를 true로 설정하면서 Access-Control-Allow-Origin 값을 요청의 Origin/쿼리/헤더 등 사용자 제어 입력을 그대로 반영할 때 발생하는 CORS 오구성 취약점입니다. 브라우저는 허용된 Origin에 대해 쿠키/Authorization 헤더 등 자격 증명을 포함해 교차 출처 요청을 보내고, 응답도 JS에서 읽을 수 있게 허용합니다. 공격자는 악성 사이트에서 피해자의 브라우저로 자격 증명을 자동 포함한 요청을 보내 민감 정보 열람이나 상태 변경을 유도할 수 있습니다. 특히 ACAO에 "null"을 사용하거나 여러 Origin을 무차별 반영하면 sandboxed iframe, file:// 등 예기치 못한 출처까지 허용되어 위험해집니다.

잠재적 영향

  • 인증된 데이터 노출: 피해자가 로그인된 상태에서 악성 Origin이 credentials 포함 요청을 유도하면, 서버가 그 Origin을 반영해 응답을 공개하여 민감 정보가 외부로 유출됩니다.

  • 무단 상태 변경: 공격자가 교차 출처로 쓰기 요청(계정 변경, 결제 등)을 보내고 응답이 허용되면, 사용자의 권한으로 서버 상태가 변경될 수 있습니다.

  • 권한 오남용/권한 상승(IDOR 연계): 동적 Origin 허용이 IDOR와 결합되면, 다른 사용자의 리소스 조회/수정이 외부 Origin에서 가능해져 수평·수직 권한 상승으로 이어집니다.

  • SOP 우회 기반 공격 확대: 동적 ACAO+Credentials는 동일 출처 정책을 사실상 약화시켜, 내부 API/관리자 콘솔 등 비공개 엔드포인트 접근 경로가 넓어집니다.

해결 방법

  • 엄격한 allowlist: 허용할 Origin을 사전에 정확히 나열(Set/Map)하고, 목록에 일치하는 경우에만 Access-Control-Allow-Origin을 해당 Origin으로 설정하세요. 정규식 사용 시 스키마/서브도메인/포트를 명확히 고정하세요.

  • 절대 반영 금지: 요청의 Origin/쿼리/헤더 값을 그대로 반영하지 마세요. Access-Control-Allow-Origin: "null"도 사용하지 마세요.

  • 자격 증명 조건부 허용: allowlist에 일치할 때만 Access-Control-Allow-Credentials: true를 설정하고, 불일치 시 이 헤더를 보내지 않거나 403으로 거절하세요.

  • Vary: Origin 설정: 여러 합법 Origin을 지원하는 경우, 캐시 오동작을 막기 위해 Vary: Origin을 함께 설정하세요.

  • 프리플라이트(OPTIONS) 검증: 허용된 메서드/헤더만 Access-Control-Allow-Methods/Headers로 응답하고, 비허용 조합은 거절하세요.

  • 쿠키 보강: SameSite=Lax/Strict, Secure, HttpOnly를 사용해 교차 사이트 전송을 최소화하고, 민감 작업에는 CSRF 토큰 등 추가 검증을 적용하세요.

취약한 코드 및 안전한 코드 예시

취약한 코드

안전한 코드

설명:

  • 취약한 코드: 요청의 Origin/쿼리 값을 그대로 Access-Control-Allow-Origin에 반영하고 Access-Control-Allow-Credentials를 항상 true로 설정했습니다. 공격자는 자신의 도메인에서 피해자의 브라우저로 요청을 보내도 브라우저가 자격 증명을 포함해 전송하고, 응답을 JS로 읽을 수 있어 민감 정보 유출과 상태 변경이 가능합니다. 또한 "null" Origin까지 허용되어 sandboxed iframe 등 예기치 못한 출처가 허용될 수 있습니다.

  • 안전한 코드: 사전에 정의된 allowlist와 정확히 일치하는 Origin에 대해서만 Access-Control-Allow-Origin과 Access-Control-Allow-Credentials를 설정합니다. 그 외 Origin에는 해당 헤더를 설정하지 않거나 403으로 거절하여 브라우저가 자격 증명을 보내지 못하게 합니다. 또한 Vary: Origin으로 캐시 혼선을 방지하고, 프리플라이트에서도 동일한 검증을 수행해 정책 일관성을 확보합니다.

참조

Last updated