ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 유저별 세션 관리 작업을 진행하며 배운 점 🔧
    CS/네트워크 2022. 6. 23. 20:14


    사내 어드민 페이지는 서비스를 사용하고 있는 유저들에 대한 정보를 조회, 관리하고 운영에 필요한 여러 가지 작업을 하는 공간이기 때문에 보안에 더더욱 신경 써야 한다. 현재 다니고 있는 회사의 어드민 페이지는 회사 네트워크 혹은 vpn을 통해서만 접근할 수 있기 때문에 어느 정도의 보안 수준은 충족한다. 그러나 유저별 세션 정보를 관리 안 하고 있었기 때문에 어드민 페이지에 한 번 로그인하면 로그인 상태가 유지되었으며, 로그인한 유저가 어떤 작업을 했는지에 대한 기록도 남지 않았다. 만약 유저가 악의적인 마음을 품고 데이터를 이상하게 변경한다거나 혹은 외부인이 사용할 수 있는 PC에 vpn을 설치하고 어드민 페이지에 로그인한 뒤, 로그아웃 하지 않았다면 보안적으로 문제 될 여지가 있다. 이런 문제점을 수정하기 위해 유저 세션 관리 작업을 진행했고 이 과정에서 알게 된 것들과 기억할만한 것, 그리고 자잘한 이슈들에 대해 기록하고자 이번 포스팅을 하기로 했다.


    아주 간략한 플로우

    (예외 처리나 암호화 같은 내용은 설명에서 제외!)
    1. 로그인
    [클라이언트 👦] 로그인 요청 →
    [서버 🤖] 계정 확인 → 세션 아이디 생성 → 세션 정보 DB 저장 → 쿠키에 세션 아이디 담아 응답 →
    [클라이언트 👦] 응답 수신

    2. 로그인한 사용자가 페이지에서 작업을 진행하는 경우
    [클라이언트 👦] 서버에 요청 →
    [서버 🤖] 쿠키에 담긴 세션 유효성 확인 (expire_date, 세션과 연결된 계정 정보 등) → 세션 검증 후 요청에 대한 응답 →
    [클라이언트 👦] 응답 수신


    토큰과 쿠키+세션 중에 어떤 것을 사용하는 게 더 적합할지 고민했는데, 현재 구축된 시스템에 기반한 보안적인 측면과 주어진 작업시간을 고려했을 때 쿠키+세션 조합으로 작업을 진행하는 게 더 적합하다고 판단했다.

    http://expressjs.com/en/4x/api.html#res.cookie
    res.cookie('쿠키명', '쿠키값', { 
        httpOnly: true, 
        expires: new Date(Date.now() + 900000), 
        maxAge: 900000
     });

    Node.js의 웹 프레임워크 중 하나인 Express는 클라이언트에 응답을 보낼 때 응답 값에 key-value 형태로 쿠키를 세팅할 수 있게 기능을 제공하고 있으며 해당 쿠키에 대해 다양한 옵션을 부여할 수도 있다. 여러 가지 옵션 중 httpOnly, expires, maxAge 옵션을 사용했다. expires와 maxAge 모두 쿠키 삭제 시점을 지정할 수 있는 옵션인데 차이점은 expires는 Date 타입으로 만료일을 지정하고 maxAge는 milliseconds 단위로 쿠키 유지 시간을 설정한다. (2009년도에 작성된 글에서는 expires옵션은 거의 모든 브라우저에서 지원되지만 maxAge 옵션은 IE 특정 버전에서 지원되지 않는다고 했다. 현재까지도 IE에서 지원 안 하는지는 모르겠지만, 혹시 몰라 두 옵션을 모두 사용했다!) httpOnly 옵션은 클라이언트단에서 해당 쿠키값에 접근을 허용할지에 대한 여부로, Express 공식문서에서 true로 설정하는 것을 권장한다. 실제로 httpOnly: true로 설정한 뒤 클라이언트에 쿠키값을 반환하면, 해당 쿠키는 document.cookie로 확인 불가하며 오직 서버단에서만 확인할 수 있다.

    로그인 시 클라이언트에 전달한 쿠키는 요청 헤더에 담기며, 클라이언트에게 요청을 받은 서버는 요청 헤더에 담긴 쿠키의 세션 아이디를 검증한다. 검증이 통과되면 로그를 남긴 후 요청을 처리하며 검증이 통과되지 않으면 검증 실패에 대한 응답 값을 반환한다. 클라이언트에서는 세션 아이디 검증 실패 값을 받으면 로그아웃 처리 후 로그인 페이지로 리다이렉트 시킨다. (⭐️ 서버 응답 값 핸들링하는 부분이 캡슐화되어 있었기 때문에 캡슐화되어있는 코드 1곳에만 리다이렉트 처리하면 됐음)

    그리고 자동 로그인 기능도 적용했다. 자동 로그인 기간을 얼마나 설정해야 할지는 사람마다 기준이 다르겠지만 보안을 위해 비교적 짧게 설정했고 이후 사용자들이 불편하다고 하면 기간을 늘리는 방향으로 생각하고 있다.

    세션 관리 작업을 통해 개선된 내용은 1) 로그인한 사용자에게 고유한 세션 아이디를 부여했기 때문에 어드민 페이지에서 해당 사용자가 어떤 작업을 했는지 추적할 수 있다는 점, 2) 쿠키&세션 만료일 지정을 통해 일정 시간이 지나면 자동 로그아웃돼 보안 강화가 되었다는 점이다.


    배포한 뒤 생긴 문제

    1. 로컬 환경에서 테스트를 마친 뒤 개발 서버에 배포했는데 어드민 페이지에 세션 검증 실패 문구가 노출되고 다른 콘텐츠가 안 뜨는 문제가 발생했다. 새로 설치한 패키지 반영하고 빌드도 정상적으로 됐는데 왜 이러지 하고 이것저것 확인한 결과, 문제는 서버의 루트 파일의 미들웨어 순서에 있었다. '세션 검증 부분 → 빌드 코드 처리' 순으로 나열돼 있었기 때문에 서버에서는 클라이언트로부터 세션 아이디를 포함한 쿠키를 받지 못하여 세션 검증 실패에 대한 값을 반환한 것이다. 그래서 '빌드 코드 처리→ 세션 검증 부분' 순서를 변경해 다시 배포했고 정상적으로 동작했다. 다음에는 이런 실수 하지 말자...
    2. 3일 뒤에 다른 이슈가 발생했다. 특정 서버에서 모니터링이나 운영 자동화와 관련된 어드민 api를 주기적으로 호출하는데, 특정 서버 → 어드민 서버로 curl을 통해 api 호출했을 때 세션 검증에 실패한 것이다. 총 3개 방안을 생각했는데, 첫 번째 방식: 화이트리스트 방식처럼 특정 api를 호출할 때는 세션 검증을 패스하는 것. 두 번째 방식: 호출 서버의 host ip를 통해 해당 ip에서 요청이 들어오면 세션 검증을 패스하는 것. 세 번째 방식: curl로 호출 시 헤더에 특정 key를 적용하는 것. 관리적 측면으로 봤을 때 세 번째 방식이 유리해보여 별도의 key를 DB에서 관리하고, 특정 서버로부터의 어드민 api호출 or 포스트맨과 같은 툴을 이용한 어드민 api 테스트 시 헤더에 key를 담아 사용하기로 했다.


    신입 기술 면접 준비할 때 쿠키, 세션, 토큰에 대해 달달 외웠던 기억이 나는데 업무에서 직접 다뤄보니 그 당시 이해가지 않았던 내용이 이해됐다. 역시 나는 몇 번 해봐야 습득하는 듯 🥲...


    댓글

jaejade's blog ٩( ᐛ )و