1. 암호화
단방향 양방향 암호방식
단방향은 복호화해서 원래의 값을 알수 없고
양방향은 복호화해서 원래의 값을 알수 있다.
그렇기 때문에 단방향 복호화해서 원래의 비밀번호는 알수 없게 하고 복호화해서 암호를 해독한다.
네이버 페이지를 보면 비밀번호 찾기를 시도할때 비밀번호를 알려주지 않고 비밀번호 변경을 시켜준다.
2. 복호화
복호화는 암호문을 편문으로 변환하는 과정
부호화(인코딩)된 데이터를 부호화 되기전 형태로 바꿔서 사람이 읽을수 있는 형태로 되돌려 놓는것
3. 단방향의 비교 검증 방법
데이터 베이스에 저장된 암호화
로그인할때 입력받은 비밀번호를 단방향으로 암호화를 통해 비교하면
기본의 비밀번호는 저장되지 않고 암호화된 문자열로만 비교시킨다.
단방향 암호화는 해쉬 알고리즘을 사용해서 문자열을 고정된 길이의 문자열로 암호화 시킨다.
예를 들어 123 과 1111111 둘의 길이가 다른데 해쉬 알고리즘으로 길이를 정해놓으면
둘다 길이가 맞춰진다.
4. crypto
const crypto = require("crypto");
let hashAlgor = crypto.createHash("sha512");
사용할 해시 알고리즘은 sha512 암호 알고리즘이다.
md5, sha1, sha256, sha512등이 있는데 sha-512 알고리즘은 국가안보국(NSA)이 설계한 암호 해쉬 함수이다.
sha512는 512비트(64비트) 해시값을 만들어주는데 일반적으로 길이가 128자리인 16진수로 렌더링 된다.
//임의의 비밀번호
const pw = "343423";
let hashing = hashAlgor.update(pw); //매개변수로 암호화 시킬 문자열
// 보여줄 인코딩 설정
let hasString = hashing.digest("base64");
인코딩할 알고리즘을 넣어준것이 base64
해싱된 객체를 base64로 문자열로 반환
5. 왜 이렇게 암호화를 해야될까?
알고리즘으로 암호화 하면 해커가 뚫기 힘들게 하기 위함이다.
지금 이렇게만 해쉬 알고리즘으로 암호화하면 같은값을 암호화시킨다면 암호화된 문자열도 같다.
그렇기 때문에 salt라는 기법을 더해준다.
6. salt
salt는 소금처럼 기존 비밀번호에 salt값을 더해줘서 마치 음식에 소금을 뿌리는것과 비슷하다.
salt값을 랜덤으로 생성한다면 같은 비밀번호라도 더해주는 salt값이 다르기 때문에 다른 암호화된 값을 가진다.
salt값은 항상 비밀번호에 매번 추가시켜서 사용해야하니까 .env같이 안전한 곳에 넣어놔야 한다.
salt값 만들기
크립토의 랜덤바이트 생성 함수 : 랜덤한 바이트를 생성 시킬수 있다, 32바이트 이상이어야 짐작하기 어렵다.
randomBytes 함수가 랜덤한 바이트를 만들어주는 함수이다.
매개변수의 인자 : 바이트의 사이즈
crypto.randomBytes(32, function (err, byte) {
// 32bit 길이의 랜덤한 byte생성
if (err) {
console.log(err);
} else {
console.log(byte);
}
});
크립토의 randomBytes 함수로 salt값을 만들어서 데이터베이스에 저장한후 비교한다.
모든 패스워드가 고유한 salt값을 가지게 할수도 있다.
7. 키 스트레칭
키 스트레칭은 salt와 패스워드를 해시 함수에 넣는 과정을 반복시켜서 해커가 복호화 하지 못하게 한다.
계산량을 늘려서 값 출력을 임의적으로 느리게 만든다.
8. 암호화 방법
(1) pbkdf
해시함수의 컨테이너 역할을 하고 해시함수에 salt를 적용해서 해시함수의 반복횟수를 지정해서
암호화 할수 있고 IOS표준에 적합하며 NIST에서 승인된 알고리즘이다.
(2) scrypto
암호화가 강력은 하지만 많은 메모리와 cpu를 잡아먹어서 부하가 걸리는 역효과가 날수 있다.
오프라인 공격에 많이 강력하지만 자원을 많이 써서 위험하다.
OpenSSL 1.1 이상을 제공하는 시스템에서만 사용할 수 있다.
주어진 자원에서 공격자가 사용할수 있는 병렬 처리 양이 한정되어 있다.
(3)bcrypto
보안에 집착하기로 유명한 OpenBSD에서 사용하고 .NET 및 자바를 포함한 많은 플랫폼 언어에서도 사용할 수 있다.
반복횟수를 늘려 연산속도를 늦출수 있고 연산능력이 증가해도 공격에 대비를 할수 있다.
암호화된 String중에서 일부분을 salt로 쓰고 있어서 그 데이터를 얻어온 후에 pw와 같이 보내서 비교한다.
9. pbkdf
예시
crypto.randomBytes(32, function (err, byte) {
crypto.pbkdf2(
//해싱하려고 한 문자열
pw,
// 문자열로 변환하는데 인코딩 방식은 base64
byte.toString("base64"),
//반복 횟수를 지정, 반복횟수가 많아질수록 복호화하기 어려워지는데 시간도 많이 걸린다.
100000,
//길이를 설정
64,
//암호화 알고리즘 설정
"sha512",
//콜백 함수
function (err, hashed) {
console.log(hashed);
}
);
});
salt 값을 만들어 주는 함수
const createSalt = () => {
//암호화를 처리하는데 시간이 걸리기 때문에
// Promise를 사용해서 비동기 처리를 한다.
return new Promise((resolve, rejects) => {
//랜덤 바이트 생성 길이가 64
crypto.randomBytes(64, (err, byte) => {
if (err) {
// 실패시 err값 반환
rejects(err);
} else {
// 성공시 resolve 함수로 반환
resolve(byte.toString("base64"));
}
});
});
};
비밀번호를 해싱해주는 함수
const pwHashed = (userId, password) => {
//비동기 처리
return new Promise((resolve, rejects) => {
// 유저 테이블에서 user_id의 값이 있는지 확인
const sql = "SELECT * FROM users WHERE user_id=?";
// 쿼리문 실행 : 유저 아이디를 찾고
client.query(sql, [userId], async (err, result) => {
// 결과값이 있으면 여기서 결과값은 해당 유저의 객체고
// 그안에 salt값을 가져온다.
console.log(result);
if (result[0]?.salt) {
const salt = await result[0].salt;
// pbkdf2 암호화를 하는데 해싱 알고리즘은 sha512이거
// 길이 : 64, 반복횟수 : 100000
crypto.pbkdf2(password, salt, 100000, 64, "sha512", (err, key) => {
if (key.toString("base64") === result[0].password) {
resolve(key.toString("base64"));
} else {
rejects("err");
}
});
} else {
rejects("err");
}
});
});
};
비밀번호 암호화해서 만들기
const createPwHashed = (password) => {
// 비동기 처리
return new Promise(async (resolve, rejects) => {
const salt = await createSalt(); //여기서 소금만들고
// 100000만큼 반복시키는데 키 스트레칭
// 비밀번호에 문자를 더해서 암호화시키는 기법 salt사용
// 여기서 salt는 랜덤값이다 랜덤 바이트 함수로 만들어낸
crypto.pbkdf2(password, salt, 100000, 64, "sha512", (err, key) => {
if (err) {
rejects("err");
} else {
// 비밀번호마다 고유의 salt값을 가지고 있게 하기 위해서
// 암호화한 비밀번호와 salt값을 둘다 데이터베이스에 저장
resolve({ password: key.toString("base64"), salt });
}
});
});
};
10. bcrypto
암호화 시키기
bcrypt.hash(password, 10, (err, data) => {
const sql = "INSERT INTO users (user_id,password)VALUES(?,?)";
//VALUES(?,?) 순서대로 [userId, password] 값 전달
client.query(sql, [userId, data], () => {
//redirect 함수로 매개변수 url 해당 경로로 이동시켜준다.
res.redirect("/");
});
});
암호화 값과 비교
const sql = "SELECT * FROM users WHERE user_id=?";
client.query(sql, [userId], (err, result) => {
if (err) {
res.send("계정 없음");
} else {
//result[0]에 값이 있으면 계정이 존재한다는 뜻. 아니면 계정이 없다.
// ?. 구문 뒤에 키값이 잇는지 먼저보고 값을 참조한다. 그래서 없으면 터지는 일을 방지
if (result[0]) {
bcrypt.compare(password, result[0]?.password, (err, same) => {
if (same) {
암호화한 값과 동일한 경우 로그인성공
}
}
}}
}
'개발 > node.js' 카테고리의 다른 글
[Node.js] mysql FOREIGN KEY (0) | 2022.08.18 |
---|---|
[Node.js] 로그인 만들기 (0) | 2022.08.18 |
[Node.js] access token, refresh token 을 활용하여 로그인 유지시키기 (0) | 2022.08.16 |
[Node.js] 로그인 Access Token, Refresh Token (0) | 2022.08.16 |
[Node.js] exports router (0) | 2022.08.10 |