로그인과 인증
로그인과 인증 시스템을 만들때 필요한 기본 내용들을 간략히 정리해본다.
A. 로그인, 인증(Authentication), 인가(Authorization)
경우에 따라서 혼용되곤 하나, 3개의 단어는 다른 의미를 가진다. 순차적으로 처리되는 거라고 봐도 된다.
로그인이라고 하면 대체로 아이디와 패스워드 등을 입력해서 인증 토큰이나 세션을 받는 행위 자체를 말한다.
로그인이 성공한 다음에 로그인된 유저로서 접근을 시도하는건 조금 별개의 일이다.
인증은 바로 그 다음 스텝을 말한다.
로그인을 통해서 받아온 토큰&세션 쿠키를 들고 서버에 접근하는 일련의 행위 자체를 인증이라고 말한다.
혹은, 맥락에 따라서 인증은 로그인을 포함해서 말할 수도 있다.
근데, 유효한 인증을 들고 있다고 해서 모든게 해결되는게 아니다.
일반 유저 인증을 가지고 있다고 해서 관리자 기능에 접근을 한다거나 하는게 말이 되겠나?
그래서 인증 통과 이후에, 인증된 값과 User 데이터 등을 기반으로 해서 권한 검증을 하는 것을 인가라고 말한다.
보통 모놀리하게 설계된 시스템들은 인증과 인가 로직이 명확하게 구분되지 않은 채로 만들어지는 경우도 많다.
인가를 처리하는 방법론은 크게 RBAC, ABAC 정도가 있다.
일반적인 비즈니스 서비스 수준에서는 ABAC 정도로도 충분하다.
https://blog.naver.com/sssang97/223431386470
B. 세션이냐 토큰이냐?
로그인 - 인증 방식에는 크게 2가지의 방식이 있다.
요즘은 jwt 토큰 기반의 인증이 트렌드가 된 분위기이긴 한데, 각자 장단점이 분명하니 결국 상황에 맞추어 선택하는 것이 중요하다. 토큰 방식도 단점이 많다.
1. 세션 기반의 인증
세션 기반의 인증은 서버가 인증 정보를 계속해서 들고있는 형태를 말한다.
일반적인 세션 기반의 인증 기법은, 서버가 세션 정보를 생성하고 그걸 클라이언트에게 쿠키의 형태로 주는 식으로 처리된다.
https://joie-kim.github.io/Session-Auth/
서버는 자신의 인메모리나 기타 저장소에 세션을 저장하고, 클라이언트가 세션 쿠키를 들고서 요청을 할때마다 자신이 들고있는 세션과 대조해서 일치하면 통과시킨다.
가장 고전적인 인증 기법이고, 어떻게 보면 가장 단순하면서 보안적으로도 강력한 구조라고 할 수 있다.
1.1. 세션 클러스터링
세션을 그냥 기본 설정으로만 쓴다면, 서버는 보통 세션을 인메모리에 저장한다.
근데 이러면 2가지 문제가 발생한다.
- 서버가 재배포되거나 다운되면 세션이 다 날라간다.
- 서버를 여러개 띄워서 스케일아웃한 경우에는 세션 정보가 분산된다. A에는 세션이 있는데, B에는 없는 그런 끔찍한 현상이 발생할 수 있다.
그래서 세션 환경을 제대로 구성한다고 하면, 머신 인메모리에 쓰는게 아니라 중앙화된 인메모리 DB나, 인메모리처럼 쓸 수 있는 Key-Value DB에 세션을 저장하는 것이 권장된다.
이를 세션 클러스터링이라고 부른다. 세션 저장소로는 보통 Redis를 많이 쓴다.
사실 이름만 거창하지 별건 없다.
1.2. 장점
-
구현하기 쉽다. 상당수의 프레임워크에서는 자동화된 세션 처리와 모듈들을 제공한다.
-
가장 강력한 보안 수준을 보장한다. 서버에서 항상 세션 정보를 들고 중앙화된 형태로 관리하기 때문에, 보안 문제가 생기면 언제든 세션을 삭제하거나 조정해서 접근을 차단할 수 있다.
1.3. 단점
-
서버에 부하가 집중되기 쉽다. 항상 서버가 세션 정보를 들고 있어야 하기 때문에 이런데서 비용이 좀 발생한다.
-
세션 클러스터링을 통해서 세션 중앙화를 구성하더라도, 세션 저장소(Redis)가 병목과 장애 지점이 된다. 관리 포인트가 늘어나는 셈이다.
-
MSA 환경이나, 여러 서버에 공유되는 인증시스템을 만들기 어렵다. 토큰 기반의 인증에서는 토큰 값 자체를 저정하거나 전달하며 사용하기 편하나, 세션 기반 환경에서는 그러기 어렵다. 못하는건 아니지만 상당히 불편할 것이다.
2. 토큰 기반의 인증 (JWT)
토큰 기반의 인증 기법은 세션의 대안으로 제시되기 시작한 인증 방법론이다.
기본적인 원리 자체는 특별할 것이 없다.
일단 여기서는 Secret Key라는 것을 먼저 두고 시작한다.
이 키는 절대 유출되어서는 안되며, 서버만 접근할 수 있어야 한다.
클라이언트가 로그인에 성공하면 서버는 키를 기반으로 해서 인증 토큰을 발행한다. 이걸 보통 Access Token이라고 말한다.
이 Access Token은 암호화된 값과 원본 값을 Pair로 엮어서 만들어진다.
원본 값은 암호화되어있지 않아서 누구나 볼 수 있기 때문에, 간단한 유저 정보 정도는 집어넣어도 된다.
하지만 대체로는 유저를 식별할 수 있는 user_id 정도만 넣어도 충분하다.
그리고 클라이언트가 다시 토큰을 들고 접속을 시도하면, 서버는 다시 Key를 가져다가 토큰의 원본 값과 암호화된 값 쌍이 일치하는지를 확인한다. 서버 외에는 Key를 알지 못하기 때문에 토큰의 위변조는 거의 불가능하다.
이 검증 과정에서 통과되면 인증 자체는 문제가 없는 것이다.
그 이후에는 서버가 토큰에서 user_id 같은걸 꺼내다가 인가를 마저 처리하곤 한다.
Access Token은 별다른 저장공간을 점유하지 않고, 오직 Key에 의해서만 검증이 이루어지기 때문에 서버 부하나 관리포인트가 적다는 장점이 있다.
2.1. Refresh Token
근데 JWT, Access Token에는 심각한 보안 수준의 문제가 있다. 한번 만들고 나면 통제할 수가 없다는 것이다.
만료를 12시간 정도 주고 발급해버리면, 그 동안에는 악성 공격자가 토큰을 탈취해서 나쁜짓을 하더라도 서버 입장에서 뭔가 제한을 가하는 것이 불가능하다.
그래서 보안 수준을 신경쓴다고 하면 JWT만 쓰는게 아니라 세션의 개념을 뒤섞어서 인증시스템을 구축한다.
이를 Refresh Token이라고 부른다.
서버는 로그인에 성공하면 클라이언트에게 Access Token과 Refresh Token를 발행해준다.
Access Token은 1-2시간이나 몇십분 정도로만 유지되게 하고, Refresh Token이라는 일종의 세션을 통해서 클라이언트가 직접 Access Token을 재발급받을 수 있게 하는 것이다.

