데이터와 보안 ·

로그인 확인과 기능 권한 확인을 서로 다르게 설계하는 법

먼저 답부터

인증은 로그인 정보로 사용자가 누구인지 확인하는 과정이고, 인가는 그 사용자가 특정 자료를 읽거나 고칠 권한이 있는지 판단하는 과정입니다. 로그인에 성공했어도 모든 기능을 쓸 수 있는 것은 아니므로 서버는 요청마다 대상과 행동에 대한 권한을 확인하고 기본값은 거부로 둬야 합니다.

로그인 확인과 기능 권한 확인을 서로 다르게 설계하는 법 대표 이미지

로그인 화면을 만들고 나면 서비스 보안이 거의 끝난 것처럼 느껴질 수 있습니다. 하지만 로그인 성공은 “이 사용자가 누구인지 확인했다”는 뜻일 뿐, 모든 자료를 읽고 모든 버튼을 누를 수 있다는 허가가 아닙니다. 이 둘을 나누는 말이 인증과 인가입니다.

회사 건물 출입을 생각해 보세요. 안내 데스크에서 신분증을 확인하는 일은 인증에 가깝고, 확인된 직원이 서버실 문을 열 수 있는지 판단하는 일은 인가에 가깝습니다. 신분이 확인된 사람이라도 맡은 업무에 따라 들어갈 수 있는 방은 다릅니다.

인증은 누구인지 확인하는 과정이다

authentication은 인증이라는 뜻으로, 사용자가 주장하는 신원이 맞는지 확인하는 과정입니다. 이메일과 비밀번호, 일회용 코드, 보안키 같은 인증 수단을 사용할 수 있습니다. 로그인에 성공하면 서버는 이후 요청을 같은 사용자와 연결할 세션을 만들 수 있습니다.

인증은 실명 확인과도 다릅니다. 이메일 주소를 소유했다는 사실을 확인해 서비스 계정을 구분할 수 있지만 그 계정이 현실의 특정 사람임을 항상 증명하는 것은 아닙니다. 서비스가 실제로 필요한 신원 수준을 먼저 정하고 불필요한 주민번호나 신분증 자료를 수집하지 않습니다.

인가는 해도 되는 행동인지 확인한다

authorization은 인가 또는 권한 확인이라는 뜻으로, 확인된 사용자나 시스템이 요청한 작업을 수행해도 되는지 판단하는 과정입니다. 일반 사용자는 자기 글을 수정할 수 있지만 다른 사람 글이나 관리자 설정은 바꾸지 못하게 하는 규칙이 예입니다.

요청 사용자: 회원 7
대상 게시물 소유자: 회원 7
행동: 수정
판단: 허용

대상 소유자가 회원 12라면 같은 로그인 상태라도 거부해야 합니다. “로그인했는가”와 “이 자료를 수정해도 되는가”를 별도 질문으로 확인해야 하는 이유입니다.

화면에서 버튼을 숨기는 것만으로 보호되지 않는다

관리자 버튼을 일반 사용자 화면에서 숨기면 실수는 줄일 수 있습니다. 그러나 브라우저 개발자 도구나 직접 만든 요청으로 숨은 주소를 호출할 수 있으므로 보안 경계가 되지는 않습니다. 서버가 요청을 받은 뒤 현재 사용자, 대상 자료, 행동을 기준으로 권한을 검사해야 합니다.

POST /posts/42/delete
Cookie: session=...

이 요청에서 서버는 세션이 유효한지만 볼 것이 아니라 게시물 42의 소유자와 삭제 권한을 확인해야 합니다. 주소의 숫자를 바꿨을 때 다른 사람 글이 지워진다면 화면 디자인 문제가 아니라 서버 인가 결함입니다.

요청마다 권한을 다시 확인한다

처음 관리자 페이지에 들어갈 때 한 번 확인했다고 이후 요청을 모두 믿으면 안 됩니다. 사용자의 역할이 바뀌거나 세션이 탈취될 수 있고, 같은 화면에서도 조회와 다운로드와 삭제의 위험이 다릅니다. 서버의 모든 보호된 기능에서 권한을 확인해야 합니다.

  • 목록 조회 전에 볼 수 있는 범위를 확인합니다.
  • 상세 조회 전에 그 자료에 접근할 권한을 확인합니다.
  • 수정 전에 현재 소유자와 허용 필드를 확인합니다.
  • 삭제 전에 별도 권한과 영향 대상을 확인합니다.
  • 관리자 기능은 주소와 메서드가 달라도 같은 정책을 적용합니다.

OWASP는 기본적으로 접근을 거부하고 명시적으로 허용된 경우만 통과시키는 방식을 권합니다. 새 기능에 권한 조건을 빼먹었을 때 자동으로 공개되는 구조보다 자동으로 막히는 구조가 안전합니다.

역할과 소유권을 함께 본다

