안녕하세요. IT 엘도라도 에 오신 것을 환영합니다.
글을 쓰는 것은 귀찮지만 다시 찾아보는 것은 더 귀찮습니다.
완전한 나만의 것으로 만들기 위해 지식을 차곡차곡 저장해 보아요.   포스팅 둘러보기 ▼

웹, 앱 (Web, Application)

[Web] 인증 (Authentication) : 비밀번호 암호화, 세션/토큰 기반 인증

피그브라더 2021. 8. 1. 22:02

1. 인증 (Authentication) vs 인가 (Authorization)

본 포스팅에서 인증에 관한 내용을 다루기 전에, 먼저 인증과 인가의 차이를 알아보자. 먼저, 인증(Authentication)이란 서비스로부터 일정 권한을 부여받은 사용자임을 인증받는 절차를 말한다. 조금 더 간단하게 말하면, 가입된 계정으로 로그인을 하는 절차를 말한다. 다음으로, 인가(Authorization)란 이미 인증받은, 즉 로그인을 한 사용자가 서비스의 특정 기능을 사용하기 위한 권한을 허가받는 절차를 말한다. 즉, 특정 기능을 사용하려 할 때 사용자가 이미 로그인을 한 상태인지, 그렇다면 그 기능을 사용할 권한이 있는지 검증하는 절차를 말한다.

 

[Figure 1] 인증과 인가의 차이

 

그중 본 포스팅에서 다룰 내용은 바로 인증(Authentication)이다. 이는 쉽게 말해서 로그인을 하는 절차라고 하였다. 인증(= 로그인)을 하는 수단으로는 여러 가지가 존재하지만, 가장 일반적인 것은 바로 아이디와 비밀번호를 이용하는 것이다. 즉, 서버는 사용자가 입력한 아이디와 비밀번호에 해당하는 계정 정보를 데이터베이스에서 탐색하고, 탐색에 성공하면 인증(= 로그인)을 성공으로 처리한다.

 

2. 비밀번호 암호화의 필요성

그렇다면 서버는 각 계정의 아이디와 비밀번호를 그대로 데이터베이스에 저장해서 관리하면 되는 것일까? 절대 그렇지 않다. 비밀번호를 평문 그대로 데이터베이스에 저장하는 것은 절대 안 된다. 왜냐하면 데이터베이스의 보안이 뚫렸을 때 모든 사용자의 비밀번호 평문이 유출될 수 있기 때문이다. 사용자는 대개 자신만의 비밀번호를 여러 서비스에서 사용하기 때문에, 한 곳에서 비밀번호 평문이 유출되면 그 피해는 상상 이상으로 심각해진다. 또한, 데이터베이스에 접근이 가능한 내부 인력이 각 계정의 비밀번호 평문을 그대로 조회할 수 있다는 문제점도 존재한다. 따라서 이러한 여러 보안 문제를 방지하기 위해서는 반드시 비밀번호를 암호화하여 저장 및 관리해야 한다. 이는 법적으로도 강제가 되고 있을 정도로 아주 중요한 규칙이다.

제 24조(고유식별정보의 처리 제한)
③ 개인정보처리자가 제1항 각 호에 따라 고유식별정보를 처리하는 경우에는 그 고유식별정보가 분실ㆍ도난ㆍ유출ㆍ위조ㆍ변조 또는 훼손되지 아니하도록 대통령령으로 정하는 바에 따라 암호화 등 안전성 확보에 필요한 조치를 하여야 한다.

 

3. 비밀번호 암호화의 핵심 : 해싱 (Hashing)

자료 구조 및 알고리즘을 공부해봤다면 한 번쯤은 해싱(Hashing)에 대해 들어봤을 것이다. 비밀번호 암호화가 바로 이 해싱을 이용한다. 해싱은 일방향 암호화 기법이다. 즉, 복호화가 불가능한 암호화 기법이라는 것이다. 다음과 같이, 특정 값을 해시 함수(Hash Function)라는 것에 입력으로 넣으면, 출력에 해당하는 해시 값(Hash Value)이 도출된다. 이 해시 값은 Digest 값이라고도 한다.

 

