Hashing
๋ณตํธํ๊ฐ ๊ฐ๋ฅํ ๋ค๋ฅธ ์ํธํ ๋ฐฉ์๋ค๊ณผ ๋ฌ๋ฆฌ, ํด์ฑ์ ์ํธํ๋ง ๊ฐ๋ฅํ๋ค.
๋จ๋ฐฉํฅ ์ํธํ ๋ฐฉ์์ด๋ฉฐ, ํด์ฑ์ ํด์ ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ์ํธํ๋ฅผ ์งํํ๋ค.
โ๋ณตํธํ๊ฐ ๋ถ๊ฐ๋ฅํ ํด์ฑ์ ์ฌ์ฉํ๋ ์ด์ ?
ํด์ฑ์ ๋ชฉ์ ์ ๋ฐ์ดํฐ ์์ฒด๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์๋๋ผ,
๋์ผํ ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ๊ณ ์๋์ง ์ฌ๋ถ๋ง์ ํ์ธํ๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ.
๋ก๊ทธ์ธ ์์ฒญ ์ฒ๋ฆฌ ์์
์๋ฒ์ธก์์ ๋น๋ฐ๋ฒํธ๋ฅผ ๋ชจ๋ฅด๋ ์ํ์์ ๋ก๊ทธ์ธ ์์ฒญ์ ๋ฐ์์ ๋
ํด์ฑํ ๊ฐ๋ผ๋ฆฌ ๋น๊ตํด์ ์ผ์นํ๋์ง ํ์ธํ์ฌ ์ฒ๋ฆฌํ๋ค.
(์ ํํ ๊ฐ์ ๋ชฐ๋ผ๋ ์ผ์นํ๋์ง๋ง ํ์ธํ๋ฉด ๋๊ธฐ ๋๋ฌธ, ๋ฐ์ดํฐ ์ ์ถ ์ํโ)
โ๏ธํด์ ํจ์์ ํน์ง
๏ผ ํญ์ ๊ฐ์ ๊ธธ์ด์ ๋ฌธ์์ด์ ๋ฆฌํด
๏ผ ์๋ก ๋ค๋ฅธ ๋ฌธ์์ด, ๋์ผํ ํด์ ํจ์ โ ๋ฐ๋์ ๋ค๋ฅธ ๊ฒฐ๊ณผ๊ฐ
๏ผ ๋์ผํ ๋ฌธ์์ด, ๋์ผํ ํด์ ํจ์ โ ํญ์ ๊ฐ์ ๊ฒฐ๊ณผ๊ฐ
๋น๋ฐ๋ฒํธ | ํด์ ํจ์(SHA1) ๋ฆฌํด ๊ฐ |
---|---|
โpasswordโ | โ5BAA61E4C9B93F3F0682250B6CF8331B7EE68FD8โ |
โPasswordโ | โ8BE3C943B1609FFFBFC51AAD666D0A04ADF83C9Dโ |
ํญ์ ๊ฐ์ ๊ฒฐ๊ณผ๊ฐ์ด ๋์ค๋ ํด์ ํจ์์ ํน์ฑ์ ์ด์ฉํ์ฌ
ํด์ ํจ์๋ฅผ ๊ฑฐ์น๊ธฐ ์ด์ ์ ๊ฐ์ ๋ฏธ๋ฆฌ ์์๋ผ ์ ์๋๋ก
๊ธฐ๋กํด๋์ ํ์ธ ๋ ์ธ๋ณด์ฐ ํ
์ด๋ธ์ด ์กด์ฌํ๋ค.
์ด๋ ๋ณด์์ ์ทจ์ฝํ๊ธฐ ๋๋ฌธ์ ์ํธ(salt)๋ฅผ ํ์ฉํด์ผ ํ๋ค.
Salt
ํด์ฑ ์ด์ ๊ฐ์ ์์์ ๊ฐ์ ๋ํด ๋ฐ์ดํฐ๊ฐ ์ ์ถ ๋๋๋ผ๋
ํด์ฑ ์ด์ ์ ๊ฐ์ ์์๋ด๊ธฐ ๋์ฑ ์ด๋ ต๊ฒ ๋ง๋๋ ๋ฐฉ๋ฒ์ด๋ค.
์ํธ๊ฐ ํจ๊ป ์ ์ถ ๋ ๊ฒ์ด ์๋๋ผ๋ฉด ์ํธํ ์ด์ ์ ๊ฐ์ ์์๋ด๋ ๊ฒ์ ๋ถ๊ฐ๋ฅ์ ๊ฐ๊น๋ค.
๋น๋ฐ๋ฒํธ + ์ํธ | ํด์ ํจ์(SHA1) ๋ฆฌํด ๊ฐ |
---|---|
โpasswordโ + โsaltโ | โC88E9C67041A74E0357BEFDFF93F87DDE0904214โ |
โPasswordโ + โsaltโ | โ38A8FDE622C0CF723934BA7138A72BEACCFC69D4โ |
ํ ํฐ ์ธ์ฆ ๋ฐฉ์
ํ ํฐ์ ์ฌ์ฉํ๋ฉด ์ฌ์ฉ์์ ์ธ์ฆ ์ ๋ณด๋ฅผ
์๋ฒ๊ฐ ์๋ ํด๋ผ์ด์ธํธ ์ธก์ ์ ์ฅํ ์ ์๋ค.
โ์ ํ ํฐ ์ธ์ฆ ๋ฐฉ์์ด ์๊ฒผ๋์?
์ธ์
์ธ์ฆ ๋ฐฉ์์์๋ ์๋ฒ์์ ์ ์ ์ ์ํ ๊ด๋ฆฌ๋ฅผ ํ๋๋ฐ
์๋ฒ์ ๋ถ๋ด์ด ์ปค์ ธ์, ์๋ฒ์ ๋ถ๋ด์ ์ค์ด๊ธฐ ์ํด
ํ ํฐ ๊ธฐ๋ฐ ์ธ์ฆ ๋ฐฉ์์ด ๋ฑ์ฅํ ๊ฒ์ด๋ค.
ํ ํฐ์ ๋ฌด์ธ๊ฐ๋ฅผ ์ด์ฉํ ์ ์๋ ๊ถํ์ด๋ ์๊ฒฉ์ ์๋ฏธํ๋ฉฐ,
์น ๋ณด์์์๋ ์ธ์ฆ๊ณผ ๊ถํ ์ ๋ณด๋ฅผ ๋ด๊ณ ์๋ ์ํธํ๋ ๋ฌธ์์ด์ ๋งํ๋ค.
์ด๋ฅผ ์ด์ฉํด ํน์ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ํ ์ฌ์ฉ์์ ์ ๊ทผ ๊ถํ์ ๋ถ์ฌํ ์ ์๋ค.
ย ๋ฌด์ํ์ฑย
์๋ฒ๊ฐ ์ ์ ์ ์ธ์ฆ ์ํ๋ฅผ ๊ด๋ฆฌํ์ง ์์.
์๋ฒ๋ ๋น๋ฐ ํค๋ฅผ ํตํด ํด๋ผ์ด์ธํธ์์ ๋ณด๋ธ ํ ํฐ์ ์ ํจ์ฑ๋ง ๊ฒ์ฆํ ๋ฟ.
But, ํ ํฐ ์์ฒด๊ฐ ํ์ทจ๋์์ ๋์๋ ์๋ฒ๊ฐ ํ ํฐ์ ๊ฐ์ ๋ก ๋ง๋ฃ์ํฌ ์ ์๋ค.
ย ํ์ฅ์ฑย
๋ค์์ ์๋ฒ๊ฐ ๊ณตํต๋ ์ธ์
๋ฐ์ดํฐ๋ฅผ ๊ฐ์ง ํ์๊ฐ ์์.
๋๋ถ์ ์๋ฒ ํ์ฅ์ด ์ฉ์ดํจ.
But, ์ ํจ๊ธฐ๊ฐ์ ๊ธธ๊ฒ ์ค์ ํ๊ฒ ๋๋ฉด ํ์ทจ๋ ๊ฒฝ์ฐ ๋ ์น๋ช ์ ์ผ ์ ์๋ค.
ย ์ด๋์๋ ํ ํฐ ์์ฑ ๊ฐ๋ฅย
ํ ํฐ์ ์์ฑ๊ณผ ๊ฒ์ฆ์ด ํ๋์ ์๋ฒ์์ ์ด๋ฃจ์ด์ง์ง ์์๋ ๋จ.
๊ทธ๋์ ํ ํฐ ์์ฑ๋ง์ ๋ด๋นํ๋ ์๋ฒ๋ฅผ ๊ตฌ์ถํ ์ ์์.
(์ฌ๋ฌ ์๋น์ค ๊ฐ์ ๊ณตํต๋ ์ธ์ฆ ์๋ฒ๋ฅผ ๊ตฌํ ๊ฐ๋ฅ)
ย ๊ถํ ๋ถ์ฌ์ ์ฉ์ดย
ํ ํฐ์ ์ธ์ฆ ์ํ, ์ ๊ทผ ๊ถํ ๋ฑ ๋ค์ํ ์ ๋ณด๋ฅผ ๋ด์ ์ ์๊ธฐ์
์ฌ์ฉ์ ๊ถํ ๋ถ์ฌ์ ์ฉ์ดํ๋ค. ๊ทธ๋์ ์ด๋๋ฏผ ๊ถํ ๋ถ์ฌ ๋ฐ ์ ๋ณด์
์ ๊ทผํ ์ ์๋ ๋ฒ์๋ ์ค์ ํ ์ ์๋ค.
But, ํ ํฐ์ ๋ง์ ๋ฐ์ดํฐ๋ฅผ ๋ด์ผ๋ฉด ๊ทธ๋งํผ ์ํธํ ๊ณผ์ ์ด ๊ธธ์ด์ง๊ณ
ํ ํฐ์ ํฌ๊ธฐ๋ ์ปค์ง๊ธฐ ๋๋ฌธ์ ๋คํธ์ํฌ ๋น์ฉ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์๋ค.
JSON Web Token
JSON ๊ฐ์ฒด์ ์ ๋ณด๋ฅผ ๋ด๊ณ ์ด๋ฅผ ํ ํฐ์ผ๋ก ์ํธํํ์ฌ ์ ์กํ ์ ์๋ ๊ธฐ์ ์ด๋ค.
ย 1. Headerย
ํ ํฐ์ ์ข
๋ฅ, ์๊ทธ๋์ฒ๋ฅผ ๋ง๋ค ๋ ์ฌ์ฉํ
์๊ณ ๋ฆฌ์ฆ์ JSON ํํ๋ก ์์ฑํ๋ค.
{
"alg": "HS256",
"typ": "JWT"
}
// ์ด JSON ๊ฐ์ฒด๋ฅผ base64 ๋ฐฉ์์ผ๋ก ์ธ์ฝ๋ฉํ๋ฉด JWT์ ์ฒซ ๋ฒ์งธ ๋ถ๋ถ์ธ Header๊ฐ ์์ฑ๋๋ค.
// โ๏ธ์ฐธ๊ณ ) base64 ๋ฐฉ์ - ์ผ๋ง๋ ์ง ๋์ฝ๋ฉํ ์ ์๋ ์ธ์ฝ๋ฉ ๋ฐฉ์์.
// ๋
ธ์ถ๋์ด์๋ ์๋๋ ์ ๋ณด๋ ๋ด์ง ๋ง๊ธฐ
ย 2. Payloadย
์ ๋ฌํ๋ ค๋ ๋ด์ฉ๋ฌผ์ ๋ด๊ณ ์๋ ๋ถ๋ถ์ด๋ค.
// ์ด๋ค ์ ๋ณด์ ์ ๊ทผ ๊ฐ๋ฅํ์ง์ ๋ํ ๊ถํ,
// ์ ์ ์ ์ด๋ฆ๊ณผ ๊ฐ์ ๊ฐ์ธ์ ๋ณด, ํ ํฐ์ ๋ฐ๊ธ ์๊ฐ ๋ฐ
// ๋ง๋ฃ ์๊ฐ ๋ฑ์ ์ ๋ณด๋ค์ JSON ํํ๋ก ๋ด๋๋ค.
{
"sub": "someInformation",
"name": "phillip",
"iat": 151623391
}
// ์ด JSON ๊ฐ์ฒด๋ฅผ base64๋ก ์ธ์ฝ๋ฉํ๋ฉด JWT์ ๋ ๋ฒ์งธ ๋ถ๋ถ์ธ Payload๊ฐ ์์ฑ๋๋ค.
ย 3. Signatureย
ํ ํฐ์ ๋ฌด๊ฒฐ์ฑ์ ํ์ธํ ์ ์๋ ๋ถ๋ถ์ด๋ค.
Signature๋ ์๋ฒ์ ๋น๋ฐ ํค(์ํธํ์ ์ถ๊ฐํ salt)์
Header์์ ์ง์ ํ ์๊ณ ๋ฆฌ์ฆ์ ์ฌ์ฉํ์ฌ
Header์ Payload๋ฅผ ํด์ฑํ๋ค.
// ๋ง์ฝ HMAC SHA256 ์๊ณ ๋ฆฌ์ฆ์ ์ฌ์ฉํ๋ค๋ฉด Signature๋ ์๋์ ๊ฐ์ ๋ฐฉ์์ผ๋ก ์์ฑ๋๋ค.
HMACSHA256(base64UrlEncode(header) + '.' + base64UrlEncode(payload), secret);
๋ค์ํ ๋ฐฉ๋ฒ๋ค์ด ์์ง๋ง, ๋ํ์ ์ผ๋ก๋
์ก์ธ์ค ํ ํฐ๊ณผ ๋ฆฌํ๋ ์ ํ ํฐ์ ํจ๊ป ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ด ์๋ค.
Access Token
๋ณด์์ ์ํด 24์๊ฐ ์ ๋์ ์งง์ ์ ํจ๊ธฐ๊ฐ.
Refresh Token
์ก์ธ์ค ํ ํฐ์ด ๋ง๋ฃ๋์์ ๋ ์๋ก์ด ์ก์ธ์ค ํ ํฐ์
๋ฐ๊ธ๋ฐ๊ธฐ ์ํด ์ฌ์ฉ๋๋ ํ ํฐ์ด๋ค. (์ก์ธ์ค ํ ํฐ๋ณด๋ค ๊ธด ์ ํจ๊ธฐ๊ฐ)
jsonwebtoken
// npm i jsonwebtoken
// ๊ตฌ์กฐ๋ถํดํ ๋นํด์ ์ฌ์ฉ ๊ฐ๋ฅ
const { sign, verify } = require("jsonwebtoken")
// ํ ํฐ ์์ฑํ๋ ๋ฐฉ๋ฒ
const token = sign(ํ ํฐ์_๋ด์_๊ฐ, process.env.ACCESS_SECRET, { ์ต์
1: ๊ฐ, ์ต์
2: ๊ฐ, ... });
// ํ ํฐ ํด๋
ํ๋ ๋ฒ verify(ํด๋
, ๊ฒ์ฆ)
const token = verify(token, process.env.ACCESS_SECRET);
dotenv
// npm i dotenv
// .env ํ์ผ ์์ฑ, ๊ทธ ์์ ํ์ํ ํ๊ฒฝ ๋ณ์๋ฅผ ํค=๊ฐ์ ํฌ๋ฉง์ผ๋ก ๋์ด.
// dotenv๋ฅผ ์ฌ์ฉํด ํ์ฌ ๋๋ ํ ๋ฆฌ์ ์์นํ .env ํ์ผ๋ก๋ถํฐ ํ๊ฒฝ ๋ณ์๋ฅผ ์ฝ์ด ๋ผ ์ ์๋ค.
require("dotenv").config();
const { sign, verify } = require("jsonwebtoken");
module.exports = {
generateToken: (user, checkedKeepLogin) => {
const payload = {
id: user.id,
email: user.email,
};
let result = {
// ํ ํฐ ์์ฑํ๋ ๋ฐฉ๋ฒ
// const token = sign(ํ ํฐ์_๋ด์_๊ฐ, ACCESS_SECRET, { ์ต์
1: ๊ฐ, ์ต์
2: ๊ฐ, ... });
accessToken: sign(payload, process.env.ACCESS_SECRET, {
expiresIn: "1d", // 1์ผ๊ฐ ์ ํจํ ํ ํฐ์ ๋ฐํํฉ๋๋ค.
}),
};
if (checkedKeepLogin) {
result.refreshToken = sign(payload, process.env.REFRESH_SECRET, {
expiresIn: "7d", // ์ผ์ฃผ์ผ๊ฐ ์ ํจํ ํ ํฐ์ ๋ฐํํฉ๋๋ค.
});
}
return result;
},
verifyToken: (type, token) => {
let secretKey, decoded;
switch (type) {
case "access":
secretKey = process.env.ACCESS_SECRET;
break;
case "refresh":
secretKey = process.env.REFRESH_SECRET;
break;
default:
return null;
}
// ํ ํฐ ํด๋
ํ๋ ๋ฒ verify(ํด๋
, ๊ฒ์ฆ)
try {
decoded = verify(token, secretKey);
} catch (err) {
console.log(`JWT Error: ${err.message}`);
return null;
}
return decoded;
},
};