Access Token은 이전처럼 서버가 저장하거나 관리하지 않지만, Refresh Token은 DB 같은 곳에 저장하면서 상태를 관리한다.
이러면 Access Token 탈취의 위험을 최소화하고 서버 부하를 줄이면서도, Refresh Token을 통해서 통제를 어느 정도 가져갈 수 있다는 장점이 있다.
하지만 그럼에도 불구하고 단점은 여전히 존재한다.
- Access Token의 만료시간을 아무리 짧게 잡더라도, 그게 살아있는 동안에는 여전히 악용 위험이 있으며
- 결국 Refresh Token을 세션처럼 저장하는 것이기 때문에 관리포인트가 생긴다는 점이다.
사실 보안을 완벽하게 잡고 가야 하는 환경이라면 그냥 금융권처럼 세션 온리로 가는게 맞다.
2.2. 장점
-
서버의 부하를 줄일 수 있다.
-
MSA 등 여러 서버가 같은 인증을 공유해야 하는 경우에 매우 적합하다.
-
민감하지 않은 평범한 User 정보 같은 것들은 JWT에 박아서 뿌려버리면 가벼운 저장공간으로도 활용할 수 있다.
2.3. 단점.
-
완벽한 보안을 잡는 것이 불가능하다.
-
Refresh Token을 사용할 경우에는 세션 방식보다 관리포인트가 많고 구현이 복잡해질 수 있다.
구현례
https://blog.naver.com/sssang97/222275298165
C. 패스워드
로그인을 구현할 때 중요한 것 중 하나가 바로 패스워드다.
이것도 고려할게 꽤 많다.
1. 패스워드 해싱
패스워드는 유출을 막기 위해서 암호화해서 저장하는 것이 기본이다.
https://stytch.com/blog/what-is-password-hashing/
혹시라도 누가 보안을 뚫고 접속해서 이렇게 패스워드를 털어버리면, 그 이후에도 마음대로 접속할 수 있는 것이기 때문이다. 게다가 이렇게 한번 유출된 패스워드는 초장기적으로 고객들에게 피해를 입힐 수 있다.
인간 심리상 대부분의 사용자는 패스워드를 여러 서비스에 돌려쓰기 때문이다...
https://stytch.com/blog/what-is-password-hashing/
그래서 정상적인 시스템의 서버들은 패스워드를 암호화해서 저장한다.
다만 패스워드의 암호화는 일반적인 암호화와는 다르다.
단방향 암호화, 해싱을 사용하기 때문에 한번 해싱된 패스워드는 다시 원본 패스워드로 바꿀 수 없다.
어차피 내용을 알아야 하는게 아니라 해싱해서 해싱된 결괏값이 같은지만 판별하면 되기 때문이다.
근데 패스워드를 아무걸로나 해싱한다고 해서 원본 패스워드를 알아낼 방법이 없는 것은 아니다.
레인보우 테이블이라고 해서 노가다로 원본->해시값 테이블을 만들면 얼마든지 역추적을 할 수 있기 때문이다.
그래서 해싱 알고리즘은 적당히 느린 것을 권장한다. 너무 빠르면 뚫기가 쉬워지기 때문이다.
현재 보안그룹에서 권장하는 패스워드 해싱 알고리즘은 argon2id다. bcrypt 같은 것도 이제는 퇴물된지 오래다.
https://blog.naver.com/sssang97/223139433184
2. 클라이언트에서 해싱하지 말것
해싱은 클라이언트에서 하는게 아니라 서버에서 하는 거다.
그냥 하는 소리가 아니라, 패스워드 유출을 방지한답시고 뭣모르고 이렇게 하는 사람을 봐서 하는 말이다. 심지어 꽤 경력이 있었다.
HTTPS에 쓰는 TLS 레이어는 괜히 있는게 아니라, 클라이언트->서버 간의 통신을 암호화하기 위해 있는 것이다.
3. 패스워드 솔트(salt)
패스워드 유출을 막기 위한 또 하나의 방법 중 하나다.
패스워드를 그냥 해싱하면 레인보우 테이블에 의해서 뚫릴 가능성은 항상 존재한다.
패스워드 salt는 salt라는 임의의 값을 집어넣어서 패스워드 역추적이 더욱 어렵도록 하는 것이다.
https://stytch.com/blog/what-is-password-hashing/
이 salt라는 값은 user마다 따로 할당하고, 별개의 저장소에 저장하는 것이 가장 바람직하다.
모든 user에 적용되는 global salt를 쓸 경우에는 salting을 하는 의미가 좀 떨어진다. 그거 하나만 어떻게 더 찾으면 바로 뚫리는 것이니까 말이다.
D. 기타 팁
자잘한 팁들이나 권장사항에 대해서 정리한다.
1. 실패에 대해서 친절하게 알려주지 마라
초보 개발자나 기획자가 자주 하는 실수가 있는데, 로그인이 실패할때마다 뭘 알려주려고 한다는 것이다.
로그인에 실패할 때마다 "아이디가 틀렸습니다.", "패스워드가 틀렸습니다."라고 알려주는 것은, 아이디가 맞으니까 패스워드만 바꿔서 더 해보라고 공격자에게 친절한 힌트를 주는 것이다.
그냥 네이버처럼 이렇게
얼버무려서 틀렸다고만 말해주는게 가장 바람직한 구조다.
2. 브루트포스 대책
서버를 좀 운영하다보면, ID나 패스워드를 무제한으로 밀어넣으면서 해킹을 시도하는 "브루트 포스" 공격을 마주할 경우가 잦다. 심지어 유출된 이력을 들고서 찌를 때도 있어서 진짜 피해자가 속출할 경우도 잦다.
여기에 대해서는 사실 명쾌한 해결법은 없지만, 최소한의 조치는 해두면 좋다.
- 로그인 시도 차단하기
rate limit 등을 걸어서 로그인을 몇번 이상 실패하면 더 이상 시도하지 못하게 차단해버리는 것이다.
다만 이것도 VPN이나 IP 돌려쓰면서 작정하고 찌르면 답이 없다.
클라우드 서비스들의 WAF를 쓰면 봇이나 DDOS를 어느정도 쳐낼 수 있지만, 한계가 있다.
- 국가 제한걸기
보통 공격이 들어오는 국가는 정해져있는 편이다, 북한이나 러시아 같은 나라들...
이런 몇몇 요주의 국가들만 트래픽을 잔뜩 막아놔도 꽤 쾌적함을 느낄 수 있다.
다만 글로벌 서비스라면 어려울 수 있다...
3. 특수문자 제한은 도움이 되지 않는다.
회원가입을 할때 패스워드 제한을 빡세게 걸어놓은 사이트는 꽤 흔하게 볼 수 있다.
길이 제한은 괜찮은데, 특수문자 제한은 불편함만 가중시키고 보안적 이득은 없는 안티 패턴에 가깝다.
어차피 대부분 @나 ! 같은 특수문자 한두개만 앞뒤에 붙이는 정도로 만들어서, 추측이 매우 쉽기 때문이다.
특수문자 룰을 처음 주장했던 bill burr도 그 주장을 후회한다고 말했다.
https://www.bbc.com/news/technology-40875534
차라리 패스워드 글자수가 길어지는게 보안 강도는 더 높고, 2단계 인증이나 지역 차단, 로그인 감지 같은 보조 매커니즘을 사용하는걸 권장한다.
참조
https://stackoverflow.com/questions/1219899/where-do-you-store-your-salt-strings