[http] SOP(Same Origin Policy) / CORS
페이지 정보
작성자 sbLAB 댓글 0건 조회 3,138회 작성일 22-12-19 23:07본문
Flutter(크롬/엣지 브라우저) 에서 WepApi 호출할 때 CORS 에러(XMLHttpRequest error.)
Cross-Origin Resource Sharing (CORS)
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
PHP 소스에서 CORS 설정(권장)
접속 클라이언트가 웹브라우저인 경우(크롬 또는 웹게시 플러터앱)에 서버에 HTTP_ORIGIN 전달됨,
순수어플에서 프로그래밍되어 호출될때 기본적으로 HTTP_ORIGIN 전달 없음 -> 전달없을 때는 CORS 과정 비실행하려고 하면..
바로 아래 소스 비슷하게 하면 원하는 동작이 되는듯 하지만 사용할수 없는 부족한 소스임(하단 개선된 소스사용).
아파치 웹서버 httpd.conf 에서 CORS 설정
예) 서버 웹 디렉토리가 D:/xampp/htdocs/jp 인 경우 (http://localhost/jp/)
[아파치 서버, PHP소스 모두 CORS 설정을 하지 않은 경우 크롬 에러]
Access to XMLHttpRequest at 'http://192.168.0.3/jp/memberselect' from origin 'http://localhost:2120' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
browser_client.dart:72
POST http://192.168.0.3/jp/memberselect net::ERR_FAILED 200
[아파치 서버에서 CORS 설정한 상태에서 PHP소스에서도 CORS 설정을 중복한 경우 크롬 에러]
Access to XMLHttpRequest at 'http://192.168.0.3/jp/memberselect' from origin 'http://localhost:2120' has been blocked by CORS policy:
The 'Access-Control-Allow-Origin' header contains multiple values '*, http://localhost:2120', but only one is allowed.
browser_client.dart:72
POST http://192.168.0.3/jp/memberselect net::ERR_FAILED 200
[크롬,엣지 웹브라우저에서 WebApi 호출할때 클라이언트 HTTP_ORIGIN 값(http://localhost:5743) 전달됨]
[순수 스마트폰 어플에서 http.post로 WebApi 호출할때(내장 웹브라우저 호출 아님) 클라이언트의 HTTP_ORIGIN 전달 없음]
[개선된 소스]
header('Access-Control-Max-Age: 600'); //cache for 10 minutes(Chromium)
위 헤더를 넣어주면, 처음 1번만 Preflight(OPTIONS 콜 -> POST 호출, 즉 2번 콜) 동작하여 검증하고,
이후 부터는 바로 이미 캐시된 정보가 있는 상황이면 처음부터 POST 로 바로 서버 콜함.(권장헤더)
여기에서는 600(10분간 유효), 캐시 유효시간은 아래참고.
Access-Control-Max-Age: <delta-seconds>
<delta-seconds>
Maximum number of seconds the results can be cached, as an unsigned non-negative integer.
Firefox caps this at 24 hours (86400 seconds).
Chromium (prior to v76) caps at 10 minutes (600 seconds). Chromium (starting in v76) caps at 2 hours (7200 seconds).
The default value is 5 seconds.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Max-Age
[위 소스 설명]
플러터(크롬게시) 에서 아래처럼 WebApi를 호출함.
var response = await http.post(url,headers: {"Content-Type": "application/json"}, body: body);
[참고]
프로그램(웹브라우저)에서 호출할 때 아래 3개의 Content-Type 은 Preflight 작동안함.
REQUEST_METHOD: POST 로 전송됨(처음부터)
application/x-www-form-urlencoded
multipart/form-data
text/plain
프로그램(웹브라우저)에서 호출할 때 아래 Content-Type 은 Preflight 으로 자동 작동됨(WebApi 를 연속 2회(왕복) 호출함)
REQUEST_METHOD: OPTIONS 로 전송됨(OPTIONS -> POST 순)
application/json
text/xml
[과정]
1) 웹브라우저에서 RestApi(WebApi) "Content-Type": "application/json" 으로 post 호출하면
아래 헤더값들이 웹브라우저에서 서버로 전달.
HTTP_ORIGIN:"http://localhost:3767"
HTTP_ACCESS_CONTROL_REQUEST_METHOD:"POST"
REQUEST_METHOD:"OPTIONS"2) HTTP_ORIGIN 값(http://localhost:3767)이 서버 $allowed_domains 에 등록되어 있다면,
header('Access-Control-Allow-Origin:' . $httpOrigin);
header('Access-Control-Allow-Credentials: true') 등록하고,
맨 처음 호출될때 REQUEST_METHOD 가 'OPTIONS' 이므로, 아래처럼 OPTIONS 헤더가 등록되고, CORS 체크로직을 종료(exit;)시킴.
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') { //1: OPTIONS -> 2: POST
header('Access-Control-Allow-Methods: GET, PUT, POST, DELETE, OPTIONS');
header('Access-Control-Allow-Headers: origin, content-type, accept, API');
header('content-type: application/json; charset=utf-8');
http_response_code(200); // 처음 OPTIONS 요청메서드로 Access-Control-Allow-Origin 사전 검증받는 역할만 하므로,
exit; //200 리턴해주고, exit; 로 아래 이하 php 스크립트 실행 종료
}
3) 위 1)2)번에서 완성된 헤더를 서버로 부터 웹브라우저가 받음.
이제부터 웹브라우저에서 서버로 실제 데이타 전송 가능해짐
4) 웹브라우저로 부터 HTTP_ORIGIN 헤더를 서버가 받아 보고 서버 검증($allowed_domains)과 일치한 상태이므로
웹브라우저는 아래 헤더들 + 실제 전송데이타와 함께 서버에 post 전송되어 서버 로직을 실행.
HTTP_ORIGIN:"http://localhost:3767"
REQUEST_METHOD:"POST"
CONTENT_TYPE:"application/json; charset=utf-8"
--------------------
Preflight 에서 1차 전송될때 REQUEST_METHOD 가 'OPTIONS' 이고, 2차 전송될때는 REQUEST_METHOD 가 'POST'이므로,
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') 구문으로 2차 전송때는 불필요한 OPTIONS 헤더 등록 로직실행을 하지 않음.
-웹브라우저로 부터 HTTP_ORIGIN 헤더를 받아 본 후 서버 검증($allowed_domains 에 등록되지 않은 경우)과 일치하지 않는 경우는
위 2)번의 아래 헤더 등록 작업을 하지 못한채로, Access-Control-Allow-Origin 값이 없는 미완의 헤더를 웹브라우저가 받음.
header('Access-Control-Allow-Origin:' . $httpOrigin);
header('Access-Control-Allow-Credentials: true')
Access-Control-Allow-Origin 값이 없는 미완의 헤더를 받은 웹브라우저는 CORS policy 를 충족하지 못하여 오류가 나고, 서버 접근이 불가능.
Access to XMLHttpRequest at 'http://192.168.0.3/jp/memberselect' from origin 'http://localhost:3767' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
5) 위 소스와 같이 아래 로직이 필요하다.
Preflight 는 서버에 검증받기 위해 먼저 물어보고, OK되면 실제 post 데이타를 전송하게된다.(왕복 2번)
그런데, 위 로직을 넣지 않으면, 1회 호출때 CORS 체크 로직을 벗어나 빈 데이타를 접근하며 하위 소스를 계속실행하게 됨.
Preflight 는 서버에서 미리 검증하여 미검증 된 HTTP_ORIGIN 접근은 사전에 실행을 차단하는 목적이므로,
CORS 체크로직 아래가 실행될수 없도록 http_response_code(200); exit; 로직을 넣어줌.
[참고 - Slim4 프레임워크 사용시는 index.php 에 아래와 같이 구성하면 됨]
Preflight 위소스 흐름도
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
[참고]
php에서 CORS설정
https://blog.mydepot.kr/20220323_543#gsc.tab=0
Apache 에서 CORS설정
https://sarc.io/index.php/httpd/1278-apache-how-to-cors
- 이전글[DI] Dart Interface 구현 22.12.22
- 다음글[getx] Route management / Parameter / Page Navigation 22.12.18
댓글목록
등록된 댓글이 없습니다.