Notice
Recent Posts
Recent Comments
Link
«   2025/03   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31
Archives
Today
Total
관리 메뉴

heyday2024 님의 블로그

[첫주차 팀프로젝트] 이어서 프로젝트 진행 중 생긴 문제와 해결방안 2 본문

프론트엔드 부트캠프

[첫주차 팀프로젝트] 이어서 프로젝트 진행 중 생긴 문제와 해결방안 2

heyday2024 2024. 10. 7. 12:16

공휴일(10/3)을 지나 이어서 members 페이지 작업을 끝내고,팀 github 계정에 push했다.그러나 갑자기 팀원분들 컴퓨터에 나의 members 페이지가 랜더링되지 않는 문제가 생겼다.

 

 

CORS 문제

내 로컬환경에서는 잘 나오는데, 다른 분이 나의 코드를 가져와서 화면에 띄우니까 CORS 문제가 생김

 

  • 일단 CORS를 이해하려면,  출처의 기본 개념동일 출처 정책(same-origin policy)를 알아야함.
    1. 출처(origin)는 크게 3가지 요소로 결정됨. 두 url이 같으려면 아래 세가지 요소가 모두 일치해야함.
    2. 동일출처 정책(SOP)은 웹 브라우저 보안 모델의 중요 요소로, 서로 다른 출처에서 온 리소스 간의 상호작용을 제한하는 정책!
      •  보통 한 사이트에서 주소가 다른 서버로 요청을 보낼 때, 자주 접하게되는 오류. 주로 API로 정보를 받아오기 위해 프론트에서 HTTP 요청을 보냈을 때, 미리 어떤 설정을 해주지 않으면 CORS 문제로 막히게됨.
      • 왜 SOP가 필요할까?
        • 예를 들어 A라는 웹사이트에 내가 로그인을 해서 접속했고, 이 사이트를 계속 이용하기 위해 다시 로그인을 하기 번거로우니까 보통 다시 접속했을 때 자동으로 로그인이 되도록, 내 브라우저에 토큰 등의 정보가 쿠키로 저장되게끔 한다. 이 저장된 쿠키는 로그인 했던 A 사이트에 접속할 때 마다 요청에 실어보내면 그 사이트에서 이 쿠키를 보고 내가 거기 로그인이 돼 있다이렇게 판단함.  그런데 해커들이 악의적인 목적으로 나에게 A와 비슷하게 생긴 B사이트를 만들어 그 링크가 담긴 메일이나 그럴싸한 게시물로 나를 유인해서 내가 그 사이트에 접속한다면, 그 해커들이 만든 HTML, CSS, JS가  내 브라우저에 받아지게됨. 그렇게되면, A라는 사이트로부터 나의 개인정보를 조회하는 요청에 크롬과 같은 내가 사용하는 브라우저에 저장된 나의 개인정보가 담긴 토큰을 실어서 A사이트에 보낸 다음, 그것으로 탈취한 정보를 B사이트의 서버로 보내버릴 수 있게됨.  
          • 위와 같은 보안 문제를 방지하고자, 브라우저는 어떤 사이트에서 다른 사이트로는 요청이 못 가게끔 SOP, 동일 출처 정책을 만들게됨!!!!
          • SOP는 말 그대로 동일한 출처들끼리만 API 등의 데이터 접근이 가능하도록 막는 것!!!
        • CORS는 SOP와 반대 개념으로 SOP에 의해 다른 출처들끼리 정보 요청을 못하는 상황을 해결하기 위한, 다른 출처간에 리소스를 공유할 수 있도록 하는 것. 
          • 웹 생태계가 다양해지면서 여러 서비스간에 자유롭게 데이터가 주고받아질 필요가 생겨서 CORS라는 개념이 생김(원래는 SOP에 의해 다른 출처간에 정보 요청, 전달 등이 어려워서 예전에는 JSONP 등의 방식으로 이를 우회하는 꼼수들을 사용함. 이것을 합의된 출처들간에 합법적으로 허용해주기 위해 어떤 조건을 충족시키면 리소스 공유가 되도록 만들어진 매커니즘이 CORS임.)
          • 그럼 CORS를 가능케하는 그 조건은? 요청을 받는 백엔드쪽에서 이걸 허락한 다른 출처들을 명시해두면 됨.
            • 백엔드 서버를 프로그래밍할 때 쓰는 프레임워크들(Spring, Django, Express 등)의 문서를 살펴보면 CORS 옵션을 넣는 방법들이 쉽게 마련되어있음.