[Figure 2] 비밀번호 해싱

 

이러한 해싱은 몇 가지 특징을 가진다. 먼저, ① 입력 값이 조금만 달라져도 해시 값이 완전히 달라진다. 따라서 입력 값이 비슷하다고 하여 해시 값도 비슷하리라는 보장은 없다. 다음으로, ② 입력 값이 같다면 항상 같은 해시 값이 도출된다. 같은 입력 값에 대해 언제는 이 값을, 언제는 저 값을 도출하지 않는다는 것이다. 마지막으로, ③ 서로 다른 입력 값이 동일한 해시 값을 도출할 수도 있다. 따라서 해시 값을 보고 역으로 입력 값을 정확히 추론하는 것은 불가능하다. 이것이 해싱을 일방향 암호화 기법이라 하는 이유이다.

 

데이터베이스에는 비밀번호 평문이 아닌, 비밀번호를 해싱한 결과인 해시 값을 저장해야 한다. 그리고 서버는 인증 시에 사용자가 입력한 비밀번호의 해시 값이 데이터베이스에 저장되어 있는 값과 동일한지 비교한다. 이 방식을 통해 얻는 이점은 명확하다. 데이터베이스의 보안이 뚫려서 비밀번호의 해시 값이 유출되어도, 그 해시 값으로부터 비밀번호 평문을 바로 알아낼 수 없다는 것이다.

 

4. 해싱을 이용한 암호화의 문제점

하지만 단순히 비밀번호의 해시 값을 저장한다고 해서 안전한 것은 아니다. 이쯤에서 우리는 해싱의 개념을 배웠을 때의 기억을 떠올려 봐야 한다. 아마 해싱은 빠른 탐색을 위한 자료 구조 및 알고리즘을 다루는 시간에 배웠을 것이다. 조금 더 구체적으로 말하자면, 해싱은 O(1)의 탐색 성능을 확보하기 위한 기법으로 배웠을 것이다. 즉, 해싱은 빠른 탐색을 위해 고안된 기법이라는 것이 핵심이다.

 

4-1. 무차별 대입 공격 (Brute Force Attack)

이를 풀어서 얘기하면, 입력 값을 받은 해시 함수가 해시 값을 도출하는 데 걸리는 시간이 아주 짧다는 의미가 된다. 이는 비밀번호를 해킹하려는 해커들에게 아주 쉽게 악용될 수 있는 치명적인 단점이다. 특정 비밀번호 해시 값이 유출된 상황이라면, 해커는 임의의 입력 값을 해시 함수에 넣어서 얻는 해시 값과 그 값을 빠른 속도로 비교하면서 비밀번호를 해킹할 수 있기 때문이다. 이를 무차별 대입 공격(Brute Force Attack)이라고 한다.

 

4-2. 레인보우 테이블 공격(Rainbow Table Attack)

또한, 조금 더 수월한 해킹을 위해 해커는 레인보우 테이블(Rainbow Table)이라는 것을 만들기도 한다. 이는 여러 개의 임의의 입력 값에 대한 해시 값을 사전에 계산해둔 테이블이다. 이렇게 한 번 레인보우 테이블을 만들어 두면, 해커는 유출된 비밀번호 해시 값과 테이블에 존재하는 해시 값들을 빠른 속도로 비교하여 매칭 되는 행을 탐색한다. 만약 매칭 되는 행이 발견된다면, 그 행의 비밀번호를 이용하여 해킹에 성공할 수 있게 되는 것이다. 이를 레인보우 테이블 공격(Rainbow Table Attack)이라고 한다.

 

