티스토리 뷰

이 문제때문에 무려 이틀을 헤맸다..ㅠㅠ

 

상황 설명

Github.com에서 사용자가 Repository에 Like를 눌렀을 때, chrome extension의 background.tsx파일에서 우리쪽 서버의 API(GET)를 콜 해주려고 했다. 

그런데 이 API를 콜하면 정상적으로 실행이 되지 않았다. Postman으로 API를 실행하면 정상 동작 했고, 호출하는 URL도 로그를 찍어봤을 때 정상적이었기 때문에 크롬 단에서 차단을 하고 있다는 의심을 하기 시작했다.

크롬 콘솔에서 뜨던 에러

 

해결 방안 모색하기

Chrome의 Network탭에서 확인을 했을 때 ERR_CERT_INVALID 로 나와서 처음에는 https인증문제를 의심했다. 

또한 CORS문제를 의심했었는데, 처음에는 크롬 Network탭에서의 에러 메세지가 Uncaught (in promise) TypeError: Failed to fetch 만 등장했기 때문이다.

 

나를 헷깔리게 했던 net:ERR_FAILED 에러...

 

CORS 문제라고 생각해서 했던 삽질들

너무나도 CORS 문제 같아 보였다. 특히나 CORS 문제 같아보였던 부분이, 기능 중에 inference를 해서 뷰를 보여주는 API는 정상적으로 호출이 되었기 때문이다.

 

HTML 태그를 찾아서 그 위치에 insertBefore js함수를 콜해서 내가 만든 ReactDOM을 render하고 있었다. 그 API는 정상적으로 호출이 되었다.

 

'마음에 들조의 추천' 이라고 쓰여있는 부분이 내가 만든 부분이다. 보다싶이 API에서 내용을 잘 받아오고 있다.

 

contentScript.tsx

find = document.querySelector(".BorderGrid.BorderGrid--spacious") // 클래스들, 컨텐츠들이 묶여있는 가장 상위 클래스임
if (find != null){
  chrome.storage.sync.get(["username"], (res)=> {
    username = res.username ?? null
    if (username != null){
      var myBody = document.createElement("div");
      find.insertBefore(myBody, null)
      var Recommendation: React.FC<{}> = () => {
        return (
          <div>
            <h2>마음에 들조의 추천</h2>
              <RepoCard username={'jonyejin'} ></RepoCard>
          </div>
        )}
      ReactDOM.render(<Recommendation />, myBody)
      }
    })
  }

 

RepoCardView.tsx

const RepoCard: React.FC<{
  username: string
}> = ({ username }) => {
  // 뷰 안에서 data를 track 하기 위해서
  const [repoData, setRepoData] = useState<Repo[]|[]>([])

  // API 호출해주기: 정상 작동 함.
  useEffect(()=> {
    console.log("useEffect 나타남")
    inference(username, 12345)
    .then((data)=>{
      return data.candidate_repos
    })
    .then((data)=> {
      console.log("========")
      console.log(data)
      setRepoData(data)
    })
    .catch(err => console.log(err))
  }, [])

  return(
    <div>
    </div>
  )
}

export default RepoCard

 

HTML은 cross-origin 정책을 따르고 FetchAPI는 same-origin정책을 따른다는 걸 알고 있어서,

계속 CORS가 문제라고 생각을 했었다. 추천하는 API는 임베드한 HTML 뷰에서 콜 되니까 cross-origin이 되고, 내가 콜 하고 싶은 Like API는 FetchAPI로 콜 하니까 same-origin 정책에 의해서 차단당하고 있다고 생각했다.

 

그래서 Chrome extension중 'Allow CORS'를 깔아서 모든 시도를 다 해보았다.

 

 

그래도 안됐다. 내 생각대로라면 github 서버에서 CORS를 허용해주지 않더라도,

브라우저 단에서 허용했다면 분명히 정상적으로 동작해야 하는데....

 

두번째로 해본 시도는 "https://cors-anywhere.herokuapp.com" 를 이용해서 내 API의 콜을 CORS허용 해주었다. 사실 이건 이미 서버에서도 CORS설정을 해주었기 때문에, 그리고 github.com의 리퀘스트를 수정하는건 아니기 때문에 의미가 없는건 알았지만 혹시나! 해서 해보았다.

 

 

