-
Hash - 금융 보안에서의 중요성CS 2025. 12. 2. 19:28
목차
- 해시는 왜 금융 보안에서 중요한가?
- 해시 함수 기본 개념 정리
2-1. 단방향성
2-2. 충돌 저항성
2-3. 눈사태 효과 - 비밀번호 저장에 쓰이는 해시 (고객 정보 보호)
3-1. 평문 저장의 위험
3-2. 솔트 + 해시 + 반복(PBKDF2)
3-3. 파이썬 예제: 회원가입/로그인 비밀번호 검증 - 거래 데이터 무결성 검증 (전문 위변조 방지)
4-1. 전송 중 데이터가 바뀌지 않았는지 확인하기
4-2. 해시값을 이용한 무결성 체크
4-3. 파이썬 예제: 파일/전문 무결성 검증 - 전자서명과 해시 (계약, 대출, 공인문서)
5-1. 전자서명에서 해시가 하는 일
5-2. 해시를 먼저 하고, 그다음에 서명하는 이유
5-3. 파이썬 예제: “서명 전 단계” 해시 처리 - API 인증에서의 HMAC 해시 (오픈뱅킹, 내부 API)
6-1. 단순 토큰보다 HMAC이 안전한 이유
6-2. 요청 본문 + 시크릿키 조합
6-3. 파이썬 예제: HMAC 기반 요청 서명 - 블록체인과 해시 (거래 이력 위조를 어렵게 만드는 장치)
7-1. 블록 헤더와 이전 해시 연결
7-2. 하나만 바꿔도 줄줄이 전부 깨지는 구조
7-3. 파이썬 예제: 장난감 블록체인 - 금융권에서 해시를 안전하게 쓰기 위한 팁
8-1. 빠른 해시(SHA-256)를 그대로 비밀번호에 쓰면 안 되는 이유
8-2. 솔트, 키 스트레칭(PBKDF2, bcrypt, Argon2 등)
8-3. 직접 암호 알고리즘 만들지 말 것 - 정리: 해시는 “숫자 바꾸는 함수”가 아니라 금융 보안의 기반
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)만 쓰면, 공격자가 미리 해시값을 만들어둔 레인보우 테이블로 쉽게 역추적할 수 있습니다.
- 그래서 보통:
- 사용자마다 랜덤한 솔트(salt) 생성
- PBKDF2, bcrypt, Argon2 같은 **느린 해시(KDF)**로 수천~수만 번 반복 -> 여기서 느린 해시는 일부러 계산 비용을 크게 만든 해시 함수를 말한다.
- 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. 전자서명에서 해시가 하는 일
- 전자서명은 보통:
- 문서 내용을 해시로 압축
- 그 해시값에 개인키(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