Hàm băm mật mã hàm nhận đầu vào có kích thước bất kỳ và tạo ra đầu ra có kích thước cố định (mã băm hoặc bản tóm tắt). Không giống như mã hóa, băm là hàm ****—bạn không thể khôi phục đầu vào ban đầu từ hàm băm. Điều này làm cho việc băm trở nên cần thiết để xác minh tính toàn vẹn, lưu trữ mật khẩu và chữ ký số.

Thuộc tính của hàm băm mật mã hàm

Hàm băm an toàn hàm phải có các thuộc tính sau:

Tài sản Mô tả Tại sao nó quan trọng
DQuyết định Đầu vào giống nhau luôn tạo ra đầu ra giống nhau Bật xác minh
Tính toán nhanh Băm nhanh bất kỳ kích thước đầu vào nào Thực tế cho các tập tin lớn
Pre-image kháng Không thể tìm thấy đầu vào từ hàm băm Bảo vệ dữ liệu gốc
Sức đề kháng thứ hai trước image Không thể tìm thấy đầu vào khác nhau có cùng hàm băm Ngăn chặn sự giả mạo
Chống va chạm Khó tìm thấy bất kỳ hai đầu vào nào có cùng hàm băm Đảm bảo tính độc đáo
Ahiệu ứng tuyết lở Thay đổi đầu vào nhỏ = hàm băm hoàn toàn khác Ẩn mẫu

Hiệu ứng tuyết lở

import hashlib

# Tiny change in input = completely different hash
hash1 = hashlib.sha256(b"Hello World").hexdigest()
hash2 = hashlib.sha256(b"Hello World!").hexdigest()  # Added '!'

print(f"'Hello World'  -> {hash1}")
print(f"'Hello World!' -> {hash2}")

# Output:
# 'Hello World'  -> a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e
# 'Hello World!' -> 7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069
# (Completely different despite one character change)

Thuật toán băm phổ biến

Thuật toán Kích thước đầu ra Trạng thái Trường hợp sử dụng
SHA-256 256 bit ✅ Được đề xuất Mục đích chung, tổng kiểm tra, blockchain
SHA-384 384 bit ✅ An toàn Yêu cầu bảo mật cao hơn
SHA-512 512 bit ✅ An toàn Dữ liệu lớn, tối ưu hóa 64-bit
SHA-3 Biến ✅ Được đề xuất Tiêu chuẩn NIST, thiết kế khác với SHA-2
BLAKE2 Biến ✅ Được đề xuất Nhanh hơn SHA-2, rất an toàn
BLAKE3 Biến ✅ Được đề xuất Nhanh nhất, song song
SHA-1 160 bit ❌ Hỏng Không bao giờ sử dụng để bảo mật
MD5 128 bit ❌ Hỏng Không bao giờ sử dụng để bảo mật

Tại sao MD5 và SHA-1 bị hỏng

Cả MD5 và SHA-1 đều có các cuộc tấn công va chạm thực tế:

  • MD5: Có thể tạo ra va chạm trong vài giây trên máy tính xách tay
  • SHA-1: Cuộc tấn công SHAttered (2017) đã tạo hai tệp PDF khác nhau với cùng hàm băm SHA-1
# ❌ NEVER use for security
md5_hash = hashlib.md5(data).hexdigest()    # Broken
sha1_hash = hashlib.sha1(data).hexdigest()  # Broken

# ✅ Use SHA-256 or better
sha256_hash = hashlib.sha256(data).hexdigest()  # Secure

Note: MD5 vẫn được chấp nhận cho các mục đích không liên quan đến bảo mật như tổng kiểm tra để phát hiện lỗi dữ liệu (không phải giả mạo độc hại).

Trường hợp sử dụng 1: Xác minh tính toàn vẹn của tệp

Băm xác minh rằng các tập tin chưa được sửa đổi:

import hashlib

def calculate_file_hash(filepath: str, algorithm: str = 'sha256') -> str:
    """Calculate hash of a file efficiently."""
    hash_func = hashlib.new(algorithm)
    
    with open(filepath, 'rb') as f:
        # Read in chunks for large files
        for chunk in iter(lambda: f.read(8192), b''):
            hash_func.update(chunk)
    
    return hash_func.hexdigest()

def verify_file_integrity(filepath: str, expected_hash: str, 
                          algorithm: str = 'sha256') -> bool:
    """Verify file matches expected hash."""
    actual_hash = calculate_file_hash(filepath, algorithm)
    return actual_hash == expected_hash

# Example: Verifying a downloaded binary
expected = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
if verify_file_integrity("download.tar.gz", expected):
    print("File integrity verified")
else:
    print("WARNING: File may be corrupted or tampered!")

Xác minh dòng lệnh

# Generate hash
sha256sum myfile.tar.gz > myfile.sha256