role은 역할이라는 뜻으로 관리자, 편집자, 일반 회원처럼 권한 묶음에 이름을 붙인 것입니다. 역할만 보면 간단하지만 실제 서비스에서는 자료의 소유자, 조직, 시간, 상태 같은 조건도 필요합니다. 편집자라도 다른 조직의 비공개 문서를 열 수 없고, 주문 담당자라도 이미 환불된 결제를 다시 처리하면 안 될 수 있습니다.

권한 표에는 “누가 어떤 대상에 어떤 행동을 할 수 있는가”를 적습니다. 관리자라는 한 단어로 끝내지 말고 회원 목록 읽기, 역할 변경, 결제 환불, 게시물 삭제처럼 행동 단위로 나눕니다. 권한을 추가할 때는 필요한 최소 범위만 열고 사용하지 않는 권한은 회수합니다.

오류 응답에도 정보 경계가 있다

로그인하지 않은 요청에는 보통 인증이 필요하다는 안내를 주고, 로그인했지만 권한이 없는 요청에는 허용되지 않는 행동임을 알립니다. 다만 비공개 자료가 실제로 존재하는지 알려 주는 것 자체가 문제가 될 수 있어 서비스에 따라 같은 404 응답을 선택하기도 합니다.

사용자 화면에는 다음 행동을 쉬운 말로 안내하고, 내부 로그에는 요청 식별자와 거부된 정책을 남깁니다. 비밀번호, 세션 값, 전체 개인정보를 로그에 기록하지 않습니다. 권한 오류를 해결하려고 모든 사용자에게 관리자 역할을 주는 것은 가장 위험한 임시 조치입니다.

권한 검사는 테스트로 경계를 확인한다

정상 사용자 한 명의 성공만 보면 결함을 놓치기 쉽습니다. 허용돼야 하는 요청과 거부돼야 하는 요청을 짝으로 테스트합니다. 사용자 A가 자기 글을 읽고 수정하는 경우, 사용자 B의 글을 읽거나 수정하려는 경우, 로그인하지 않은 경우, 역할이 회수된 경우를 각각 확인합니다.

테스트 데이터는 실제 고객 정보를 복사하지 말고 가짜 계정과 자료를 사용합니다. 운영 환경에서 권한을 시험한다며 다른 사용자의 주소 번호를 바꾸는 행동은 개인정보 접근 시도가 될 수 있습니다. 승인된 테스트 환경과 범위에서만 수행합니다.

오늘 바로 해볼 작은 실습

코드를 실행하지 않고 서비스 기능 하나의 권한 표를 만듭니다.

  1. 일반 회원, 편집자, 관리자 세 역할을 세로로 적습니다.
  2. 글 읽기, 자기 글 수정, 다른 글 수정, 글 삭제를 가로로 적습니다.
  3. 허용과 거부를 각각 표시하고 이유를 한 줄로 씁니다.
  4. 화면에서 버튼을 숨기는 조건과 서버에서 검사할 조건을 따로 적습니다.
  5. 로그인하지 않은 요청과 권한이 없는 요청의 안내 문장을 만듭니다.
  6. 허용 테스트 두 개와 거부 테스트 세 개를 작성합니다.

인증 라이브러리나 권한 미들웨어를 잘 모르는 상태에서 직접 암호 방식이나 관리자 우회 코드를 만들지 마세요. 비밀키와 세션 값은 채팅, 화면, 저장소에 붙이지 않고, 권한 오류가 나도 검사를 끄거나 역할을 일괄 승격하지 않습니다. 누가 영향을 받는지 확인할 수 없으면 변경을 멈추고 보안 담당자나 서비스 문서를 확인합니다.

확인한 자료

함께 읽기

  • 환경변수와 API 키를 서랍과 금고처럼 구분하는 법

    환경변수는 주소나 실행 모드처럼 프로그램 밖에서 바꿀 설정을 담는 서랍이고, API 키는 외부 서비스 사용 권한을 가진 비밀번호에 가깝습니다. 비밀키는 브라우저 코드나 Git 저장소에 넣지 말고 서버의 비밀 저장소에서만 읽도록 해야 합니다.

  • 쿠키와 세션과 브라우저 저장소를 보관함 위치로 구분하기

    쿠키는 브라우저가 보관하며 조건에 맞는 서버 요청에 함께 보낼 수 있는 작은 값이고, 세션은 보통 서버가 로그인 상태를 보관하고 쿠키에는 연결용 식별자만 두는 방식입니다. localStorage는 같은 출처의 브라우저에 남지만 요청에 자동 첨부되지 않으므로 화면 설정에는 쓸 수 있어도 비밀키 저장소로 보면 안 됩니다.

  • SQL로 데이터를 만들고 찾고 고치고 지우는 첫 연습

    SQL은 데이터베이스에 말을 거는 문법입니다. INSERT는 새 행을 넣고 SELECT는 조건에 맞는 행을 읽으며 UPDATE는 기존 값을 고치고 DELETE는 행을 지웁니다. 변경 명령은 WHERE 조건이 빠지면 범위가 커질 수 있으므로 같은 조건을 SELECT로 먼저 확인해야 합니다.