인증과 인가
- 인증(Authentication) = 로그인 : 관리자든 고객이든 인증을 통해서 사이트에 가입된 사용자라는 걸 증명하는 것
- ex) : ID, PW로 로그인 하는 행위
- 인가(Authorization) : 인증 이후의 프로세스로, 인증된 유저가 어떠한 자원에 접근할 수 있는지 확인하는 절차
- ex) : 같은 사이트 내에 관리자/고객에 따라 접근할 수 있는 페이지가 다르다,
쿠키 vs 세션 vs JWT
쿠키
쉽게 말하면, 로그인을 하면 서버가 쿠키를 구워서 준다. 그 후 사용자랑 서버가 쿠키를 가지고 핑퐁한다.
- 웹에서 서버와 클라이언트가 주고받는 데이터 중 하나
- 생성은 웹 서버에서 하여 웹 브라우저에게 주면, 브라우저가 자기 메모리에 저장한 후 다음번에 같은 웹서버 방문할 때 쿠키를 들고 요청한다.
- 장점
- 기존 로그인 정보를 사용하기 때문에 인증을 위한 추가적인 데이터 저장이 필요없다.(쿠키는 서버가 아닌 클라이언트 웹 브라우저에 한다) -> Stateless(상태저장하지않는다)하다 -> RESTful
- 단점
- 사용자의 주요 정보를 매번 요청에 담기 때문에 보안상 문제가 있다.
- 클라이언트에서 쿠키 정보를 쉽게 변경,삭제할 수 있고, 가로채기 당할 수 도 있다.
- 쿠키사이즈가 커질수록 네트워크 부하가 심해진다.
이처럼 Cookie에 담아서 계속 사용하기에는 누가 중간에 쿠키를 낚아채서 뜯어 볼까 걱정이된다. 그래서 해결방안이 Session이다.
세션
쉽게 말하면,로그인을 하면 서버가 금고를 만들어서 정보를 저장 하고 그 금고 번호를 준다. 그 후 사용자와 서버는 금고 번호만 가지고 핑퐁을 한다.
- Cookie에 중요한 정보를 담지 말고 중요한 정보는 서버에 저장해두고 그 정보가 어딨는지 주소만 적어서 Cookie에 담는 것
- 쿠키에 넣어서 보내기엔 너무 중요한 내용은 서버가 가진 금고(Session)에 넣어두고, 그 금고번호(Seesion ID)만 쿠키에 넣어서 통신함
- 장점
- 사용자의 로그인 정보를 주고 받지 않기 대문에 상대적으로 안전
- 사용자마다 고유한 세션 ID가 발급되기 때문에, 요청이 들어올 때마다 회원 DB를 찾지 않아도 된다.
- 단점
- 사용자를 식별할 수 있는 값인 세션 ID를 생성하고, 서버에 저장해야 하는 작업이 생긴다. -> Stateless하지 않다
- 서버세션저장소를 사용하므로 요청이 많아지면 서버 부하가 심해진다.
JWT(JSON Web Token)
JSON 형태의 데이터를 안전하게 전송하기 위한(웹에서 사용하는) 토큰 즉 토큰을 가진 사용자가 "증명"하기 위한 수단
- 토큰 : (인증용) 입장 가능한 유저, (인가용) 관리자권환&일반 유저 권한
- 장점 : 보안에 강하다(암호화가 되어 있다), Stateless하다(서버가 상태를 저장하지 않는다), 서버 부담을 줄일 수 있다
JWT 구조
- 헤더 : 토큰을 암호화하 데 사용한 알고리즘, 토큰의 형태(jwt)
- 페이로드 : 사용자 정보(이름, 주소, 핸드폰, ... 비밀번호 X)
- 서명 : 만약 페이로드 값이 바뀌면, 이 서명값이 통째로 바뀌기 때문에 우리는 JWT를 믿고 쓸 수 있다.
JWT로 인증/인가 하는 절차
- 로그인 요청( HTTP body에 username과 password)
- 내부로직을 통해 DB에서 사용자 확인
- 로그인 정보를 Payload에 담고, Secret Key를 사용해서 JWT(토큰, Access Token) 발행
- 서버가 JWT를 Client에 전달( 전달방법은 개발자가 정함)
- 클라이언트는 전달받은 토큰(JWT)를 저장(쿠키, Local Storage 등)
- 클라이언트가 서버에 요청할 때마다 토큰(JWT)를 요청 Header의 Authorization에 포함시켜 함께 전달
- 서버는 클라이언트가 전달한 토큰의 Signature(서명)를 secret Key로 복호화한 후, 위변조 여부 및 유효기간을 검증
- 검증에 성공하면 jwt에 사용자 정보를 확인하고 요청에 응답
JWT 구현
https://www.npmjs.com/package/jsonwebtoken
npm install jsonwebtoken
var jwt = require('jsonwebtoken');
var token = jwt.sign({foo:'bar'},'shhhhh');
//token생성 = jwt 서명을 했다!(페이로드, 나만의 암호키) + SHA256
console.log(token);
콘솔에서 확인한 토큰 값
방금 발급한 토큰을 jwt.io에서 검증해보면 Decoded된 정보들을 확인할 수 있습니다.
// verify a token symmetric
var decoded = jwt.verify(token, 'shhhhh');
console.log(decoded);
매번 검증을 jwt.io에서 할 수 없으므로 위의 코드를 실행시켜 확인해보면 iat가 위의 jwt.io랑 다르게 나옵니다. 즉, iat(Issued at)는 토큰이 발행한 시간을 초로 나타낸 것입니다.
한번더 실행해보면 마찬가지로 Header의 값은 같은데 Payload의 값과 서명값은 다릅니다.
위코드에서 verify(token, 'shhhhh') 에서 'shhhhh'는 나만의 암호키입니다. 저렇게 코드에 유출하면 안 되고 따로 파일에 저장하든가 해야합니다.
.env(environment : 환경변수 '설정 값')
- 개발을 하다가 포트넘버, 데이터베이스 계정, 암호키 등 외부에 유출되면 안 되는 중요한 환경변수(깃허브에 올라가면 안되는 값)들을 다로 관리하기 위한 파일
- 파일 확장자 : . env
.env 파일은 환경변수파일이기 때문에 프로젝트 최상위 패키지에 존재해야한다.
# install locally (recommended)
npm install dotenv --save
최상단에 .env파일을 만들어주고 키값을 작성해준다.
env파일을 불러와서 수정한 코드이다. 포트넘버도 해당파일에서 config해준뒤 process.env.PORT를 하면 사용가능하다
var jwt = require('jsonwebtoken');
var dotenv = require('dotenv');
dotenv.config();
//서명 = 토큰발행
var token = jwt.sign({foo:'bar'}, process.env.PRIVATE_KEY);
//token생성 = jwt 서명을 했다!(페이로드, 나만의 암호키) + SHA256
console.log(token);
//검증
// 만약 검증에 성공하면 ,페이로드 값을 확인할 수 있음
// verify a token symmetric
var decoded = jwt.verify(token, process.env.PRIVATE_KEY);
console.log(decoded);
cookie
서버에서 클라이언트에게 token 값을 아래처럼 body에 실어서 보낼 수는 있지만 보통 cookie에 많이 실어서 보냅니다.
//token 발급
const token = jwt.sign({
email : loginUser.email,
name : loginUser.name
}, process.env.PRIVATE_KEY);
res.status(200).json({
message: `${loginUser.name}님 로그인 되었습니다.`,
token : token
})
cookie에 token이라는 상자를 하나 만들어서 여기에 위의 token을 담아서 보내주겠습니다.
//token 발급
const token = jwt.sign({
email : loginUser.email,
name : loginUser.name
}, process.env.PRIVATE_KEY);
res.cookie("token",token);
res.status(200).json({
message: `${loginUser.name}님 로그인 되었습니다.`
})
postman에서 확인해보겠습니다.
위의 postman에서 Cookies탭에 Secure,HttpOnly를 확인해보면 false인것을 알 수 있습니다.
Secure는 HTTP이면 false HTTPS이면 true가 됩니다.
- HTTP
- http:://localhost:1234/login
- HTTPS(secure)
- https:://www.naver.com
HTTPOnly( true가 되면 프론트엔드신경쓰지 않고 API 호출"만"허락하게 한다) : XSS 공격(프론트엔드 공격: 웹 브라우저 JS접근 => 공격) true로 바꿀려면 아래처럼 httpOnly:true 해줍니다.
res.cookie("token",token, {
httpOnly:true
});
jwt 유효기간 설정
jwt토큰에 유효기간을 설정할 수 있습니다. expriesIn를 이용해 토큰의 유효기간을 설정할 수 있습니다. 30m 즉 30분으로 설정했고 추가적으로 누가 토큰을 생성했는지 issuer을 통해 설정할 수 있습니다.
//token 발급
const token = jwt.sign({
email : loginUser.email,
name : loginUser.name
}, process.env.PRIVATE_KEY, {
expiresIn : '30m',
issuer : "choims"
});
res.cookie("token",token, {
httpOnly:true
});
console.log(token);
res.status(200).json({
message: `${loginUser.name}님 로그인 되었습니다.`
})
발급된 토큰을 jwt.io 에서 확인해보면 PAYLOAD를 보면 정확하게 나옵니다.
'백엔드 > node.js(express)' 카테고리의 다른 글
도서주문관리 프로젝트 - 2. 유저API(컨트롤러, 단뱡향암호화(crypto),jwt token) (1) | 2024.01.03 |
---|---|
도서주문관리 프로젝트 - 1.API 설계(API,테이블) (0) | 2023.12.28 |
토이프로젝트 유튜브4 (유효성검사,next) (0) | 2023.12.23 |
토이프로젝트 유튜브4(회원API,채널API sql적용) (1) | 2023.12.22 |
토이프로젝트 유튜브3(DB생성,express-db연동,db모듈화) (0) | 2023.12.20 |