# Verify hash
sha256sum -c myfile.sha256
# myfile.tar.gz: OK

# Verify downloaded software
echo "expected_hash_here  terraform.zip" | sha256sum -c -

Trường hợp sử dụng 2: Lưu trữ mật khẩu

Không bao giờ lưu trữ mật khẩu ở dạng văn bản gốc. Nhưng việc băm thông thường cũng không đủ—bạn cần các chức năng băm mật khẩu **** được thiết kế đặc biệt để chạy chậm và ngốn bộ nhớ.

Tại sao việc băm thông thường lại có hại cho mật khẩu

# ❌ BAD: Fast hash allows rapid brute force
import hashlib
password_hash = hashlib.sha256(password.encode()).hexdigest()
# Attacker can try billions of guesses per second!

# ❌ ALSO BAD: Unsalted hash vulnerable to rainbow tables
# Same password = same hash for all users

Băm mật khẩu hàm

Thuật toán Trạng thái Ghi chú
Argon2id ✅ Tốt nhất Người chiến thắng PHC, khó nhớ, được đề xuất
bcrypt ✅ Tốt Đã được thử nghiệm trong trận chiến, giới hạn 72 byte
scrypt ✅ Tốt Bộ nhớ cứng, được sử dụng trong tiền điện tử
PBKDF2 ✅ Chấp nhận được Được hỗ trợ rộng rãi, cần độ lặp lại cao

Argon2id (Được khuyến nghị)

from argon2 import PasswordHasher
from argon2.exceptions import VerifyMismatchError

# Initialize with secure defaults
ph = PasswordHasher(
    time_cost=3,        # Number of iterations
    memory_cost=65536,  # 64 MB memory
    parallelism=4,      # 4 threads
)

def hash_password(password: str) -> str:
    """Hash a password with Argon2id."""
    return ph.hash(password)

def verify_password(password: str, hash: str) -> bool:
    """Verify a password against its hash."""
    try:
        ph.verify(hash, password)
        return True
    except VerifyMismatchError:
        return False

# Example usage
password = "user_secret_password"
hashed = hash_password(password)
print(f"Hash: {hashed}")
# $argon2id$v=19$m=65536,t=3,p=4$...(salt)...$...(hash)...

# Verification
assert verify_password("user_secret_password", hashed) == True
assert verify_password("wrong_password", hashed) == False

Bcrypt (Được hỗ trợ rộng rãi)

import bcrypt

def hash_password_bcrypt(password: str) -> bytes:
    """Hash a password with bcrypt."""
    salt = bcrypt.gensalt(rounds=12)  # 2^12 iterations
    return bcrypt.hashpw(password.encode(), salt)

def verify_password_bcrypt(password: str, hashed: bytes) -> bool:
    """Verify a bcrypt password hash."""
    return bcrypt.checkpw(password.encode(), hashed)

# Example
hashed = hash_password_bcrypt("my_password")
print(f"Hash: {hashed}")
# b'$2b$12$...(salt+hash)...'

assert verify_password_bcrypt("my_password", hashed) == True

Khuyến nghị lưu trữ mật khẩu OWASP

# OWASP 2024 recommendations
recommended_algorithms:
  first_choice: argon2id
  argon2id_config:
    memory: 19456  # 19 MiB minimum (46 MiB recommended)
    iterations: 2
    parallelism: 1
  
  second_choice: scrypt
  scrypt_config:
    n: 2^17  # CPU/memory cost
    r: 8     # Block size
    p: 1     # Parallelism
  
  third_choice: bcrypt
  bcrypt_config:
    cost: 10  # Minimum (12+ recommended)
  
  legacy_acceptable: pbkdf2
  pbkdf2_config:
    iterations: 600000  # SHA-256
    # or 210000 for SHA-512

Trường hợp sử dụng 3: Mã xác thực thư (HMAC)

HMAC kết hợp hàm băm với khóa bí mật để cung cấp tính xác thực và tính toàn vẹn:

import hmac
import hashlib

def create_hmac(message: bytes, key: bytes) -> str:
    """Create HMAC-SHA256 for a message."""
    return hmac.new(key, message, hashlib.sha256).hexdigest()

def verify_hmac(message: bytes, key: bytes, expected_mac: str) -> bool:
    """Verify HMAC using constant-time comparison."""
    actual_mac = create_hmac(message, key)
    return hmac.compare_digest(actual_mac, expected_mac)

# Example: Webhook signature verification
secret_key = b"webhook_secret_from_provider"
payload = b'{"event": "payment.completed", "amount": 100}'

# Provider sends this signature in header
signature = create_hmac(payload, secret_key)

# You verify it
if verify_hmac(payload, secret_key, signature):
    print("Webhook is authentic")
else:
    print("WARNING: Invalid webhook signature!")

