본문 바로가기
생각정리

보안에 대한 오늘의 고찰(GraphQL, 파일다운로드)

by for2gles 2021. 7. 14.
반응형

오늘은 RSTEAM CMS 제작 중 보안에 대한 의견차이로 인해 대표님과 투닥투닥하는 일이 있었다.

 

일단 논점은 회사에서 자체적으로 제작하고 있는 CMS에 들어가는 파일 다운로드 문제이다.

 

우리 회사에서 제작하고있는 CMS는 Backend 에서는 NestJS + GraphQL, Frontend에서는 React를 활용하고, JWT 토큰을 통해 User 를 식별한다.

 

따라서 프론트엔드에서 어떤 데이터를 받기 위해 요청을 하기 위해서는 Apollo Header에 JWT 토큰을 입력해서 전송을 해줘야 하는 것이다.

 

다만 예외적인 부분이 있다. GraphQL특성상 파일 업로드를 할 수 없고, 정해놓은 router를 통해 접속했을 때 검증을 진행하고, 해당 파일정보를 res.send 해주는 작업이 불가하다.

 

오늘 개발 회의의 이슈는 파일 업로드와 파일 다운로드에 보안을 어떻게 적용시킬지에 대한것이었다.

 

첫번째로 업로드의 경우는 사실 간단하게 보안할 수 있다.

NestJS 에서는 기본적으로 위 사진과 같은 굉장히 비합리적인 업로드 구조를 가진다.

 

업로드 로직에 대한 설명은 이번글에서 중요한것이 아니므로 나중에 따로 작성 해 보도록 하고,

 

이 업로드에서의 보안은 외부 Postman이나 다른곳에서 POST를 통해 해당 URL로 파일업로드를 시도하면, 그 파일을 일단 업로드를 하게되고, 그 이후 Controller 에서 이 사람이 실제 업로드 권한이 있는지에 대해 확인을 하게 된다.(어찌나 비합리적인가)

 

하지만 간단하게 업로드는 CSRF 방식의 보안을 채택하여 해결하기로 결정했다.

 

두번째로 다운로드가 가장 큰 문제였다.

 

다운로드 로직은 이러했다.

위와같은 구조로 AuthGuard 에서 req.headers를 확인하여 획득한 mem_idx 를 사용하여 Controller 내부에서 권한 파악 한 후 다운로드가 가능한 구조이다.

우리 서비스에서는 메인 업로드 컨트롤러, 메인 다운로드 컨트롤러를 통해 사이트 내부 모든 업로드/다운로드, 권한체크를 담당한다.

 

이렇게 되면 게시글 내용에 작성된 이미지같은경우는 

 

<img src="https://test.com/download/fds51f65fdsf15f1d5s6f"/>

 

와 같은 형태를 띄거나 "https://test.com/download/fds51f65fdsf15f1d5s6f"같은 URL에 접속하여 다운로드 로직을 타게 된다.

이 말인 즉슨 GET 형태로 직접 요청을 하기 때문에 header 에 authorizaition 토큰값을 넣을 수가 없다.

 

그래서 Controller에서 설정한 권한 확인 로직을 태울 수 없는 문제가 있었다.

 

이렇게 하여 JWT를 불가피하게 포기를 하고 세션+쿠키 방식으로 돌아가자는 결론이 나오게 되었다.

 

굉장히 무난한 결론이다. 다만, 문제는 이게 끝이 아니었다.

 

뒤에 계시던 대표님이 게시글 이미지에 들어가는 파일에 조차도 권한확인을 하냐는것이다.

 

대표님은

"어차피 게시글 페이지에 들어왔다면 이미 권한확인이 1회 진행된건데 이미지가 20개가 등록된 게시글이면 권한 확인을 21번이나 진행을 해야하는게 불합리적이다."

 

나는

"보안이라함은 다다익선이다. 21번이던 100번이던 중요한 것이 아니고, 중요한 이미지일 경우 외부로 이미지던 파일이던 URL이 유출될경우 해당 파일이 외부인에게 노출되선 안된다."

 

이렇게 대표님과 나는 의견이 대립하여 갈등이 생겼다.

 

대표님께서는 파일은 당연히 권한체크를 하는게 맞다고 하셨고, 이미지까지 굳이 권한체크를 또 진행하는것이 아니라 다른방법으로 외부유출을 막아야 하는게 아니냐는 논리로, 이미지 외부링크 차단방법을 제시하셨다.

 

