❗️OAuth2.0
OAuth2.0 기술이 나오게된 시대적 배경과 개념에 대한 설명 (이해하기 쉽게 너무 설명 잘해줌)
OktaDev 공식 유튜브 영상 - https://www.youtube.com/watch?v=996OiexHze0
인증이라는게 보안과 관련되어 있다보니 복잡하고 발전된 새로운 기술들이 끊임없이 나와서 공부하기 까다로운 분야인것 같다.
브라우저에서 자주 쓰이는 3가지 인증 방법에 대해서 공부해봤다.
아래는 이번에 인증/보완에 대해 공부를 하면서 궁금했던 질문들과 강사님 답변들.
❓질문1
과제에서 배운 리프레쉬 토큰의 목적이 '보안'이 아닌 순전히 ‘로그인 유지 기능’을 구현하기 위한 수단인건지 궁금합니다.
액세스 토큰의 유효기간(예를들어 1일)을 짧게 가져가는 이유가 보안상의 이유라고 하셨는데,
리프레쉬 토큰(7일)은 사용자의 편의성은 높이는 대신에 보안은 저해하는 요소인것 같아서요.
(만약 토큰이 탈취된다면 액세스 토큰이 만료되도 7일 동안은 로그인을 거치지 않고 서비스를 이용할 수 있다는 뜻이니까)
절충안으로 리프레쉬 토큰을 클라이언트에 보관하는 대신 서버나 DB에 따로 보관하는 방법을 말씀해주셨는데, 그렇다 해도 유저가 다시 로그인해서 로그인 정보를 서버로 넘겨주지 않는 이상은, 만료된 액세스 토큰을 들고있는 유저가 적합한(valid) 유저인지 판단할 수 있는 방법은 없지 않나요? 아니면 유저의 적합성을 판단할 다른 방법이 있는건가요?
그렇기 때문에 이 방법 또한 리프레쉬 토큰 자체가 클라이언트 사이드에서 탈취될 위험을 방지할 수 있다는것 외에는, 보안 강화에는 도움이 안되는것 같아서요.
그리고 위의 절충안의 경우에는 만약 액세스 토큰을 브라우저에 쿠키로 전달해 준다면, 액세스 토큰의 만료일은 짧게 설정한다해도, 쿠키의 만료일은 리프레쉬 토큰의 기간만큼 설정해줘야 하나요? 왜냐면 만료된 액세스 토큰이라도 들고 있어야 서버단에서 확인하고 리프레쉬 토큰으로 재발급이 가능하니까?
✅ 강사님 답변: 안녕하세요 00님! 인증 보안이 00님을 많이 괴롭히고 있는 모양이네요😅 답변 드리겠습니다!
리프레시 토큰은 액세스 토큰을 재발급해주기 위한 용도로, 로그인 유지를 위한 것이라고 보는 것이 맞고, 분명히 보안을 저해하는 요소로 작용합니다. 사용자 편의성을 갖추는 대신에 생기는 트레이트 오프인거죠!따라서 서버 내부에서 토큰 기능을 구현할 때, 편의성과 보안을 저울질해서 구현 방식에 조금씩 변화를 줄 수 있습니다.
1. 액세스 토큰만 사용하기
보안이 정말 중요한 서비스라면, 리프레시 토큰 안쓰고 액세스 토큰 무조건 짧게 만들기 (액세스 토큰의 페이로드에서 정보가 유출될 가능성도 있으니, 페이로드에는 최소한의 정보만 담고, 만료되면 토큰이 남지 않고 삭제되도록 해줄 수 있습니다. 저희 과제에서 쿠키 유효기간도 함께 설정해줬던 것처럼요!)
2. 리프레시 토큰을 사용할 때,
2-1. 리프레시 토큰만 있어도 액세스 토큰 재발급해주기
2-2. 리프레시 토큰이 있더라도 만료된 액세스 토큰을 같이 가져와야 재발급해주기 (이럴 경우, 액세스 토큰이 담긴 쿠키를 날려버리면 안되겠죠?)
3. 리프레시 토큰을 서버의 DB에 저장해놓고 사용할 때,
3-1. 토큰을 클라이언트에도 전달해주기
3-1-1. 클라이언트에서 받은 리프레시 토큰을 서버의 리프레시 토큰과 비교하기 (시그니쳐를 사용하는 토큰 검증에 더해, 서버에서 발급해준 바로 그 토큰이 맞는지도 추가로 확인하는 것입니다.)
3-2. 토큰의 id를 만들어서 id만 전달해주기 (세션 id를 전달해준 것 처럼!)
여기에다 2-1, 2-2 방법도 엮어서 사용할 수 있습니다.
이 외에도 여러가지 방식이 있을 것 같은데, 일단 생각나는건 이정도인 것 같아요! 하지만 구현 방식이 어떻든 액세스 토큰을 탈취당하면 서버에서는 막을 길이 거의 없습니다. 정말 중요한 정보에 접근하려고 할 때에는 액세스 토큰이 있더라도 비밀번호를 다시 요구하는 정도일텐데, 결국 작정하고 탈취한 토큰을 악용하려는 사용자를 막으려면, 1번의 방법이 가장 효과적이에요. 또한, 완벽한 보안은 없으니 중요한건 보안과 사용자 편의성의 중요도를 어느정도로 할 것인지 정하고 거기에 맞는 서버를 구현하는 것입니다.
❓질문2
JWT에서 비밀키(솔트)를 이용해 해당 토큰이 유효한지 판단하기 때문에 솔트값이 탈취거나 유출되지 않도록 하는것이 굉장히 중요하다고 하셨는데요.
그러면 실제로는 솔트값을 설정할때 저희 과제에 나온것처럼 ACCESS_SECRET 과 REFRESH_SECRET 값을, 서버 코드를 작성하는 개발자가 알아볼 수 있도록 작성하면 안될것 같은데요. (소스 코드에 솔트값이 보인다는것 자체가 유출될 위험이 생기는 거니까?)
백엔드 개발자 조차 '실제' 솔트값은 알 수 없도록 안전하게 솔트값을 생성하고 사용할 수 있는 방법이 있을까요 아니면 불가능한가요?
( 참고로 공유해주신 과제 레퍼런스에 .env.example 파일이 빠져있어서 .env.example 파일을 생성하고 ACCESS_SECRET, REFRESH_SECRET 변수를 할당해주어야 로그인이 정상적으로 실행되네요! 페어 다함님 감사합니다ㅎ)
✅ 우선은 토큰을 만들때 사용하는 시크릿에 접근할 수 있는 사람을 최소한으로 하는 것이 좋습니다. 예를 들어, 과제에서 사용했던 것처럼 .env로 환경변수를 만들어 사용한다면 소스코드와는 별도의 환경에 시크릿을 분리해놓고 해당 파일에 접근할 수 있는 인원을 제한해야합니다. 그럴 경우, 소스코드에서는 ACCESS_SECERT, REFRESH_SECRET과 같이 실제 시크릿이 아닌 변수명으로만 사용할 수 있으므로 시크릿이 노출되는 일은 없어지고 개발하는 데에도 지장이 생기지 않습니다. 여기에 시크릿에 누가 접근했는지 로그가 남도록 만들면 문제가 생겼을 때 책임소재도 확실히 할 수 있겠죠!
그 외에도 서버의 시크릿을 관리해주는 서비스도 있다고 하는데요. AWS Secrets Manager, Google Cloud Secret Manager, Vault 등이 있다고 합니다!
❓질문3
위와 연결해서 생각난 궁금점이
저희가 보통 프론트에서 폼
폼형식의 디폴트 기능중에 http 요청을 보내기 전에 패스워드를 자체적으로 암호화 시켜주는 기능이 있는건지, 아니면 서버에서 프론트에서 받은 패스워드를 그대로 사용하기 전에 암호화 작업을 해주는건지 궁금합니다.
근데 만약 후자라면 백엔드 개발자가 나쁜 마음만 먹는다면 혹은 실수로 암호화를 안거쳤다면, 유저의 패스워드가 있는 그대로 노출된다는 뜻이니까 너무 위험할것 같아서요. 이런 문제를 시스템적으로 막을 수 있는 방법이 있지 않을까 생각해봤습니다.
그런데 또 패스워드가 ‘암호화를 거친 후에’ 데이터베이스에 저장된다고 해도 그 암호화된 패스워드 조차 유출되면 위험한것 아닌지 궁금하네요. 브라우저의 로그인폼을 거치지 않고도 바로 서버나 데이터베이스에 유저 아이디와 암호화된 패스워드로 http 요청을 보낼 수 있다는 뜻이니까?
인증이라는게 보안과 연관되다보니 고민할수록 더 복잡하게 느껴지는것 같네요
(그리고 만약 유어클래스 자료에 나온것처럼 여러개의 서버 혹은 여러가지 서비스에 동일한 토큰을 사용할 경우 각 서버가 동일한 시크릿키를 가져야 할텐데, 동일한 시크릿키 값을 각 서버에 어떻게 설정할 수 있나요?
혹시 그렇다면 마치 유저의 패스워드를 클라이언트에서 서버에 넘겨주기 전에 해싱으로 암호화 하는 것처럼 ( 이 경우 유저만 실제 패스워드를 알고 있고, 개발자는 실제 패스워드 값이 뭔지 알 수 없음 ))
✅ 비밀번호의 해싱 위치 관련해서는 다음 링크 참고해보시면 좋을 것 같아요.
https://yoonhogo.github.io/blog/2020-09-08/HTTPS-plain-text-safety/
이 링크에서는 서버에서만 해싱하는 것이 보안상 더 이롭다고 판단하고 있는데요.
클라이언트에서 한 번 해싱한 후 서버에서도 해싱을 진행하는 방법도 분명 이점이 있고 실제로 사용하는 서비스도 많습니다.
개발자가 의도를 가지고 악용하는 것에 대해서는 개인정보보호법이 안전망이 되어줄 것 같아요!
❗️이외의 새로 알게된 개념들
1. Cookie
2. Web Storage API (e.g. Local Storage, Session Storage)
3. Service Worker API (e.g. Cache Storage)
❗️웹개발할때 신경써야할 주요 공격들
Cross-site scripting (XSS)
Cross-site request forgery (CSRF)
Access Token 을 local storage 에 저장하면 좋지 않은 이유:
1. 브라우저 탭을 닫아도 계속 유지됨. 보안에 좋지 않아.
2. javascript로 localStorage.setItem() 으로 바로 접근할 수 있음 (때문에 XSS: Cross-site Scripting 에 취약함)
대신 cookie 를 쓰면 위의 문제가 해결될 수 있음
1. 브라우저 탭 닫으면 삭제되게
2. javascript로 접근 못하게 막을 수도 있음 httpOnly
대신 cookie 의 단점은 쿠키는 웹 브라우저밖에서 사용하지 못함. 웹브라우저를 위한 토큰임,
Cross-site request forgery 공격을 받을 수 있음 (이문제는 SameSite 설정을 잘 해주면 되)