미새문지

24.08.01 day38 Base64 인코딩, 패스워드를 전송하고 보관하는 방법 본문

개발 TIL

24.08.01 day38 Base64 인코딩, 패스워드를 전송하고 보관하는 방법

문미새 2024. 8. 1. 22:32
728x90

Base64 인코딩

Base64 인코딩은 바이너리 데이터를 ASCII 텍스트로 변환하는 인코딩 방식이며, 이는 데이터를 텍스트 기반 시스템(예: 이메일, JSON, XML 등)에서 쉽게 전송하고 저장할 수 있도록 하기 위해 사용된다.

Base64는 바이너리 데이터를 텍스트로 변환할 때 데이터 크기를 약 33% 정도 증가시킨다.

 

인코딩 과정

  1. 바이너리 데이터 분할: 원본 바이너리 데이터를 3바이트(24비트)씩 나눈다.
  2. 비트 그룹 변환: 각 3바이트 블록을 6비트씩 4개의 그룹으로 나눕니다. 6비트는 2^6 = 64개의 값(0부터 63까지)을 가질 수 있다.
  3. Base64 인덱스 매핑: 각 6비트 그룹을 Base64 인덱스 테이블의 값으로 변환하는데, 이 테이블은 " A-Z (0-25)", "a-z (26-51)", "0-9 (52-61)", "+ (62)", "/ (63)"의 문자 집합을 가진다.
  4. 패딩 추가: 원본 데이터의 길이가 3의 배수가 아닐 경우, 남은 바이트를 처리하기 위해 '=' 문자를 사용하여 패딩을 추가한다.

디코딩 과정

Base64로 인코딩된 문자열을 디코딩하는 과정은 인코딩의 역순이다

  1. 패딩 제거: 문자열 끝에 있는 '=' 문자를 제거
  2. Base64 인덱스 역매핑: 각 문자에 대해 Base64 인덱스 테이블을 사용하여 6비트 값으로 변환
  3. 비트 병합: 6비트 그룹을 다시 8비트(1바이트) 그룹으로 병합
  4. 바이너리 데이터 재구성: 병합된 비트를 원본 바이너리 데이터로 복원

예제

"hello"라는 문자열을 Base64로 인코딩하는 예제이다.

 

1. 바이너리 데이터: "hello"의 ASCII 값은 104 101 108 108 111

2. 2진수 변환: 각 바이트를 8비트 2진수로 변환

104 -> 01101000
101 -> 01100101
108 -> 01101100
108 -> 01101100
111 -> 01101111

 

3. 3바이트 그룹화 및 6비트 분할

01101000 01100101 01101100 -> 011010 000110 010101 101100
01101100 01101111 -> 011011 000110 1111 (남은 4비트는 0으로 패딩)

4. Base64 인덱스 매핑

011010 -> a
000110 -> G
010101 -> V
101100 -> s
011011 -> b
000110 -> G
111100 -> 8 (추가된 0비트 포함)

결과적으로, "hello"는 "aGVsbG8="로 인코딩된다.

 

활용 사례

  • 이메일 첨부파일: MIME(Multipurpose Internet Mail Extensions)에서 바이너리 파일을 텍스트로 인코딩하기 위해 사용된다.
  • 데이터 URI 스키마: 이미지를 Base64로 인코딩하여 HTML/CSS에 직접 포함시킬 수 있다.
  • 웹 API: JSON 및 XML과 같은 텍스트 포맷으로 바이너리 데이터를 주고받을 때 사용된다.

Base64 인코딩은 데이터를 안전하고 호환성 있게 전송하기 위해 널리 사용되며, 특히 텍스트 기반 시스템에서 유용할 수 있다.


사용자 패스워드를 전송하고 보관하는 방법

사용자 패스워드를 전송하고 보관하는 것은 보안에 있어서 매우 중요한 문제인데, 이를 안전하게 처리하기 위해 여러 가지 기술을 사용할 수 있다. 

