비밀번호 해시 알고리즘 적용
1. 기존 상태
기존에는 비밀번호는 암호화를 적용하지 않는 raw 상태로 저장되어 있습니다. DB의 `members` 테이블을 SELECT로 조회를 하면 user_password에서 확인할 수 있습니다.
2. 적용하기
1) 의존성 추가
jBCrypt는 Java에서 BCrypt 해시 알고리즘을 구현한 라이브러리입니다. 해시함수를 사용해서 비밀번호를 암호화하고, 쉽게 복호화할 수 없게 설계되어 있습니다. 무작위 솔트(salt)와 반복적인 해시 처리를 통해서 무차별 대입 공격(Brute-force attack)을 방지할 수 있습니다.
<dependency>
<groupId>org.mindrot</groupId>
<artifactId>jbcrypt</artifactId>
<version>0.4</version>
</dependency>
3) 비밀번호 저장 테이블 수정
비밀번호를 해싱하면 변환된 해시로 저장하기 때문에 비밀번호의 길이가 길어집니다. 따라서 데이터베이의 user_password의 길이도 변경되어야 합니다.
일반적으로 VARCHAR(60) 이상을 권장합니다.
ALTER TABLE members MODIFY user_password VARCHAR(60);
4) 유틸리티 클래스 구현
비밀번호를 해싱하고 검증하는 유틸리티 클래스를 구현합니다.
package com.ssafy.util;
import org.mindrot.jbcrypt.BCrypt;
public class PasswordUtil {
// 비밀번호 해시 생성
public static String hashPassword(String plainPassword) {
return BCrypt.hashpw(plainPassword, BCrypt.gensalt());
}
// 비밀번호 검증
public static boolean checkPassword(String plainPassword, String hashedPassword) {
return BCrypt.checkpw(plainPassword, hashedPassword);
}
}
5) 회원가입 시 비밀번호 암호화 저장
회원가입 시 입력된 비밀번호를 해싱해서 데이터베이스에 저장하도록 MemberControlle를 수정합니다.
MemberController에서 비밀번호를 해싱해서 서비스로 전달하도록 수정합니다.
// 비밀번호 해싱
String plainPassword = request.getParameter("userpwd");
String hashedPawword = PasswordUtil.hashPassword(plainPassword);
memberDto.setUserPwd(hashedPawword);
6) 로그인 시 비밀번호 검증
사용자가 로그인 시 입력된 비밀번호와 데이터베이스에 해시로 저장된 비밀번호를 비교하도록 MemberServiceImpl과 MemberServiceImpl을 수정합니다.
import com.ssafy.util.PasswordUtil;
// ...
@Override
public MemberDto loginMember(String userId, String userPwd) throws Exception {
MemberDto memberDto = memberDao.getMemberById(userId);
if (memberDto != null) {
String hashedPassword = memberDto.getUserPwd();
if (PasswordUtil.checkPassword(userPwd, hashedPassword)) {
return memberDto;
}
}
return null;
}
loginMember 메소드를 사용자 ID로만 조회하도록 변경하고, 비밀번호 검증은 서비스 계층에서 수행하도록 변경합니다.
@Override
public MemberDto getMemberById(String userId) throws SQLException {
MemberDto memberDto = null;
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = dbUtil.getConnection();
StringBuilder sql = new StringBuilder();
sql.append("SELECT * FROM members WHERE user_id = ?");
pstmt = conn.prepareStatement(sql.toString());
pstmt.setString(1, userId);
rs = pstmt.executeQuery();
if (rs.next()) {
memberDto = new MemberDto();
memberDto.setUserId(rs.getString("user_id"));
// 해시
// 해시된 비밀번호
memberDto.setUserPwd(rs.getString("user_password"));
memberDto.setUserName(rs.getString("user_name"));
memberDto.setEmailId(rs.getString("email_id"));
memberDto.setEmailDomain(rs.getString("email_domain"));
memberDto.setJoinDate(rs.getString("join_date"));
}
} finally {
dbUtil.close(rs, pstmt, conn);
}
return memberDto;
}
7) 구현 결과 확인
데이터베이스에서 해시로 비밀번호가 설정되어 있는지 확인합니다.
댓글