일반적으로 사용자는 그렇게까지 길고 복잡한 비밀번호를 사용하지 않는다. 그렇게 하면 본인도 까먹기 쉽기 때문이다. 이는 곧 해커가 해킹을 위해 시도해볼 임의의 입력 값들의 범위가 줄어든다는 것을 의미한다. 이런 상황에서 위와 같은 수법으로 해킹을 시도하면 생각보다 어렵지 않게 해킹이 가능하다. 그리고 이렇게 해킹에 성공을 하면, 해당 서비스에서 같은 비밀번호를 가진 다른 사용자까지 해킹을 당할 수 있다는 문제점 또한 존재한다. 따라서 이러한 취약점을 보완할 또 다른 방법이 필요하다.

 

5. 해싱을 이용한 암호화의 문제점 보완 방법

그렇다면 위와 같은 취약점은 어떻게 보완할 수 있을까? 그 보완 방법으로는 아주 여러 가지가 존재하겠지만, 여기서는 가장 대표적인 보완 방법 두 가지만 소개한다. 그러나 실제로는 이 두 방법을 혼합한 방식도 있고, 다른 방법들도 여러 가지 있음을 기억하자.

 

5-1. Key Stretching

해싱을 통해 얻어낸 해시 값을 한 번 더 해싱하고, 그 결과에 해당하는 해시 값을 한 번 더 해싱하는 과정을 여러 번 반복하는 기법을 말한다. 이는 해싱에 걸리는 시간이 아주 짧다는 문제를 보완한 것이다. 이를 통해 해커가 여러 개의 임의의 입력 값들에 대해 해시 값을 계산하는 과정이 빠른 시간 내에 일어날 수 없도록 방지한다. 즉, 무차별 대입 공격을 어느 정도 방어할 수 있다.

 

5-2. Salting

해싱할 때 입력 값으로 비밀번호 평문뿐 아니라 해당 사용자의 고유한 Salt 값을 함께 넣는 기법을 말한다. 즉, 비밀번호 평문과 Salt 값을 입력으로 삼아 해시 값을 얻도록 하는 해시 함수를 사용하는 것이다. 이름이 의미하는 것처럼 Salt 값이라는 소금을 치는 것이다. 일반적으로 Salt 값은 사용자마다 다른(= 고유한) 임의의 문자열로, 데이터베이스에 저장이 된다. 따라서 인증 과정은 사용자가 입력한 비밀번호 평문과 데이터베이스에 저장되어 있는 Salt 값을 해싱한 해시 값이 데이터베이스에 저장된 해시 값과 동일한지 비교하는 식으로 이뤄진다.

 

[Figure 3] Salting을 이용한 비밀번호 해싱

 

Salt 값의 존재는, 해커가 미리 만들어둬야 하는 레인보우 테이블의 크기가 그 전과는 비교도 할 수 없을 만큼 커진다는 것을 의미한다. 왜냐하면 각각의 행이 (비밀번호 평문, 해시 값)의 쌍으로 되어 있는 테이블보다는 (비밀번호 평문, Salt 값, 해시 값)의 쌍으로 되어 있는 테이블이 당연히 클 수밖에 없고, 더군다나 Salt 값은 비밀번호 평문과 달리 무식하게 길이가 긴 임의의 문자열이기 때문이다. 따라서 Salting을 통해 레인보우 테이블 공격을 어느 정도 방어할 수 있다.

 

※ 비밀번호 암호화를 위한 대표적인 해시 함수
  • PBKDF2 : Django에서 사용하는 해시 함수로, Salt 값 적용 및 해싱의 반복 횟수 지정이 가능함.
  • bcrypt : Key Stretching과 Salting을 구현한 해시 함수로, 언어별로 라이브러리가 존재함.
  • scrypt : 무차별 대입 공격 시 병렬 처리를 어렵게 하기 위해, 해싱 시 메모리 오버헤드를 갖도록 구현한 해시 함수.

 

6. 두 종류의 인증 방식 : 세션 기반 vs 토큰 기반