다시 정리하자면,

  • CORS(Cross-Origin Resource Sharing), 교차 출처 자원 방식:
    • 웹 브라우저에서 다른 출처 (origin) 간의 리소스 요청을 제어하는 보안 매커니즘. 웹 애플리케이션이 한 출처에서 로드된 리소스를 이용해 다른 출처의 리소스에 접근할 때 발생하는 문제를 해결하는 데 사용됨.
    • 다른 출처의 서버로부터 이미지와 폰트를 긁어온 경우 (Access-Control-Allow-Origin에 해당 출처 없다면 CORS 오류 생길 것임.)

갑자기 궁금해진 CORS의 동작방식:

 

- 서버가 브라우저에게 다른 출처에서 온 요청을 허용할지 말지 명시적으로 알려주는 방식으로 작동함.

- 서버는 특정 헤더 (HTTP headers)를 통해 어떤 출처의 요청을 허용할지 설정함.

 

동작 방식 예시, 

 

1) A 사이트에서 네이버 지도 API로 요청 보냄.

(다른 출처로의 요청임으로 cross-origin 요청임)

 

2) 브라우저는 이처럼 다른 출처끼리의 요청이 보내질 때 요청에 origin이라는 header를 추가함.

(header: 데이터가 다른 곳으로 전송될 때 데이터의 맨 앞쪽에 붙은 보충 정보)

(주로 받는 쪽의 IP 주소, 사용할 프로토콜이나 옵션 등이 담김.)

 

(origin 에는 요청하는 쪽의 scheme과 도메인, 그리고 포트가 담김)

 (scheme은 프로토콜로, 요청한 자원에 접근할 방법을 말함:  http, ftp, telnet 등)

 

3) 이 요청을 받은 네이버 지도 API 서버는 답장의 헤더에 지정된 Access-Control-Allow-Origin 정보를 실어서 보냄.

(Access-Control-Allow-Origin :서버가 요청을 허용할 출처를 지정)

 

4) 만약 A 사이트가 요청 허용 출처로 등록된 상태면 Access-Control-Allow-Origin에 그 URL이 들어있을 것임.

 

5) 그럼 크롬과 같은 브라우저는 Origin에서 보낸 출처값이 서버의 답장 헤더에 담긴 Access-Control-Allow-Origin에 똑같이 있으면( 두 출처 URL이 일치하면 ),  안전한 요청으로 간주하고 응답 데이터를 받아올 수 있음.

 

(만약 일치하지 않으면 아래 이미지와 같은 COSE 에러 메세지를 띄움!!)

내 로컬환경에서는 잘 나오는데, 다른 분이 나의 코드를 가져와서 화면에 띄우니까 CORS 문제가 생김

 

6) 참고로! 만약, 토큰 등 사용자 식별 정보가 담긴 요청에 대해서는 더욱 엄격함. 

 

크게 두가지 방식이 있음!!1. simple request: GET이나 POST 등 일정 조건의 요청들에 사용.- 일단 보내는 측에서는 요청의 옵션에 credentials 항목을 true로 세팅해야함.- 받는 쪽에서도, 아무 출처나 다 된다는 와일드카드가 아니라 보내는 쪽의 출처, 웹페이지의 주소를 정확히 명시해야함.

fetch('https://api.example.com/data', {
    method: 'GET',  // Simple Request
    headers: {
        'Accept': 'application/json'
    }
});

 

2. preflighted request: PUT과 DELETE 등 다른 요청들은 본 요청을 보내기 전에 Preflight 요청이란 것을 먼저 보내서 본 요청이 안전한지 확인 후 여기서 허락이 떨어져야 본격적으로 요청을 보낼 수 있음.(아무래도 서버의 데이터에 영향을 주는 명령어들이라 요청자체를 보내기 전에 먼저 허용 여부를 검증하는 것!!)

fetch('https://api.example.com/data', {
    method: 'POST',  // Preflight Request
    headers: {
        'Content-Type': 'application/json',  // 커스텀 헤더 사용
        'Authorization': 'Bearer token'      // 사용자 정의 헤더
    },
    body: JSON.stringify({ key: 'value' })
});

 


 

- 나의 궁금증... (POST도 서버를 변경시킬 수 있는 명령어인데, 얘는 왜 simple request?)

 

ChatGPT 왈: 

DELETE, PUT, PATCH와 같은 메서드는 보통 서버 자원의 직접적인 삭제, 수정 등을 포함하는데, 이러한 메서드는 잠재적으로 보안에 더 큰 영향을 미칠 수 있습니다. 따라서 이러한 요청은 기본적으로 CORS preflight가 필요합니다.

반면, POST는 일반적으로 폼 제출과 같은 데이터 전송 시 가장 많이 사용되며, 역사적으로 그렇게 큰 보안 문제가 발생하지 않아서 Content-Type이나 헤더가 단순한 경우 preflight를 요구하지 않습니다.

이제 CORS가 뭔지 알겠어요...  그래서 어떻게 해결하는데요..?

아래 해결방법은 나의 상황과 무언가 맞지 않는 방식이었다.

