토큰 기반 인증방식에 대해 살펴보기 전에 서버 기반 인증방식에 대해 살펴볼려고 합니다.
서버 기반 인증방식 일 경우에는 서버 측에서 유저들의 정보를 기억하고 있어야했습니다.
저번 시간에 세션은 서버측에 저장한 다는 것에 대해서 살펴봤습니다. 유저의 수가 엄청 많다면 이 세션도 엄청 많아집니다. 이는 곧 서버해야할 처리 용량이 늘어나는 것을 의미합니다. 처리해야할 용량이 늘어나니, 분산 시스템을 설계해야하는데 설계를 하는 과정에 비용, 처리 과정에서 시간이 추가적으로 걸립니다.
이러한 문제점에 대한 대안으로 토큰 기반 인증이 나오게되었습니다
토큰 기반 인증방식에는 claim기반 토큰과 랜덤 값 기반 토큰이 있습니다.
토큰은 client에 저장하기 때문에 stateless하며, 서버를 확장하기에 적합한 환경을 제공합니다. 만약에 세션을 서버측에 저장했고 서버를 여러대 사용하여 요청을 분선하였다면, 어떤 유저가 로그인 했을 때, 그 유저는 처음 로그인했던 서버에만 요청을 보내야만 설정을 해야합니다.
클라이언트가 서버에 요청을 보낼 때, 더 이상 쿠키를 전달하지 않음으로 쿠키를 사용함으로 인해 발생하는 취약점이 사라집니다.
Scalability는 서버를 확장하는 걸 의미하는 반면, Extensibility는 로그인 정보가 사용되는 분야를 확장하는 걸 의미합니다. 토큰을 사용하여 다른 서비스에서도 권한을 공유할 수 있습니다.
Json 포맷을 이용해서 사용자에 대한 속성을 저장하는 Claim 기반의 Web Token입니다. JWT는 토큰 자체를 정보로 사용하는 Self-Contained 방식으로 정보를 안전하게 전달합니다.
JWT는 Header, Payload, Signature의 3부분으로 이루어지며, Json 형태인 각 부분은 Base64로 인코딩되어 표현됩니다. 또한 각각의 부분을 이어주기 위해 .
구분자를 사용하여 구분합니다.
어떤 종류의 토큰인지, 어떤 알고리즘으로 sign 할지가 적혀있습니다.
{
"alg": "H256",
"typ": "JWT"
}
어떤 정보에 접근 가능한지에 대한 권한을 담을 수도 있고, 사용자의 유저 이름등 필요한 데이터는 이곳에 담아 sign 시킵니다.
토큰의 페이로드에는 토큰에서 사용할 정보의 조각들인 클레임(Claim)이 담겨져 있습니다.
클레임은 총 3가지로 나누어지며, Json(Key/Value) 형태로 넣을 수 있습니다.
{
"sub": "someInformation",
"name": "phillip",
"iat": 151623391
}
{
"iss": "velopert.com",
"exp": "1485270000000",
"https://velopert.com/jwt_claims/is_admin": true,
"userId": "11028373727102",
"username": "velopert"
}
등록된 클레임은 토큰 정보를 표현하기 위해 이미 정해진 종류의 데이터들로, 모두 선택적으로 작성이 가능하며 사용할 것을 권장합니다. 또한 JWT를 간결하게 하기 위해 key는 모두 길이 3의 String입니다. 여기서 subject로 unique한 값을 사용하는데, 사용자 이메일을 주로 사용합니다.
공개 클레임은 사용자 정의 클레임으로, 공개용 정보를 위해 사용됩니다.충돌 방지를 위해 URI 포맷을 이용하며, 예시는 아래와 같습니다
{
"https://tistory.com": true
}
비공개 클레임은 사용자 정의 클레임으로, 서버와 클라이언트 사이에 임의로 지정한 정보를 저장합니다.
{
"token_type": access
}
토큰을 인코딩하거나 유효성 검증을 할 때 사용하는 고유한 암호화 코드입니다. 서명은 위에서 만든 헤더와 페이로드의 값을 각각 BASE 64로 인코딩하고, 인코딩된 값을 비밀 키를 이용해 헤더에서 정의한 알고리즘으로 해싱하고, 이값을 다시 BASE64로 인코딩하여 생성합니다.
base64로 인코딩된 첫 번째, 그리고 두 번째 부분이 완성되었다면, 원하는 비밀 키(암호화에 추가할 salt)를 사용하여 암호화합니다. base64 인코딩 한 값은 누구나 쉽게 디코딩할 수 있지만, 서버에서 사용하고 있는 비밀키를 보유한 게 아니라면 해독해 내는데 엄청난 시간과 노력이 들어갈 겁니다.
HMACSHA256(base64UrlEncode(header) + '.' + base64UrlEncode(payload), secret);
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
토큰이 탈취될 때를 대비하여, JWT를 처음 발급 할 때 Access Token 과 Refresh Token을 함께 발급하고 토큰의 만료시간을 짧게 합니다.
Refresh Token은 Access Token을 Refresh 해주는 것을 보장하는 토큰입니다.
Refresh Token은 다음과 같이 동작합니다.
Access Token을 통한 인증 방식의 문제점은 제 3자에게 탈취당할 경우
npm i jsonwebtoken
모듈을 먼저 설치합니다.
jwt 인증에 사용할 비밀키를 환경변수에 등록해줍니다. 비밀키는 소스코드에 입력해도 되지만, 외부에 노출되지않도록 환경변수에 등록해 관리하는게 좋습니다,
.env
파일에 JWT 인증에 사용할 비밀키를 입력해줍니다.
ACCESS_SECRET=비밀키 설정
REFRESH_SECRET=비밀키 설정
토큰 생성을 위해서는 sign이라는 메서드를 사용합니다.
let jwt = require("jsonwebtoken"); //모듈을 호출합니다,
require("dotenv").congfig()
router.get("/login", function(req,res,next){
// default : HMAC SHA256
let token = jwt.sign({
email: "foo@example.com" // 토큰의 내용(payload)
},
process.env.ACCESS_SECRET, // 비밀 키
{
expiresIn: '5m' // 유효 시간은 5분
})
models.user.find({ //데이터 베이스
where: {
email: "foo@example.com"
}
})
.then( user => {
if(user.pwd === "1234"){
res.cookie("user", token);
res.json({
token: token
})
}
})
})
module.exports = router;
jwt.sign()
- sign() 메서드는 기본값으로 HMAC SHA256 알고리즘을 사용합니다.
- 첫 번째 인자에는 payload, 내용을 작성합니다.
- 두 번째 인자에는 비밀키를 전달합니다. 환경변수에 등록해놓았다면
process.env.ACCESS_SECRET
이 될 것입니다.- 세 번쨰 인자는 토큰에 대한 정보를 객체로 전달합니다.
- 네 번째 인자는 콜백함수를 작성합니다.\
토큰 유효성확인은 verify
를 사용합니다.
https://oopsys.tistory.com/277
https://behonestar.tistory.com/37
https://velopert.com/2350
https://mangkyu.tistory.com/56
https://velopert.com/2389
https://brunch.co.kr/@jinyoungchoi95/1
https://victorydntmd.tistory.com/116