Phòng chống tấn công theo thời gian

Luôn sử dụng so sánh thời gian không đổi để so sánh nhạy cảm về bảo mật:

# ❌ BAD: Vulnerable to timing attack
if actual_hash == expected_hash:  # String comparison leaks timing info
    return True

# ✅ GOOD: Constant-time comparison
import hmac
if hmac.compare_digest(actual_hash, expected_hash):
    return True

Trường hợp sử dụng 4: Bộ nhớ có thể định địa chỉ nội dung

Git, Docker và nhiều hệ thống phân tán sử dụng hàm băm làm mã định danh nội dung:

import hashlib
import json

def content_address(data: bytes) -> str:
    """Generate content-addressable identifier."""
    return f"sha256:{hashlib.sha256(data).hexdigest()}"

# Docker image digests
layer_data = b"... layer tarball ..."
digest = content_address(layer_data)
print(f"Layer digest: {digest}")
# sha256:abc123def456...

# If content changes, digest changes
# If digest is same, content is guaranteed identical

Băm đối tượng Git

# Git uses SHA-1 (transitioning to SHA-256)
echo "Hello" | git hash-object --stdin
# e965047ad7c57865823c7d992b1d046ea66edf78

# Verify a commit
git cat-file -p HEAD | sha1sum

Băm trong bối cảnh DevOps

Thông báo container image

# Dockerfile: Pin base image by digest (immutable)
FROM nginx@sha256:abc123def456...  # ✅ Reproducible
# FROM nginx:1.25                   # ❌ Tag can change

# Kubernetes: Reference by digest
spec:
  containers:
    - name: app
      image: myregistry/app@sha256:abc123def456...

Tổng kiểm tra artifact

# Download and verify Terraform
curl -O https://releases.hashicorp.com/terraform/1.6.0/terraform_1.6.0_linux_amd64.zip
curl -O https://releases.hashicorp.com/terraform/1.6.0/terraform_1.6.0_SHA256SUMS

# Verify checksum
sha256sum -c terraform_1.6.0_SHA256SUMS --ignore-missing
# terraform_1.6.0_linux_amd64.zip: OK

Vô hiệu hóa bộ đệm

import hashlib
import json

def cache_key(config: dict) -> str:
    """Generate cache key from configuration."""
    # Deterministic JSON serialization
    config_str = json.dumps(config, sort_keys=True)
    return hashlib.sha256(config_str.encode()).hexdigest()[:16]

# Same config = same cache key
config = {"version": "1.0", "features": ["a", "b"]}
key = cache_key(config)
print(f"Cache key: {key}")  # Consistent across runs

Các lỗi băm phổ biến

1. Sử dụng MD5/SHA-1 để bảo mật

# ❌ BAD: Broken algorithms
file_hash = hashlib.md5(data).hexdigest()
signature = hashlib.sha1(message).hexdigest()

# ✅ GOOD: SHA-256 or better
file_hash = hashlib.sha256(data).hexdigest()

2. Băm mật khẩu bằng thuật toán nhanh

# ❌ BAD: Fast hash for passwords
password_hash = hashlib.sha256(password.encode()).hexdigest()

# ✅ GOOD: Purpose-built password hasher
from argon2 import PasswordHasher
password_hash = PasswordHasher().hash(password)

3. Không sử dụng muối cho mật khẩu

# ❌ BAD: Unsalted (vulnerable to rainbow tables)
hash = sha256(password)

# ✅ GOOD: Salted (Argon2/bcrypt do this automatically)
# Each password gets unique salt, stored with hash

4. So sánh thời gian không cố định

# ❌ BAD: Timing attack vulnerability
if user_token == stored_token:
    authenticate()

# ✅ GOOD: Constant-time comparison
if hmac.compare_digest(user_token, stored_token):
    authenticate()

Tóm tắt

Những điểm chính của việc băm:

  • Hash hàm là một chiều—bạn không thể đảo ngược hàm băm để lấy đầu vào ban đầu
  • Sử dụng SHA-256 hoặc tốt hơn để xác minh tính toàn vẹn—tránh MD5 và SHA-1 để bảo mật
  • Sử dụng các chức năng dành riêng cho mật khẩu (Argon2id, bcrypt)—không bao giờ băm mật khẩu bằng SHA-256
  • HMAC để xác thực—kết hợp hàm băm với khóa bí mật
  • So sánh liên tục theo thời gian—ngăn chặn các cuộc tấn công theo thời gian với hmac.compare_digest()
  • Địa chỉ nội dung—sử dụng hàm băm làm mã định danh bất biến (Docker, Git)

Trong phần tiếp theo, chúng ta sẽ khám phá TLS/SSL—cách kết hợp mã hóa và băm để bảo mật dữ liệu khi truyền.