JWT 인증토큰 적용시키기

wltjd1688·2025년 3월 1일

풀사이클

목록 보기
26/74

로그인 세션이 만료되었습니다. 다시 로그인해주세요.

웹페이지에서 로그인을 오래동안 유지하다보면 다음과 같은 창이 나오기도 한다.
이는 로그인(인증)을 한 지 오래되어 서버에서 로그인을 만료시키기에 일어나는 일이다.

그러면 왜 로그인을 하는가?

유저가 웹페이지에 회원가입하고 로그인을 하여 자신이 회원임을 인증함으로써, 웹페이지는 해당 유저에게 웹페에지의 기능을 사용할 수 있는 권한을 인가하게된다.
그런데 만약 한번 로그인을 하면 영원이 지속되도록 한다면, 유저의 권한이 침해당할 수 있다.

인증과 인가

  • 인증: 유저가 누구인지 확인하는 절차, 회원가입하고 로그인하여 자신이 회원임을 증명함
  • 인가: 유저에게 맞는 권한을 허락하는 것

세션은 뭐야?

상태를 말하며, 여기서는 로그인이 되어있는 상태를 말한다.
로그인 세션이 없다면 한 기능을 사용할려고 접근할 때마다 로그인을 해야하는 번거로움이 생기게 된다.

쿠키, 세션, JWT

  • 쿠키
    포춘쿠키에 비유되어 서버와 클라이언트가 통신할때 마다 보내진다.
    유저가 로그인을 하면 서버는 쿠키를 구워서 유저에게 보낸다. 그리구 유저가 그걸 저장하고 매번 새로운 페이지에 접근할 때마다 쿠키를 주면서 로그인을 매번할 필요없이 기능을 사용할 수 있게 해준다.
    • 장점: 서버에 로그인 상태를 저장하지 않는 Stateless한 상태를 유지함으로 RESTful한 서버가 된다.
    • 단점: 중간에 가로챌 수 있다.(보안 취약)
  • 세션
    로그인을 하면 서버가 금고를 만들어서, 정보 저장 그 금고 번호를 줘요
    사용자와 서버가 번호만 가지고 대화를 함
    • 장점: 보안이 개선됨
    • 단점: 서버에 금고번호를 저장하기에 Stateless한 상태를 유지하지 못함
  • JWT(JSON Web Token)
    쿠키와 세션의 단점을 보완한 JSON 형태의 데이터(토큰)
    토큰을 가진 사용자가 자신을 증명하기 위한 수단이다.
    • 장점
      • 암호화가 되어있어, 보안이 많이 개선되었다.
      • 서버가 상태를 저장하지 않아서 Stateless한 상태로 유지 가능하다.

JWT(JSON Web Token)

JWT의 구조

JWT홈페이지에서 인코딩(암호화)된 데이터를 디코딩(번역) 해준걸 볼 수 있다.

다음과 같이 디코딩된 형태가 JSON형태이기에 jwt라고 부른다.

  • HEADER
    토큰을 암호화하는데 사용한 알고리즘, 토큰의 형태
    • alg: 암호화에 사용된 알고리즘
    • tpy: 말그대로 타임
  • PAYLOAD
    body아 데이터를 넣는거처럼 payload에 사용자 정보같은 것을 넣어 보낸다.
  • VERIFY SIGNATURE
    웹서버에서 서명하는 곳으로, 만약 암호화된 알고르즘을 알더라도 페이로드 데이터를 수정하게 되면 서명값이 통째로 바뀌기 때문에 이를 통해 중간에 수정되는 것을 알 수 있다.

JWT를 인증/인가 하는 절차

  1. 클라이언트가 서버에 POST /login으로 로그인 요청
  2. 서버가 내부로직을 확인하여 회원인지 확인
  3. 확인 되었다면 JWT를 발행하면서, 언제 로그인 했는지 저장
  4. 이렇게 만든 JWT를 클라이언트에게 전달한다.
    • 이렇게 전달하게 되면 페이지를 요청할 때마다 토큰을 통해 로그인상태인걸 확인한다.
  5. 이후 클라이언트가 서버에 다른 요청대 마다 body에 JWT를 넣어 보낸다.
  6. 그러면 이제 서버는 JWT를 매번 확인하는 로직을 실행해주는 것이다.

JWT구현해보기