지금까지 인증을 위한 비밀번호의 안전한 저장 및 관리를 위한 방법에 대해 알아보았다. 그렇다면 이제는 사용자가 아이디와 비밀번호를 입력했을 때 서버가 그 인증 과정을 어떻게 처리하는지에 대해 알아보자. 이는 곧 서버가 인증 과정을 처리하는 두 가지 구현 방식을 소개한다는 것을 의미하고, 더 쉽게 표현하자면 로그인을 구현하는 두 가지 방식을 알아본다는 것을 의미한다. 하나는 로그인된 사용자의 정보를 서버 단에 저장하는 세션 기반 인증, 다른 하나는 클라이언트 단에 저장하는 토큰 기반 인증이다.

 

6-1. 세션 기반 인증

사용자가 아이디와 비밀번호를 입력하여 로그인을 요청하면, 서버는 그 정보를 데이터베이스의 정보와 비교하여 올바른 계정인지 검사한다(= 인증). 만약 올바른 계정이라면, (메모리 혹은 데이터베이스 등의 저장 공간에) 세션이 생성되고 그 세션 안에 해당 사용자의 정보가 저장된다. 그러고 나서 해당 세션의 ID를 클라이언트에게 응답하여 세션 쿠키로 저장하게 한다. 그러면 그 이후부터는 클라이언트가 요청을 보낼 때마다 해당 세션 ID가 쿠키로 서버에 전송된다. 이를 통해 서버는 요청을 보낸 사용자가 누군지 식별할 수 있게 되어(= 인가), 로그인 상태가 유지된다. 이후 만약 사용자가 로그아웃을 요청하면, 서버는 해당 세션을 삭제한다.

 

6-2. 토큰 기반 인증

사용자가 아이디와 비밀번호를 입력하여 로그인을 요청하면, 서버는 그 정보를 데이터베이스의 정보와 비교하여 올바른 계정인지 검사한다(= 인증). 만약 올바른 계정이라면, 해당 사용자의 정보와 서버의 비밀 키를 이용하여 토큰을 발급하고 이를 클라이언트에게 응답한다. 그러면 클라이언트는 해당 토큰을 쿠키, 로컬 스토리지, 세션 스토리지 중 하나에 저장한다. 이후 클라이언트는 권한이 필요한 요청을 보낼 때마다 해당 토큰을 서버에 전송한다. 이를 통해 서버는 그 토큰을 보고 해당 사용자가 누군지 식별할 수 있게 되어(= 인가), 로그인 상태가 유지된다. 이후 만약 사용자가 로그아웃을 요청하면, 클라이언트는 저장소(쿠키, 로컬 스토리지, 세션 스토리지 중 하나)에서 해당 토큰을 삭제한다. (다만 실제 서비스에서는 로그아웃 요청 시 해당 토큰을 더 이상 어디에서도 사용할 수 없도록 서버의 데이터베이스에 기록을 해둘 필요가 있다. 예를 들면 토큰의 블랙 리스트 같은 것을 데이터베이스에서 관리하는 것이다.)

 

인증 완료 후 서버가 클라이언트에게 응답하는 세션 ID 혹은 토큰은 로그인 상태의 유지를 위한 수단이다. 즉, 이미 한 번 인증이 완료되었다면 매번 아이디와 비밀번호를 다시 입력할 필요가 없도록 하는 것이다. 그런데 세션 기반 인증과 토큰 기반 인증은 그러한 로그인 상태 유지 수단이 약간 다르다. 세션 기반 인증은 세션 ID를 사용하고, 토큰 기반 인증은 토큰을 사용하기 때문이다. 물론 각각의 방식에는 장단점이 있다. 이와 관련된 자세한 내용은 인가(Authorization)에 대해 다룬 이 포스팅의 내용을 읽어보기 바란다.

 

 

 

 

 

 

본 글은 아래 링크의 내용을 참고하여 학습한 내용을 나름대로 정리한 글임을 밝힙니다.

https://gxone.github.io/blog/authentication/

https://www.youtube.com/watch?v=1QiOXWEbqYQ&t=6s