여기서부터 멘붕이 왔다.

 

CSP에러 발견하기

 

혹시나 해서 github.com을 실행한 크롬 콘솔에서 fetch를 콜 해봤다. 

그러자 에러가 등장했다.

 

Refused to connect to 'http://127.0.0.1:3000/init/yejin' because it violates the document's Content Security Policy.
CSP 는 XSS 공격을 감지하고 완화하기위한 추가 보안 계층입니다.
스크립트를 실행 가능한 화이트리스트를 만들어 화이트리스트 도메인에서 로드된 스크립트만 실행합니다.
CSP는 스크립트, 스타일 시트, 이미지, 글꼴, 개체, 미디어 (오디오, 비디오), 프레임 및 양식 작업을 포함한 모든 동적 원본에 대해 허용된 원본을 지정할 수 있습니다.
 
CSP 는 Content-Security-Policy HTTP 헤더를 통해 설정됩니다.
 
CORS 와의 차이점은 CORS는 제 3자가 서버에 액세스하는 것을 방지하는 반면 CSP는 XSS에 대한 방어로 웹 사이트 자체가 제3자로부터 콘텐츠를 로드하지 못하도록 차단한다는 것입니다.
 
CSP 는 XSS를 무조건 막는건 아니지만 도움이 됩니다. 궁극적으로 XSS는 프론트 엔드와 백엔드 모두에서 신중한 설계와 이스케이핑을 통해 방지되어야합니다. 하지만 설계 난이도가 복잡하고 구현하기가 가장 어려울 수 있습니다. 그리고 충분한 테스트가 없다면 배포후 중단 될 수 있습니다. 

출처: https://chanto11.tistory.com/67

 

github.com에는 CSP설정이 되어 있는 것 같았다. 

 

하..근데 이건 어떻게 해결하는거지? 감이 잡히질 않았다. 그래서 구글링을 미친듯이 시작했다.

 

오 나랑 비슷한데? 해서 보니까 9년전

여기 적힌 추가사항들도 당연히 확인해봤다.

  • ad-blocker같은 다른 chrome extension들 꺼보기
  • Content Script에서 API 호출하고 있으면 Background에서 하기

여기서 얘기한걸 보니 chrome extension단에서 무언가.. 더 설정을 더 할 수 있는 것 같았다.

 

Documentation을 찾긴 했는데... 2012년 글이다.

 

여기서 How to comply with CSP를 읽어보았다.

 

It's very possible that you are using templating libraries and many of these won't work with CSP. 

 

안된다는건가..? 😱

 

그런데 마지막 항목에 이게 있었다. 

그냥 위에서 뷰를 만들어서 API호출 한 것 처럼 하는게 방법이라는 것...

 

그래서 EmptyView를 만들어서 embed 하는 식으로 해결했다.

 

contentScript.tsx

var myBody = document.createElement("div");
repoidtag.insertBefore(myBody, null)

ReactDOM.render(<EmptyView
	username={username}
	repoid={repoid}
	></EmptyView>, myBody)

 

EmptyView.tsx

const EmptyView: React.FC<{username: string
                         repoid: number}> = ({username, repoid}) => {
  // API 호출해주기
  useEffect(()=> {
    clickedRepo(username, repoid)
      .then((data)=> {
        console.log("clicked Repo 완료")
      })
      .catch(err => console.log(err))
  }, [])

  return(
    <div></div>
  )
}
export default EmptyView

 

이렇게 했더니 잘 됐다. 눈물의디버깅

 

+) Sandbox를 만들어서 해결하는 방법도 있는 것 같다. 이 방법은 잘 알지 못해서, 필요하신 분은 공식 문서를 참조하길 바란다.

https://developer.chrome.com/docs/extensions/mv3/manifest/sandbox/

https://developer.chrome.com/docs/extensions/mv3/sandboxingEval/

 

다들 버그를 잘 해결하길 바라며..

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/10   »
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
글 보관함