CORSは認証ではない
한국어 원문으로 표시합니다.
목표
출처·메서드·헤더의 허용 행렬을 실제 프리플라이트로 검증합니다.
왜 중요한가
프런트엔드에서 응답을 읽지 못하자 모든 출처에 별표를 허용했다. 쿠키를 보내는 요청에서는 정책이 더 복잡해졌고, 개발자는 CORS만 켜면 외부 요청이 차단된다고 오해했다. 이 실습에서는 브라우저의 읽기 정책과 서버 인증을 분리한다. 인증 기능을 대신 만들지는 않는다.
단계
/root/work/fa-cors-policy-lab/service.py에서 origin(value)는 http 또는 https URL이며 host가 있고 path·query·fragment·사용자 정보가 없으면 입력 문자열을 반환합니다. 그 외 ValueError입니다. 끝의 /도 path이므로 거절합니다.
처음 한 번 준비하세요. 기존 파일은 덮어쓰지 않습니다.
mkdir -p /root/work/fa-cors-policy-lab
test -e /root/work/fa-cors-policy-lab/service.py || cp /opt/fixtures/ten_labs/fa-cors-policy-lab/service.py /root/work/fa-cors-policy-lab/service.py
cd /root/work/fa-cors-policy-lab
-
/root/work/fa-cors-policy-lab/service.py에서 origins(values)는 각 항목을 origin으로 검증한 뒤 처음 나온 순서대로 중복을 제거한 새 리스트입니다. -
/root/work/fa-cors-policy-lab/service.py에서 methods(values)는 GET·POST·PUT·DELETE·OPTIONS만 허용하고 대문자로 바꿔 중복을 제거합니다. 빈 목록이나 그 외 값은 ValueError입니다. -
/root/work/fa-cors-policy-lab/service.py에서 policy(allowed, credentials)는 credentials가 bool인지 확인합니다. allowed에 '*'가 있으면 ValueError이고, {allow_origins:origins(allowed), allow_credentials:credentials}를 반환합니다. -
/root/work/fa-cors-policy-lab/service.py에서 create_app(allowed, credentials=True)는 policy를 검증하고 CORSMiddleware를 설정한 앱입니다. GET/POST만 허용하고 Content-Type·X-Request-ID 요청 헤더를 허용하며 X-Trace 응답 헤더를 expose합니다. GET /data는 {ok:True}, X-Trace='trace-1'을 반환합니다. -
/root/work/fa-cors-policy-lab/service.py에서 preflight_headers(source, method, requested='X-Request-ID')는 Origin, Access-Control-Request-Method, Access-Control-Request-Headers 세 키를 가진 딕셔너리입니다. method는 대문자입니다. -
/root/work/fa-cors-policy-lab/service.py에서 preflight_status(app, source, method, requested='X-Request-ID')는 TestClient로 /data에 OPTIONS 요청을 보내 HTTP 상태를 반환합니다. 다른 출처·DELETE·X-Secret 헤더는 400이어야 합니다. -
/root/work/fa-cors-policy-lab/service.py에서 cors_observation(app, source)는 GET /data를 보내 (상태, Access-Control-Allow-Origin 값 또는 None, JSON 본문)을 반환합니다. 허용되지 않은 출처여도 200 본문은 실행되지만 허용 출처 헤더는 없어야 합니다.
참고
- 인터넷과 패키지 설치 없이 기존 lab-dev 환경에서 수행합니다.
- 각 단계는 45초 채점 예산 안에서 실행됩니다. 실제 sleep이나 네트워크 호출을 추가하지 마세요.
- 채점은 제출 모듈을 새로 불러오고 독립 입력과 임시 DB로 검사합니다. 예상값을 상수로 반환하는 대신 계약을 구현하세요.
- FastAPI 공식 문서 · pytest 공식 문서 · Python sqlite3
- 한계: TestClient는 브라우저가 아니다. CORS 응답 헤더와 프리플라이트를 검사하지만 브라우저 자체의 읽기 차단까지 구현하지는 않는다. 허용되지 않은 Origin을 보낸 일반 GET도 서버에서 실행될 수 있다. 민감한 동작은 별도의 인증·권한·CSRF 정책으로 보호해야 한다.
출처 형식을 검증한다
/root/work/fa-cors-policy-lab/service.py에서 origin(value)는 http 또는 https URL이며 host가 있고 path·query·fragment·사용자 정보가 없으면 입력 문자열을 반환합니다. 그 외 ValueError입니다. 끝의 /도 path이므로 거절합니다.
처음 한 번 준비하세요. 기존 파일은 덮어쓰지 않습니다.
mkdir -p /root/work/fa-cors-policy-lab
test -e /root/work/fa-cors-policy-lab/service.py || cp /opt/fixtures/ten_labs/fa-cors-policy-lab/service.py /root/work/fa-cors-policy-lab/service.py
cd /root/work/fa-cors-policy-lab
URL 전체를 출처로 허용하면 경로나 사용자 정보를 혼동할 수 있습니다.
저장 후 bash /opt/lab/checks/fa-cors-policy-lab/01-contract.sh로 확인하세요.
중복 출처를 제거한다
/root/work/fa-cors-policy-lab/service.py에서 origins(values)는 각 항목을 origin으로 검증한 뒤 처음 나온 순서대로 중복을 제거한 새 리스트입니다.
허용 목록은 문자열 부분 일치가 아니라 정확한 출처 목록입니다.
저장 후 bash /opt/lab/checks/fa-cors-policy-lab/02-contract.sh로 확인하세요.
메서드를 허용 목록으로 제한한다
/root/work/fa-cors-policy-lab/service.py에서 methods(values)는 GET·POST·PUT·DELETE·OPTIONS만 허용하고 대문자로 바꿔 중복을 제거합니다. 빈 목록이나 그 외 값은 ValueError입니다.
허용하지 않은 PATCH와 임의 메서드를 조용히 추가하지 않습니다.
저장 후 bash /opt/lab/checks/fa-cors-policy-lab/03-contract.sh로 확인하세요.
자격 증명과 별표를 함께 허용하지 않는다
/root/work/fa-cors-policy-lab/service.py에서 policy(allowed, credentials)는 credentials가 bool인지 확인합니다. allowed에 '*'가 있으면 ValueError이고, {allow_origins:origins(allowed), allow_credentials:credentials}를 반환합니다.
이 실습의 명시적인 정책은 자격 증명 여부와 관계없이 별표를 받지 않습니다.
저장 후 bash /opt/lab/checks/fa-cors-policy-lab/04-contract.sh로 확인하세요.
실제 CORS 미들웨어를 단다
/root/work/fa-cors-policy-lab/service.py에서 create_app(allowed, credentials=True)는 policy를 검증하고 CORSMiddleware를 설정한 앱입니다. GET/POST만 허용하고 Content-Type·X-Request-ID 요청 헤더를 허용하며 X-Trace 응답 헤더를 expose합니다. GET /data는 {ok:True}, X-Trace='trace-1'을 반환합니다.
preflight와 실제 응답에 헤더를 손으로 따로 붙이면 두 정책이 쉽게 어긋납니다.
저장 후 bash /opt/lab/checks/fa-cors-policy-lab/05-contract.sh로 확인하세요.
프리플라이트 요청을 만든다
/root/work/fa-cors-policy-lab/service.py에서 preflight_headers(source, method, requested='X-Request-ID')는 Origin, Access-Control-Request-Method, Access-Control-Request-Headers 세 키를 가진 딕셔너리입니다. method는 대문자입니다.
실제 요청 메서드는 OPTIONS이며 검사하려는 메서드는 별도 헤더에 있습니다.
저장 후 bash /opt/lab/checks/fa-cors-policy-lab/06-contract.sh로 확인하세요.
거절 행렬을 계산한다
/root/work/fa-cors-policy-lab/service.py에서 preflight_status(app, source, method, requested='X-Request-ID')는 TestClient로 /data에 OPTIONS 요청을 보내 HTTP 상태를 반환합니다. 다른 출처·DELETE·X-Secret 헤더는 400이어야 합니다.
거절 사유 세 종류를 한 요청에 섞지 않아야 빠진 정책을 찾을 수 있습니다.
저장 후 bash /opt/lab/checks/fa-cors-policy-lab/07-contract.sh로 확인하세요.
CORS와 인증의 차이를 관찰한다
/root/work/fa-cors-policy-lab/service.py에서 cors_observation(app, source)는 GET /data를 보내 (상태, Access-Control-Allow-Origin 값 또는 None, JSON 본문)을 반환합니다. 허용되지 않은 출처여도 200 본문은 실행되지만 허용 출처 헤더는 없어야 합니다.
curl이나 서버 간 요청은 브라우저의 CORS 읽기 제한을 따르지 않습니다.
저장 후 bash /opt/lab/checks/fa-cors-policy-lab/08-contract.sh로 확인하세요.