하지만 나는 분명히 외부링크를 차단한다고 하더라도 해당 방법이 외부유출을 모두 방지하는것이 아니고,

URL을 직접 쳐서 해당 이미지 URL로 접속할경우 권한이 없는사람도 해당 이미지를 볼 수 있을테고,

외부링크를 차단하지만 해당 이미지 URL을 가지고 모든사람이 볼 수 있는 게시판에 해당 이미지를 올리면 당연히 노출될 수 밖에 없기 때문에 이 방법은 완전차단방법이 아니라고 생각을 하였다.

구글드라이브에 올라가있는 파일을 URL만 안다고 권한없는 사람이 볼 수 있는가

 

대표님과 나는 의견이 1시간이 지난 후에도 좁혀지지 않았고, 대표님께서 다른 CSR에서는 어떻게 적용되어있는지 확인해보자 하셨고

리액트의 대표 선두주자인 페이스북에서 어떻게 처리했는지 보자고 하셨다.

 

테스트 방법은 이러하다.

  1. 페이스북에 나만보기 권한으로 이미지 하나를 등록한다.
  2. 해당 이미지의 주소를 복사한다.
  3. 해당 브라우져와 세션이 연동되지 않는 시크릿창, 카카오톡으로 해당 URL을 보내 모바일에서 접속 등을 통해 테스트를 한다.

 

위 테스트 결과는 아래와 같다.

???????

 

나는 당연히 접속이 안될줄 알았다.

이유는 모르겠는데 그냥 접속이 된다.

7월 14일 오후 10시 30분에 올렸다. 언제까지 확인할 수 있을지 모른다.

 

https://scontent-ssn1-1.xx.fbcdn.net/v/t1.6435-9/213068247_2318246911644645_8986761550594175535_n.jpg?_nc_cat=103&_nc_rgb565=1&ccb=1-3&_nc_sid=730e14&_nc_ohc=nNU-jiL5nbgAX-ihUQ-&_nc_ht=scontent-ssn1-1.xx&oh=1fb0f4853a7ad5d5792143606d401c20&oe=60F3AA2A 

 

나참 어이가 없다... 어이가 없어서 이미지 컨트롤러에서 권한체크를 모두 빼버렸다.

 

페이스북의 결과를 보고 사실 혼란이 왔다.

 

'내가 과한것인가? 내가 너무 결벽증처럼 완벽주의자처럼 모든걸 처리하려고 하는것인가? 내가 잘못된건가?'

 

한참을 생각해 본 끝에 나온 결론은 이렇다.

 

보안이라는것은 내가 생각할때 가치관 차이인것같다.

 

회사에서 코딩을 하다보니 보안에 취약하게 SQL 인젝션, XSS방어의 시도조차 하지 않은 정말 보안을 하지 않은 코드도 보았고, Frontend 에서 javascript 를 통해 보안체크만 하고 백엔드에서는 체크하지 않는 등의 코드들을 수도 없이 보았다.

나는 정말 이해할 수 없었다.

분명히 이렇게 코딩을 하면 정말 쉬울 수 있다. 다만 이렇게 퀄리티가 떨어지는 코딩을 하는것이 누구를 위한것이고 어떤 이득이 있을까

분명 의뢰한 회사도 손해이고, 납품한 회사도 손해이다.

 

나는 제한해야한다면, 완전히 차단해야하는 옳다고 생각하는 성격이다. 구글드라이브와 같이 권한이 없을 경우 수도없이 권한체크를 해서라도 차단을 해야하는것이다.

효율을 생각한다면 그다지 좋은 방법은 아닐 수 있다.

 

 

ps.

하지만 타협점이란 분명히 필요하다.

예를들자면 타입스크립트를 사용할 때, 타입스크립트를 완전히 잘 쓰기위해서 무조건 any를 쓰면 안되는것인가?

Socket.io 에 Socket Type을 확인해보면, Custom으로 데이터를 넣을 수  있는부분에 data라는 key 가 있는데 이부분은 any로 되어있다. 

Socket.io에서 무슨수로 우리가 Custom 데이터로 어떤것을 넣을 줄 알고 Type을 지정해놓을까?

분명 어느정도 타협점은 필요하다라고 생각한다.

반응형

댓글