본문 바로가기
시작/TIL(Today I Learned)

230118 - JavaScript - 내장 모듈 crypto, NodeJS

by 백씨네 2023. 1. 19.

오늘 내가 배운 것

1. 내장 모듈 crypto

2. JWT - Signature

3. JWT - 토큰 만들기

4. JWT - 토큰 검증하기

 

1. 내장 모듈 crypto

Node.js의 내장 모듈이기 때문에 require 해서 사용해야 한다.  crypto는 클래스이다..

const crypto = require("crypto")

const salt = process.env.SALT || "web7722"
const hash = crypto.createHmac(`sha256`, salt).update(평문).digest("hex") // createHmac은 `정적메서드`다.

 

JWT에서 Signature을 만드는 목적이  headerd와 payload가 위조, 변조되었는지 확인하기 위해서인데,

salt값이 없으면 sha256을 이용하여 다 똑같은 암호 값을 가지게 된다.

그래서 salt 값을 넣어서 암호화를 진행한다. 복호화를 했을 때 똑같은 값을 얻기 위해서는 반드시 salt를 이용하여 암호화를 해야 한다. 

그리고 이 salt 값을 이용해서 복호화를 할 수 있기 때문에 절대로 공유되거나 노출되지 않도록 조심해야 한다.


update에는
 인자값으로 암호화를 할 평문의 내용을 적는다.


digest는 
결과를 어떤 인코딩으로 나타낼 것인지를 쓰면 된다.

 

hex를 이용해서 16진수로 인코딩을 하면 평문의 글자가 몇 글자든 64글자로 나온다.

 

 

 

2. JWT - Signature

header와 payload가 위조, 변조되었는지 확인할 수 있다. ( 4번의 토큰 검증하기에서 방법을 확인)

 

평문 -> hash -> base64로 인코딩을 한 것이다. 

그래서 base64를 아무리 디코딩을 하더라도 salt가 없는 없다면 hash값 밖에 못 얻기 때문에 보안에 좋다.

 

 

 

3. JWT - 토큰 만들기

내장 모듈 crypto를 이용한 토큰 만들기

 

const crypto = require("crypto")

const header = {
    alg: "HS256",
    typ: "JWT",
}
const payload = {
    sub: "1234567890",
    userid: "admin",
    username: "admin",
    iat: 1516239022,
}

function encode(obj) {
    return Buffer.from(JSON.stringify(obj)).toString("base64url")
}

const header64 = encode(header)
const payload64 = encode(payload)
console.log(header64, payload64)

const 평문 = header64 + "." + payload64
console.log(평문)

const signature = crypto
    .createHmac("sha256", "web7722")
    .update(평문)
    .digest("base64url")
console.log(signature)

 

 

이 토큰을 탈취해서 접근을 시도하면 접근이 가능한가?
접근이 가능하다.  그래서 여러 가지 방법을 이용해서 최대한 보안에 신경을 쓴다.

예를 들면 액세스 토큰과 리프레시 토큰을 이용한 방법이다.

이 방법은 유효시간을 짧은 액세스 토큰과 비교적 긴 리프레시 토큰을 설정하여 액세스 토큰의 시간 동안 작업을 하다가 시간이 만료되면 새로 발급을 받아야 하는데 그 새로 발급을 받을 수 있는 시간은 리프레시 토큰의 기간 동안 가능하다.
리프레시 토큰의 기간도 만료가 되면 재로그인을 요청하여 위의 과정을 반복하는 방법이 있다.
토큰의 만료시간을 짧게 두어 탈취가 되더라도 그 시간에만 사용가능하게 하는 방법이다.

 

 

4. JWT - 토큰 검증하기

 

token을 뜯어보면 .(점)을 기준으로 header, payload, signature로 이루어져 있다.
여기서 header와 payload를 가져와서 다시 hash를 진행한다.
그리고 나온 값을 새로운 hash라고 했을 때, 기존의 hash와 비교를 해서 같으면 이 토큰은 사용가능한 토큰이 된다.

 

verify(token, salt){
        const [header, payload, signature] = token.split('.')
        const newSignature = this.createSignature([header, payload], salt)
        if(newSignature !== signature) {
            throw new Error("토큰이 다릅니다.")
        }
        return this.decode(payload)
    }

const payload = jwt.verify(token, salt)

 

 

 

JWT 공식 웹사이트

https://jwt.io/
 

JWT.IO

JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.

jwt.io

 

 


클래스를 이용하여 토큰을 만들고, 검증하기


const crypto = require("crypto")
//MVC - Service 에서 처리해야할 코드들이다...

class JWT{
    constructor({crypto}){
        this.crypto=crypto
    }
    sign(data, options={}){
        const header = this.encode({typ:"JMT", alg:"HS256"})
        const payload = this.encode({...data, ...options})
        const signature = this.createSignature([header, payload])

        return [header,payload,signature].join(".")
    }
    verify(token, salt){
        const [header, payload, signature] = token.split('.')
        const newSignature = this.createSignature([header, payload], salt)
        if(newSignature !== signature) {
            throw new Error("토큰이 다릅니다.")
        }
        return this.decode(payload)
    }
    encode(obj){
        return Buffer.from(JSON.stringify(obj)).toString("base64url") //base64
    }
    decode(base64url){
        return JSON.parse(Buffer.from(base64url, "base64url").toString("utf-8"))
    }
    createSignature (base64url, salt="web7722"){
        const data = base64url.join(".")
        return this.crypto.createHmac("sha256", salt).update(data).digest("base64url")
    }
}
const jwt = new JWT({crypto})

const token = jwt.sign({userid:"web7722", username:"baek"})

console.log(token) 
//eyJ0eXAiOiJKTVQiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyaWQiOiJ3ZWI3NzIyIiwidXNlcm5hbWUiOiJiYWVrIn0.jhQDnAM4rrsPKDmvFi2BqiOKrGT7SClWebDuVnjTdgY
const payload = jwt.verify("eyJ0eXAiOiJKTVQiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyaWQiOiJ3ZWI3NzIyIiwidXNlcm5hbWUiOiJiYWVrIn0.jhQDnAM4rrsPKDmvFi2BqiOKrGT7SClWebDuVnjTdgY","web7722")
console.log(payload)

 

 


 

 

댓글