jsonwebtoken라이브러리를 사용하여 구현하고자 한다.

  • 설치

    $ npm install jsonwebtoken
  • 토큰 만들기

    var jwt = require('jsonwebtoken');
    var token = jwt.sign({ foo: 'bar' }, 'shhhhh');
    // token 생성 = jwt 서명을 했다! (페이로드, 나만의 암호키) + 기본적으로 제공되는 HS256로 암호화가 진행됨
    
    console.log(token);
    [Running] node "jwt-demo.js"
    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE3NDA5MDQ3OTB9.OzH9x71f4DRG-XgoFI3aw-9JQ-Ez1heVZB0MAipH6Ww

  • 검증하기

    var decoded = jwt.verify(token, 'shhhhh');
    console.log(decoded) // bar
    [Running] node "jwt-demo.js"
    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE3NDA5MDUwMDR9.FwfEXNQXv2JCaK0H64_ilztNnl_d5-Xn1vutH79slDk
    { foo: 'bar', iat: 1740905004 }

    iat?
    시간을 나타내는 것으로, JWT토큰이 언제 발행되었는지 알려준다.
    또한, 각 시간마다 토큰의 이름이 달라지는 것을 알 수 있다.

암호키 처리방법

  1. 변수로 관리

    const privateKey = 'shhhh';
    
    var token = jwt.sign({ foo: 'bar' },privateKey);
  2. 외부 파일(.txt)로 관리

    // sign with RSA SHA256
    var privateKey = fs.readFileSync('private.key');
    var token = jwt.sign({ foo: 'bar' }, privateKey);
  3. .env로 관리
    환경 변수 설정값을 넣어서 만드는 파일로,

    • 포트넘버
    • 데이터 베이스 계정
    • 암호키

등등의 외부에 유출되면 안되는 중요한 환경 변수들을 따로 관리하기 위한 파일확장자
dotenv 라이브러리를 통해 관리할 수 있다.

  • 설치

    npm i dotenv
  • .env파일 생성 후 내용 추가

    PRIVATE_KEY = "shhhh" # JWT 암호키
  • 코드 수정

    var jwt = require('jsonwebtoken');
    var dotenv = require('dotenv');
    
    // .env파일 사용하겠다는 선언
    dotenv.config();
    
    var token = jwt.sign({ foo: 'bar' }, process.env.PRIVATE_KEY);

    프로젝트에 적용하기

    이전에 했던거 처럼 user파일의 로그인에 JWT를 넣어서 보내주면 된다.
    cookie() 명령어를 통해 jwt 토큰을 쿠키에 넣어 보낼 수 있다.

// 로그인
router.post("/login",
    [
        body('email').notEmpty().isEmail().withMessage('이메일 형식 필요!'),
        body('password').notEmpty().isStrongPassword().withMessage('비밀번호 필요!'),
        validate
    ]
    ,(req, res, next)=>{
    try{
        isExist(req.body, res);
        const {email, password} = req.body;

        let selEmailSQL = `SELECT * FROM Users WHERE email = "${email}"`;
        db.query(selEmailSQL, function(err, results, fields){
            if (err){
                console.log(err);
                return res.status(400).end();
            };

            let userInfo = results[0];

            if (userInfo && userInfo.password === password){
                // token 발급
                const token = jwt.sign({
                    emila: userInfo.email,
                    name: userInfo.name
                }, process.env.PRIVATE_KEY);

                res.cookie("token", token)

                console.log("로그인에 성공하였습니다.");
                res.status(200).json({
                    message: `${userInfo.name}님 환영합니다.`,
                });
            } else {
                console.log(err);
                res.status(403).json({message: "아이디 또는 비밀번호가 틀렸습니다."});
            };
        });
        // 메인 페이지로 이동
    } catch {
        res.status(500).json({message: "서버와의 연결에 실패하였습니다."})
    }
})

유효기간 설정하기

지금까지 JWT를 쿠키에 담아서 클라이언트에게 보내는 거 까지했다.
근데 지금은 유효기간을 설정하지 않았기에 유효기간 설정하는 것을 할것이다.

const token = jwt.sign({
  emila: userInfo.email,
  name: userInfo.name
}, process.env.PRIVATE_KEY,{
  expiresIn : '5m',
  issuer: "root"
});

Cookie의 Secure와 HttpOnly

  • Secure
    쿠키에서 Secure라는게 있다. 이는 보안과 관련된 것인데, HTTP로 보내는지 HTTPS 보내는지에 따라 false, true로 나뉘어진다.

  • HttpOnly
    API호출만 허락하는지를 true, false로 선택할 수 있는 창이다.
    true라면 화면을 신경쓰지 말고 HTTP로 API를 신청했을 때이다.
    그래서 이를 true로 설정한다.

res.cookie("token", token, {
  httpOnly: true
})

HTTP랑 HTTPS차이

  • HTTP (Hypertext Transfer Protocol)
    데이터를 암호화 없이 전송하여 보안에 취약함
    기본 포트: 80
    중간자 공격(MITM)이나 패킷 스니핑에 노출될 수 있음
  • HTTPS (HTTP Secure)
    SSL/TLS 프로토콜을 통해 데이터를 암호화하여 전송
    기본 포트: 443
    서버 인증서를 통해 신뢰성을 확보하며, 데이터 무결성과 보안을 보장함
profile
일단 해!!!!

0개의 댓글