ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Hash - 금융 보안에서의 중요성
    CS 2025. 12. 2. 19:28

    목차

    1. 해시는 왜 금융 보안에서 중요한가?
    2. 해시 함수 기본 개념 정리
      2-1. 단방향성
      2-2. 충돌 저항성
      2-3. 눈사태 효과
    3. 비밀번호 저장에 쓰이는 해시 (고객 정보 보호)
      3-1. 평문 저장의 위험
      3-2. 솔트 + 해시 + 반복(PBKDF2)
      3-3. 파이썬 예제: 회원가입/로그인 비밀번호 검증
    4. 거래 데이터 무결성 검증 (전문 위변조 방지)
      4-1. 전송 중 데이터가 바뀌지 않았는지 확인하기
      4-2. 해시값을 이용한 무결성 체크
      4-3. 파이썬 예제: 파일/전문 무결성 검증
    5. 전자서명과 해시 (계약, 대출, 공인문서)
      5-1. 전자서명에서 해시가 하는 일
      5-2. 해시를 먼저 하고, 그다음에 서명하는 이유
      5-3. 파이썬 예제: “서명 전 단계” 해시 처리
    6. API 인증에서의 HMAC 해시 (오픈뱅킹, 내부 API)
      6-1. 단순 토큰보다 HMAC이 안전한 이유
      6-2. 요청 본문 + 시크릿키 조합
      6-3. 파이썬 예제: HMAC 기반 요청 서명
    7. 블록체인과 해시 (거래 이력 위조를 어렵게 만드는 장치)
      7-1. 블록 헤더와 이전 해시 연결
      7-2. 하나만 바꿔도 줄줄이 전부 깨지는 구조
      7-3. 파이썬 예제: 장난감 블록체인
    8. 금융권에서 해시를 안전하게 쓰기 위한 팁
      8-1. 빠른 해시(SHA-256)를 그대로 비밀번호에 쓰면 안 되는 이유
      8-2. 솔트, 키 스트레칭(PBKDF2, bcrypt, Argon2 등)
      8-3. 직접 암호 알고리즘 만들지 말 것
    9. 정리: 해시는 “숫자 바꾸는 함수”가 아니라 금융 보안의 기반

    1. 해시는 왜 금융 보안에서 중요한가?

    • 인터넷 뱅킹, 카드 결제, 오픈뱅킹 API, 신용평가 시스템까지 대부분의 금융 서비스는 “데이터”와 “신원”을 다룹니다.
    • 이때 중요한 건 (1) 정보가 중간에 바뀌지 않았는지, 그리고 (2) 누군가의 비밀 정보(비밀번호, 키)가 그대로 노출되지 않는지입니다.
    • 해시(hash)는 이 두 가지를 모두 도와주는, 금융 보안의 핵심 도구 중 하나입니다.
      • 비밀번호를 “원래 값으로 되돌릴 수 없게” 저장
      • 전송된 메시지가 중간에 바뀌지 않았는지 검증
      • 전자서명, 블록체인, API 인증 등에서 핵심 역할

    2. 해시 함수 기본 개념 정리

    2-1. 단방향성

    • 입력 → 해시값은 쉽지만, 해시값 → 입력은 사실상 불가능해야 합니다.
    • 이 성질 덕분에 비밀번호를 “복호화 가능한 암호”가 아니라 “해시값”으로 저장합니다.
    import hashlib
    
    password = "MySecurePassword123!"
    hashed = hashlib.sha256(password.encode("utf-8")).hexdigest()
    
    print("원본 비밀번호:", password)
    print("SHA-256 해시:", hashed)

    2-2. 충돌 저항성

    • 서로 다른 두 입력이 같은 해시값을 갖지 않도록 만드는 성질입니다.
    • 완전히 불가능한 것은 아니지만, 현실적으로 찾기 매우 어렵도록 설계됩니다.

    2-3. 눈사태 효과(Avalanche Effect)

    • 입력을 아주 조금만 바꿔도 해시값이 완전히 달라져야 합니다.
    m1 = hashlib.sha256(b"transfer 10000 to A").hexdigest()
    m2 = hashlib.sha256(b"transfer 10001 to A").hexdigest()
    
    print("10000원 전송:", m1)
    print("10001원 전송:", m2)

     

     


    3. 비밀번호 저장에 쓰이는 해시 (고객 정보 보호)

    3-1. 평문 저장의 위험

    • DB에 password = "abcd1234" 이런 식으로 저장돼 있다면, DB가 한 번 털리는 순간 모든 계정이 즉시 위험해집니다.
    • 그래서 금융권에서는 비밀번호를 절대 평문으로 저장하지 않습니다.

    3-2. 솔트 + 해시 + 반복(PBKDF2)

    • 단순히 SHA-256(password)만 쓰면, 공격자가 미리 해시값을 만들어둔 레인보우 테이블로 쉽게 역추적할 수 있습니다.
    • 그래서 보통:
      1. 사용자마다 랜덤한 솔트(salt) 생성
      2. PBKDF2, bcrypt, Argon2 같은 **느린 해시(KDF)**로 수천~수만 번 반복 -> 여기서 느린 해시는 일부러 계산 비용을 크게 만든 해시 함수를 말한다. 
      3. salt와 해시 결과를 같이 저장

    3-3. 파이썬 예제: 회원가입/로그인 비밀번호 검증 (PBKDF2 사용)

    import os
    import hashlib
    import hmac
    
    # 회원가입 시 비밀번호 해시 생성
    def hash_password(password: str, iterations: int = 100_000) -> dict:
        salt = os.urandom(16)  # 16바이트 랜덤 솔트
        dk = hashlib.pbkdf2_hmac(
            "sha256",
            password.encode("utf-8"),
            salt,
            iterations
        )
        return {
            "salt": salt.hex(),
            "iterations": iterations,
            "hash": dk.hex()
        }
    
    # 로그인 시 비밀번호 검증
    def verify_password(password: str, stored: dict) -> bool:
        salt = bytes.fromhex(stored["salt"])
        iterations = stored["iterations"]
        expected_hash = bytes.fromhex(stored["hash"])
    
        dk = hashlib.pbkdf2_hmac(
            "sha256",
            password.encode("utf-8"),
            salt,
            iterations
        )
        # 타이밍 공격 방지를 위한 안전한 비교
        return hmac.compare_digest(dk, expected_hash)
    
    # 사용 예시
    if __name__ == "__main__":
        # 회원가입 단계
        stored_info = hash_password("MySecurePassword123!")
        print("DB에 저장할 정보:", stored_info)
    
        # 로그인 단계
        print("맞는 비밀번호?", verify_password("MySecurePassword123!", stored_info))
        print("틀린 비밀번호?", verify_password("WrongPassword", stored_info))

     


    4. 거래 데이터 무결성 검증 (전문 위변조 방지)

    4-1. 전송 중 데이터가 바뀌지 않았는지 확인하기

    • 예:
      • A → B로 10,000원을 보내는 전문이 네트워크를 타고 가는 동안
      • 누군가가 중간에서 값을 100,000으로 바꾸려 한다면?
    • 서버 입장에선 “이 메시지가 원래 보낸 그대로인지” 확인할 방법이 필요합니다.

    4-2. 해시값을 이용한 무결성 체크

    • 메시지와 함께 해시값을 같이 전송하고, 수신 측에서 다시 해시를 계산해 비교합니다.
    • 다만, 이 해시값 자체도 보호해줘야 하므로 실제 시스템에서는 전자서명이나 HMAC과 같이 사용합니다.

    4-3. 파이썬 예제: 파일/전문 무결성 검증

    import hashlib
    
    def calc_sha256(data: bytes) -> str:
        return hashlib.sha256(data).hexdigest()
    
    # 송신자
    message = b"transfer 10000 KRW from A to B"
    msg_hash = calc_sha256(message)
    print("전송할 데이터:", message)
    print("전송할 해시 :", msg_hash)
    
    # 수신자
    received_message = message  # 정상 케이스라 가정
    received_hash = msg_hash
    
    verify_hash = calc_sha256(received_message)
    if verify_hash == received_hash:
        print("무결성 OK: 데이터가 변조되지 않았습니다.")
    else:
        print("무결성 FAIL: 데이터가 중간에 바뀌었습니다.")

     

     

    5. 전자서명과 해시 (계약, 대출, 공인문서)

    5-1. 전자서명에서 해시가 하는 일

    • 전자서명은 보통:
      1. 문서 내용을 해시로 압축
      2. 그 해시값에 개인키(private key)로 서명
    • 이렇게 하면 문서가 조금이라도 바뀌면 해시가 완전히 달라져서, 서명이 무효가 됩니다.

    5-2. 해시를 먼저 하고, 그다음에 서명하는 이유

    • 문서가 몇 MB, 몇십 MB가 되어도, 해시값은 항상 고정 길이(예: 256비트)라서 서명 연산이 빨라집니다.
    • 동시에, 해시의 눈사태 효과 덕분에 위변조를 아주 잘 탐지할 수 있습니다.

    5-3. 파이썬 예제: “서명 전 단계” 해시 처리

    실제 전자서명은 RSA, ECDSA 같은 비대칭키 알고리즘이 필요하지만, 여기서는 “서명에 들어갈 해시를 만드는 단계”만 단순화해서 보여줄 수 있습니다.

    import hashlib
    
    document = """
    대출 계약서
    - 고객명: 홍길동
    - 금액: 50,000,000 KRW
    - 기간: 3년
    """
    
    # 전자서명 전에 문서 내용을 해시로 압축
    doc_hash = hashlib.sha256(document.encode("utf-8")).hexdigest()
    print("문서 SHA-256 해시:", doc_hash)
    
    # 실제 시스템에서는 이 해시(doc_hash)를 개인키로 서명해서 전자서명을 생성
    # signature = sign_with_private_key(doc_hash)
    # 검증 시에는 공개키로 서명을 검증하고, 문서를 다시 해싱해 해시가 같은지 비교

     

     


    6. API 인증에서의 HMAC 해시 (오픈뱅킹, 내부 API)

    6-1. 단순 토큰보다 HMAC이 안전한 이유

    • 그냥 Authorization: Bearer <token> 형태만 쓰는 것보다,
    • 요청 내용 + 시크릿 키를 같이 해시한 HMAC 서명을 쓰면:
      • 중간자 공격자가 요청을 수정해도 서명이 틀어져서 바로 탐지 가능
      • 서버와 클라이언트만 아는 키로 검증하므로, 외부에서 위조하기 매우 어려움

    6-2. 요청 본문 + 시크릿키 조합

    • 보통 다음 정보들을 붙여서 HMAC 해시:
      • HTTP 메서드 (GET/POST…)
      • 요청 URL
      • 타임스탬프
      • 요청 본문(body)

    6-3. 파이썬 예제: HMAC 기반 요청 서명

    import hmac
    import hashlib
    import time
    
    API_SECRET = b"my-very-secret-key"
    
    def sign_request(method: str, path: str, body: str, timestamp: str) -> str:
        message = f"{method}\n{path}\n{timestamp}\n{body}".encode("utf-8")
        signature = hmac.new(API_SECRET, message, hashlib.sha256).hexdigest()
        return signature
    
    # 클라이언트에서 요청 보낼 때
    method = "POST"
    path = "/api/v1/transfer"
    body = '{"from":"A","to":"B","amount":10000}'
    timestamp = str(int(time.time()))
    
    signature = sign_request(method, path, body, timestamp)
    print("요청 시 포함할 헤더 예시:")
    print("X-Timestamp:", timestamp)
    print("X-Signature:", signature)
    
    # 서버에서 검증할 때도 동일한 방식으로 sign_request를 호출해서 값 비교

     


    7. 블록체인과 해시 (거래 이력 위조를 어렵게 만드는 장치)

    7-1. 블록 헤더와 이전 해시 연결

    • 블록체인의 블록은 보통:
      • 현재 블록의 거래 데이터
      • 이전 블록의 해시값
      • 기타 메타데이터를 포함합니다.
    • 이 구조 때문에, 과거의 거래를 조금만 바꿔도 그 이후 모든 블록의 해시가 달라져서 전체 체인이 깨지게 됩니다.

    7-2. 하나만 바꿔도 줄줄이 전부 깨지는 구조

    • 길게 연결된 도미노처럼, 앞쪽 블록을 건드리면 뒤쪽 블록까지 모두 영향이 가기 때문에 조작이 매우 어렵습니다.

    7-3. 파이썬 예제: 장난감 블록체인

    import hashlib
    from dataclasses import dataclass
    
    def sha256_hex(data: str) -> str:
        return hashlib.sha256(data.encode("utf-8")).hexdigest()
    
    @dataclass
    class Block:
        index: int
        prev_hash: str
        data: str
    
        @property
        def hash(self) -> str:
            content = f"{self.index}{self.prev_hash}{self.data}"
            return sha256_hex(content)
    
    # 체인 생성
    genesis = Block(index=0, prev_hash="0" * 64, data="Genesis Block")
    block1 = Block(index=1, prev_hash=genesis.hash, data="A -> B : 10000")
    block2 = Block(index=2, prev_hash=block1.hash, data="B -> C : 5000")
    
    chain = [genesis, block1, block2]
    
    print("=== 정상 체인 ===")
    for b in chain:
        print(b.index, b.data, "hash:", b.hash, "prev_hash:", b.prev_hash)
    
    # 중간 블록 데이터 조작
    print("\n=== block1 데이터 조작 후 ===")
    block1.data = "A -> B : 1000000"  # 10000 -> 1000000 으로 바꾸기
    for b in chain:
        print(b.index, b.data, "hash:", b.hash, "prev_hash:", b.prev_hash)
    
    # block2.prev_hash 는 여전히 '예전 block1.hash' 를 가리키기 때문에, 실제 시스템이라면 검증 실패

     


    8. 금융권에서 해시를 안전하게 쓰기 위한다면?

    8-1. 빠른 해시(SHA-256)를 그대로 비밀번호에 쓰면 안 되는 이유

    • SHA-256은 빠르게 설계된 해시입니다.
    • 공격자도 GPU/ASIC으로 초당 수십억 번 시도할 수 있어서, 비밀번호 해싱에는 적합하지 않습니다.

    8-2. 솔트, 키 스트레칭(PBKDF2, bcrypt, Argon2 등)

    • 각 사용자마다 랜덤 솔트 사용
    • 수천~수만 번 반복해서 계산 시간을 늘리기 (PBKDF2, bcrypt, Argon2 등)
    • 이렇게 해야 공격자가 비밀번호 하나를 깨는 데도 큰 비용이 들게 됩니다.

    8-3. 직접 암호 알고리즘 만들지 말 것

    • 검증되지 않은 “자작 암호”는 거의 항상 취약합니다.
    • 표준화된 알고리즘과 검증된 라이브러리를 사용하는 것이 기본입니다.

     

    'CS' 카테고리의 다른 글

    Apache Spark  (0) 2025.11.26
    Apache Kafka  (0) 2025.11.26
    면접을 위한 JAVA 정리  (0) 2025.11.17
    면접을 위한 C 정리  (0) 2025.11.17
    면접을 위한 DB 정리  (0) 2025.11.14
Designed by Tistory.