패스워드 전송 방법

  1. HTTPS 사용
    • 패스워드를 전송할 때는 항상 HTTPS(SSL/TLS)를 사용하여 전송해야 하며, HTTPS는 데이터가 전송되는 동안 도청 및 변조를 방지할 수 있다.
    • HTTPS를 사용하면 클라이언트와 서버 간의 통신이 암호화되어 네트워크를 통한 패스워드 유출 위험이 줄어든다.
  2. 전송 중 암호화
    • 패스워드를 전송하기 전에 클라이언트 측에서 암호화할 수 있으며, 이를 통해 서버로 전송되는 패스워드가 더 안전하게 보호된다.
    • 그러나 서버가 암호화된 패스워드를 복호화해야 하는 경우, 서버 측의 보안 관리가 더 복잡해질 수 있다.

패스워드 보관 방법

해싱(Hashing)

  • 패스워드를 원문 그대로 보관하는 것은 매우 위험하기 때문에, 패스워드를 해시 함수로 변환하여 저장한다.
  • 강력한 해시 함수 사용: SHA-256, SHA-3 등의 강력한 해시 함수를 사용한다. MD5, SHA-1 등은 안전하지 않으므로 사용하지 않는다.
  • 솔팅(Salting): 해시 함수에 솔트를 추가하여 해시를 생성한다. 솔트는 각 사용자마다 고유한 값으로, 같은 패스워드라도 다른 해시값을 가지도록 할 수 있으며, 이를 통해 무차별 대입 공격 및 레인보우 테이블 공격을 방어할 수 있다.
  • 페퍼링(Peppering): 솔트와 유사하지만, 서버에 저장하지 않고 애플리케이션 코드에 내장된 비밀 값을 추가로 사용한다.

예시

import bcrypt

# 패스워드 해싱
password = b"super_secret_password"
hashed_password = bcrypt.hashpw(password, bcrypt.gensalt())

# 패스워드 검증
def check_password(stored_hash, user_password):
    return bcrypt.checkpw(user_password, stored_hash)

 

KDF(Key Derivation Functions)

  • 해시 함수 대신 패스워드 기반 키 도출 함수(PBKDF2, scrypt, Argon2 등)를 사용하며, 이는 반복적으로 해시를 적용하여 계산을 더 복잡하게 만들어 무차별 대입 공격을 어렵게 한다.
  • Argon2: 현재 가장 추천되는 패스워드 해싱 알고리즘 중 하나이다.

예시

from argon2 import PasswordHasher

# 패스워드 해싱
ph = PasswordHasher()
hashed_password = ph.hash("super_secret_password")

# 패스워드 검증
def check_password(stored_hash, user_password):
    try:
        ph.verify(stored_hash, user_password)
        return True
    except VerificationError:
        return False

 

추가 보안 방법

  1. 이중 인증(Two-Factor Authentication, 2FA)
    • 패스워드 외에 추가적인 인증 수단을 사용하여 보안을 강화한다.
    • 예를 들어, SMS 코드, OTP(One-Time Password) 앱 등을 사용할 수 있다.
  2. 계정 잠금(Account Lockout)
    • 일정 횟수 이상 잘못된 로그인 시도를 할 경우 계정을 잠금 처리하여 무차별 대입 공격을 방어한다.
  3. 주기적인 패스워드 변경
    • 사용자가 주기적으로 패스워드를 변경하도록 유도하여 보안을 강화한다.
  4. 패스워드 정책
    • 강력한 패스워드 정책을 설정하여 사용자가 최소한의 복잡성을 가지는 패스워드를 사용하도록 한다.
    • 예를 들어, 최소 8자 이상, 대문자, 소문자, 숫자, 특수문자 포함 등의 거지같은 패스워드 정책을 따른다.

사용자 패스워드의 전송과 보관은 매우 중요한 보안 문제이며, HTTPS를 사용하여 전송을 암호화하고, 안전한 해시 함수 및 KDF를 사용하여 패스워드를 해싱하여 저장하는 것이 기본적인 보안 방법이다. 또한 추가적인 보안 방법을 함께 사용하여 보안을 강화할 수 있다.

728x90