안녕하세요!!
다들 아시다시피 로그인 시스템에서 사용자 정보 보안은 무엇보다 중요합니다.
따라서 개발자들은 최신 보안 기술을 적용해 안전한 인증 시스템을 구축해야 합니다.
이번 포스트에서는 해시와 Salt의 기본 개념부터 최신 암호화 로그인 방법까지, 그리고 각 방법에 대한 예제 코드까지 함께 살펴보겠습니다.
요즘 다시 한번 로그인 로직 부분을 분석하다 보니 트렌드를 공유하고 싶어 포스팅을 합니다!!
1. 해시와 Salt: 기본부터 이해하기
해시(hash)와 Salt는 안전한 비밀번호 저장을 위한 핵심 요소입니다.
- 해시(Hash): 비밀번호를 고정된 길이의 암호화된 문자열로 변환하여 저장합니다. 해시는 단방향 함수로, 한 번 해시된 값은 원래의 비밀번호로 복원할 수 없습니다.
- Salt: 각 사용자마다 고유한 임의의 문자열을 생성하여 비밀번호에 추가합니다. 이를 통해 동일한 비밀번호라도 서로 다른 해시 값을 가지게 되어 무차별 대입 공격(Brute-force attack)을 방어할 수 있습니다.
해시와 Salt를 사용하면 데이터베이스가 해킹되더라도 실제 비밀번호를 알아내기 어렵게 만들 수 있습니다.
암호화 로그인 예제 코드
아래는 Java로 작성된 해시와 Salt를 사용한 비밀번호 암호화 예제 코드입니다.
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
public class EncryptionExample {
// Salt 생성 메서드
public static String getSalt() {
SecureRandom random = new SecureRandom();
byte[] salt = new byte[16];
random.nextBytes(salt);
return Base64.getEncoder().encodeToString(salt);
}
// SHA-256 암호화
public static String encodeSHA256(String password, String salt) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(salt.getBytes());
byte[] hashedPassword = md.digest(password.getBytes());
return Base64.getEncoder().encodeToString(hashedPassword);
}
// BCrypt로 추가 암호화
public static String encodeBCrypt(String hashedPassword) {
return BCrypt.hashpw(hashedPassword, BCrypt.gensalt(12));
}
public static void main(String[] args) throws NoSuchAlgorithmException {
String password = "myPassword";
String salt = getSalt();
String sha256Password = encodeSHA256(password, salt);
String bcryptPassword = encodeBCrypt(sha256Password);
System.out.println("Salt: " + salt);
System.out.println("SHA-256 Hashed Password: " + sha256Password);
System.out.println("BCrypt Password: " + bcryptPassword);
}
}
코드 설명:
- getSalt() 메서드를 통해 랜덤한 Salt를 생성합니다.
- encodeSHA256() 메서드에서 비밀번호와 Salt를 합쳐 SHA-256 해시를 만듭니다.
- encodeBCrypt() 메서드로 SHA-256 해시된 비밀번호를 BCrypt로 한 번 더 암호화합니다.
- 최종적으로 생성된 해시와 Salt를 데이터베이스에 저장합니다.
2. 최신 로그인 암호화 트렌드
보안 기술은 계속해서 발전하고 있으며, 로그인 시스템에서도 다양한 최신 기술이 적용되고 있습니다.
2.1 Argon2: 차세대 해시 알고리즘
Argon2는 2015년 비밀번호 해시 대회에서 우승한 알고리즘으로, 현재 가장 안전한 해시 함수 중 하나로 인정받고 있습니다. Argon2는 메모리 사용량과 실행 시간을 조절할 수 있어 GPU를 이용한 병렬 처리 공격에 강합니다.
Argon2 예제 코드
import de.mkammerer.argon2.Argon2Factory;
import de.mkammerer.argon2.Argon2;
public class Argon2Example {
public static void main(String[] args) {
String password = "myPassword";
// Argon2 인스턴스 생성
Argon2 argon2 = Argon2Factory.create();
// 비밀번호 해싱
String hash = argon2.hash(2, 65536, 1, password);
// 해싱된 비밀번호 출력
System.out.println("Argon2 Hashed Password: " + hash);
// 비밀번호 검증
boolean matches = argon2.verify(hash, password);
System.out.println("Password matches: " + matches);
}
}
코드 설명:
- argon2.hash(반복횟수, 메모리 사용량(KB), 병렬성, 비밀번호)로 해시를 생성합니다.
- argon2.verify(해시값, 비밀번호)로 비밀번호를 검증합니다.
※ 위 코드에서 비밀번호 해싱 부분의 65536은 Argon2 알고리즘에서 사용하는 메모리 비용 설정. 이 숫자는 Argon2가 해시를 생성할 때 사용할 메모리 양을 킬로바이트(KB) 단위로 나타내며, 다음과 같은 이유로 사용됨: 1. 보안 강화 : 높은 메모리 비용은 공격자가 해시를 추측하거나 해독하려고 할 때 더 많은 메모리 리소스를 필요로 하게 만듬. 일반적인 메모리 사용량보다 훨씬 높은 값을 설정함으로써, 공격자들이 쉽게 병렬 처리로 암호를 해독하지 못하게 하는 효과. 2. GPU 공격 방어 : Argon2는 메모리 중심 알고리즘으로, 메모리 비용이 높을수록 GPU를 사용하는 병렬 공격에 대한 방어가 강화됨. 65536 KB는 약 64 MB로, 일반적인 CPU에서는 큰 부담 없이 처리할 수 있지만, GPU나 ASIC과 같은 병렬 처리 장치에는 큰 부담 됨. 3. 최적의 성능과 보안성 : Argon2에서는 메모리 비용이 지나치게 높아지면 속도에 영향이 있을 수 있기 때문에, 65536 KB 정도는 적절한 보안성과 성능의 균형을 맞추기 위한 값으로 자주 사용됨. |
2.2 Passwordless 로그인
Passwordless 로그인은 비밀번호 없이 사용자 인증을 수행하는 방식입니다.
일반적으로 이메일이나 SMS로 전송된 일회용 코드 또는 링크를 통해 인증합니다.
Passwordless 예제 코드 (Node.js 예제)
const express = require('express');
const nodemailer = require('nodemailer');
const crypto = require('crypto');
const app = express();
let tokens = {};
app.get('/login', (req, res) => {
const email = req.query.email;
const token = crypto.randomBytes(32).toString('hex');
tokens[token] = email;
// 이메일 전송 설정
let transporter = nodemailer.createTransport({
service: 'Gmail',
auth: {
user: 'your_email@gmail.com',
pass: 'your_email_password'
}
});
// 이메일 전송
let mailOptions = {
from: 'your_email@gmail.com',
to: email,
subject: 'Login Link',
text: `로그인하려면 다음 링크를 클릭하세요: http://localhost:3000/verify?token=${token}`
};
transporter.sendMail(mailOptions, (error, info) => {
if (error) return res.status(500).send(error.toString());
res.send('로그인 링크가 이메일로 전송되었습니다.');
});
});
app.get('/verify', (req, res) => {
const token = req.query.token;
const email = tokens[token];
if (email) {
// 로그인 성공 처리
delete tokens[token];
res.send(`${email}님, 환영합니다!`);
} else {
res.send('유효하지 않거나 만료된 토큰입니다.');
}
});
app.listen(3000, () => {
console.log('Server started on port 3000');
});
코드 설명:
- 사용자가 이메일을 입력하고 /login 엔드포인트로 요청을 보냅니다.
- 서버는 랜덤 토큰을 생성하고 해당 이메일로 로그인 링크를 전송합니다.
- 사용자는 이메일의 링크를 클릭하여 /verify 엔드포인트로 이동합니다.
- 서버는 토큰을 검증하고 로그인 처리를 합니다.
2.3 멀티팩터 인증(MFA)
MFA는 비밀번호 외에 추가 인증 수단을 요구하여 보안을 강화합니다.
일반적으로 SMS 인증, OTP(일회용 비밀번호), 인증 앱 등이 사용됩니다.
MFA 예제 코드 (Google Authenticator 사용)
import de.taimos.totp.TOTP;
import org.apache.commons.codec.binary.Base32;
import org.apache.commons.codec.binary.Hex;
public class MFAExample {
public static void main(String[] args) {
String secretKey = "JBSWY3DPEHPK3PXP"; // 사용자의 비밀 키
// 현재 TOTP 코드 생성
String code = getTOTPCode(secretKey);
System.out.println("Current TOTP code: " + code);
}
public static String getTOTPCode(String secretKey) {
Base32 base32 = new Base32();
byte[] bytes = base32.decode(secretKey);
String hexKey = Hex.encodeHexString(bytes);
return TOTP.getOTP(hexKey);
}
}
코드 설명:
- 사용자는 앱에 제공된 QR 코드를 스캔하여 비밀 키를 저장합니다.
- 로그인 시 서버는 사용자가 입력한 TOTP 코드를 getTOTPCode() 메서드를 통해 생성된 코드와 비교합니다.
3. 추가 보안 팁: 멀티팩터 인증(MFA) 적용하기
암호화된 로그인과 함께 MFA를 적용하면 보안을 더욱 강화할 수 있습니다.
비밀번호 유출 시에도 추가 인증 단계를 거치기 때문에 계정 탈취를 방지할 수 있습니다.
자 그럼 안전한 로그인 시스템을 구축하여 사용자들의 소중한 정보를 지키세요!
'※ 소소한 IT' 카테고리의 다른 글
[Oracle] SQL 성능 최적화를 위한 Hint 활용법 (1) | 2024.11.15 |
---|---|
AI 코드 편집기 Cursor로 본 요즘 개발 트렌드 (0) | 2024.11.14 |
클라우드 조직의 트렌드로 SaaS와 IaaS 팀 구성의 최적화 방법 (0) | 2024.11.06 |
SaaS와 IaaS의 차이점 완벽 이해하기! 클라우드 서비스 선택 가이드 (1) | 2024.11.05 |
CentOS 기반 Postfix 메일 서버 구축 및 Java 연동 (0) | 2024.11.04 |