2025년 April 7일
  1. 개발 히스토리
  2. [Backend] 비밀번호 암호화

[Backend] 비밀번호 암호화

비밀번호 암호화

기존 DB에서는 회원 테이블에서 비밀번호를 암호화하지 않은 채, 원본 그대로를 저장하고 있었습니다.
그러나 이러한 방식은 보안상의 문제가 있기 때문에 비밀번호를 암호화했습니다.

Bcrypt

현재 비밀번호 암호화를 위해 Bcrypt를 사용했습니다.
Bcrypt는 해시 알고리즘을 통해 데이터를 암호화합니다.
데이터를 암호화한 이후 원본으로 복원하는 것이 불가능한 단방향 구조이기 때문에, 기존 방식보다 보안이 강화됩니다.

CRUD 코드

암호화 및 검증 함수

import bcrypt

def hash_password(password: str) -> str:
    return pwd_context.hash(password)

def verify_password(plain_password: str, hashed_password: str) -> bool:
    return pwd_context.verify(plain_password, hashed_password)

hash_password는 암호화된 비밀번호를 얻기 위한 함수,
verify_password는 로그인할 때 암호화된 비밀번호를 원본으로 다시 해석하기 위한 함수입니다.

회원가입

def user_register(db : Session, email : str, pw : str, name : str):
    hashed_pw = hash_password(pw)
    new_user = User(
        email = email,
        password=hashed_pw,
        name = name,
        role = 'user',
        group = 'newUser',
        register_at=datetime.utcnow()
    )
    db.add(new_user)
    db.commit()
    db.refresh(new_user)
    return new_user

그리고 회원가입 함수에서 입력된 password를 그대로 저장하는 것이 아니라, hash_password로 한 번 암호화를 거쳐 저장합니다.

로그인

def user_login(db: Session, email: str, pw: str):
    user = db.query(User).filter(User.email == email).first()
    if not user:
        return None

    if user.password.startswith("$2b$"):  # bcrypt 해시 형식이면
        if verify_password(pw, user.password):
            return {
                "id": user.id,
                "email": user.email,
                "name": user.name,
                "role": user.role
            }
    else:
        # 암호화   경우 (기존 방식) 평문 비교
        if user.password == pw:
            # 로그인 성공  비밀번호 해시로 업데이트 (마이그레이션)
            user.password = hash_password(pw)
            db.commit()
            return {
                "id": user.id,
                "email": user.email,
                "name": user.name,
                "role": user.role
            }

    return None

로그인 함수의 경우 두 가지 조건에 따라 실행됩니다.
우선 이미 비밀번호 암호화가 제대로 되어있는 계정의 경우 비밀번호의 앞자리에 ‘$2b$’가 붙습니다.
그렇기 때문에 ‘$2b$’로 시작하는 비밀번호의 경우 이미 암호화된 것으로 간주하여 verify_password를 통한 검증을 수행합니다.

반대로 그렇지 않은 비밀번호의 경우,
아직 암호화가 안 된 것으로 판단하여 DB에서 그대로 일치하는 비밀번호를 찾아 로그인합니다.
이 경우는 로그인이 성공한 이후 다시 암호화된 비밀번호로 업데이트하여, 보안을 강화합니다.

Google 연동

랜덤 함수

import random

def generate_random_password():
    random_number = ''.join([str(random.randint(0, 9)) for _ in range(8)])
    return f'default_password{random_number}'

Google 연동 로그인의 경우,
기본적으로 ‘default_password’라는 값으로 비밀번호가 저장되어서 보안에 취약했습니다. 그렇기 때문에 Google 비밀번호를 랜덤으로 생성하기 위한 Generate Random Password 함수를 사용합니다.

Google 연동 계정 생성

def create_google_user(db : Session, email : str, name : str):
    password = generate_random_password()
    hashed_pw = hash_password(password)
    new_user = User(
        email = email,
        password = hashed_pw,
        name = name,
        role = 'googleUser',
        group = 'newUser',
        register_at = datetime.utcnow()
    )
    db.add(new_user)
    db.commit()
    db.refresh(new_user)
    return new_user

Google 연동 계정이 생성될 때,
이제 랜덤 비밀번호가 암호화를 거친 후 저장됩니다.

적용 이후 테이블

이러한 과정을 거치면, 회원 정보 테이블의 모든 비밀번호들은 원본을 확인할 수 없는 단방향 해시 형태의 암호로 저장됩니다.

Leave a Reply

Your email address will not be published. Required fields are marked *

연관 글
BCT NEWS
인기 글
워드프레스 보안
워드프레스 모음
워드프레스 제작 팁