CORS 문제 해결 방법
1. 서버에서 허용할 출처를 명시적으로 설정
서버에서 Access-Control-Allow-Origin을 설정하여, 요청을 허용할 출처를 지정해야 합니다.
2. 모든 출처 허용 (*) 설정 서버가 모든 출처에서의 요청을 허용할 경우, Access-Control-Allow-Origin을 *로 설정할 수 있습니다. 하지만 보안상 권장되지는 않습니다.
3. 프록시 서버 사용 CORS 문제를 해결하기 위해 프록시 서버를 설정해 클라이언트가 동일한 출처에서 리소스를 요청하는 것처럼 보이게 할 수 있습니다. 프록시 서버는 클라이언트와 실제 서버 사이에 위치하여 요청을 중계합니다.

 


다행히 팀원분이 Chatgpt에서 해결방안을 찾아 제안하셨다.

 

firestore에서 정보를 가져오는 코드 HTML에 inlining 하기

나는 html 파일에 script 태그로 관련 코드를 옮겼고, 문제는 해결되었다.

 

하지만,

 

어떻게 해결이 된걸까??

 그리고 뭔가 ㅎㅎ 야매같은 이 해결법이 맞는 걸까...? ㅋㅋㅋㅋㅋ

 

그래서 튜터님께 이에 대해 질문하였고, 

답을 찾아냈다!!!!

 

튜터님께서는 inlining 하는 방법으로 해결하는 것은 썩 좋지 않은 방법이라고 하셨고, 다른 방법을 제안하셨다.

 

우선,


inlining으로 해결된 이유를 생각해보았다.


문제의 상황을 다시 뜯어보자면, 에러 메세지는 이렇다.

 

from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, isolated-app, chrome-extension, chrome, https, chrome-untrusted.

 

VSCode에서 html을 open with live server로 열면 각각의 html, css, js 파일을 하나의 주체로 판단한다고 한다.

즉, 로컬 html을 그냥 클릭하고, module 타입인 js파일을 같은 하나의 어플리케이션으로 판단하지 않는 것이다.

html과 js가 서로 다른 어플리케이션으로 판단하고, 그 js에서 정보를 가져오려니,

SOP 정책을 어기게 되어서 CORS 에러가 난 것이다.

 

inline 스크립트 태그로 js 코드를 html에 합쳐주면, html은 하나의 어플리케이션으로 역할하고,

이로 인해 firebase 관련 코드도 정상 작동한다.

 

하지만, 애초에 문제는 vsCode에서 페이지 렌더링 하는 방식이 잘못되었던 것이다.

확장에 liver Server를 깔아주고 vsCode 우측 하단에 Go live로 html, css, js 파일을 하나의 어플리케이션으로 빌드해주면,

해당 코드가 담긴 각각의 파일을 하나의 출처로 인식함으로, 

CORS 에러가 나지 않는다.

 

 

튜터님 감사합니다 😉

 


 

CORS개념정리와 문제 해결 방안에 대해 생각하는데만 시간을 무진장 쏟았다...

아.. 나는 아직 갈 길이 멀구나...

 

사실 firebase 관련 코드를 html이나 js에 넣을 때 사용자들이 개발자 도구로 볼 수 있지 않을까하는 생각이 들었고.

이러한 보안 문제를 어떻게 처리할 지 궁금했다.

 

아래와 같은 방식으로 보안을 강화할 수 있다.

 

 

  • Firebase 인증 사용: 사용자 인증을 통해 데이터에 접근할 수 있는 사용자를 제한. Firebase Authentication을 통해 사용자의 로그인 상태를 관리함.
  • 데이터베이스 규칙 설정: Firestore 또는 Realtime Database의 보안 규칙을 설정하여 어떤 사용자가 어떤 데이터에 접근할 수 있는지 정의함. 예를 들어, 사용자가 인증되어야만 특정 데이터에 접근할 수 있도록 설정할 수 있음.
  • 환경 변수 사용: API 키와 같은 중요한 정보를 환경 변수로 관리하여 클라이언트 코드에 직접 노출되지 않도록 함.
  • 서버 측 로직 구현: 비즈니스 로직이나 민감한 데이터 처리에는 서버 측 코드를 사용하여 클라이언트에 민감한 정보를 노출하지 않음. Firebase Functions와 같은 서버리스 아키텍처를 활용할 수 있음.
  • HTTPS 사용: 모든 요청은 HTTPS를 통해 이루어지도록 하여 데이터 전송 시 보안을 강화함.

 

하지만, 이에 대해 너무 깊이 들어가면 내용이 또 너무 길어질 것 같고, 
본캠프 내에서도 나중에 더 자세히 다룬다고 하니 오늘은

이만 여기까지 정리하려고 한다.

 

이어서 다음 포스트는 깃허브 관련 문제를 다룰 예정이다.