Bảo mật Python thực chiến: Đừng để lộ mật khẩu vì dùng hashlib sai cách

Python tutorial - IT technology blog
Python tutorial - IT technology blog

Tại sao băm (Hash) và tạo chuỗi ngẫu nhiên lại quan trọng?

Hồi mới làm Web, mình từng mắc một sai lầm ngớ ngẩn: lưu mật khẩu người dùng dưới dạng văn bản thuần (plain text) vào database. Lúc đó mình tự trấn an “chắc không ai hack được đâu”. Kết quả là chỉ sau một lỗ hổng SQL Injection nhỏ, toàn bộ dữ liệu khách hàng bị phơi bày. Đó là bài học đắt giá khiến mình phải nghiêm túc học cách dùng hashlibsecrets.

Nhiều bạn vẫn hay nhầm giữa Hashing (Băm)Encryption (Mã hóa). Mã hóa là đường hai chiều, bạn có thể giải mã nếu có khóa. Ngược lại, Hashing là đường một chiều. Một khi đã băm, bạn không thể dịch ngược nó về giá trị cũ. Đây là lớp phòng thủ cuối cùng. Dù hacker lấy được database, thứ họ thấy chỉ là những chuỗi ký tự vô nghĩa.

Ngoài ra, việc tạo token reset mật khẩu cũng cần sự cẩn trọng. Nếu dùng module random thông thường, hacker có thể dự đoán giá trị tiếp theo dựa trên thời gian hệ thống. Module secrets sinh ra để giải quyết việc này. Nó đảm bảo tính ngẫu nhiên đạt chuẩn bảo mật (cryptographically secure).

Cài đặt môi trường

Cả hashlibsecrets đều là thư viện chuẩn của Python. Bạn không cần pip install thêm gì cả. Chỉ cần cài sẵn Python 3.6 trở lên là dùng được ngay.

import hashlib
import secrets
import hmac

print("Môi trường đã sẵn sàng!")

Với các dự án chạy production năm 2024, mình khuyến khích dùng Python 3.9+. Phiên bản này tối ưu hiệu năng băm tốt hơn cho các hệ thống tải cao.

Triển khai thực tế và cấu hình chi tiết

1. Băm mật khẩu với hashlib (Tuyệt đối tránh MD5)

Nhiều tutorial cũ vẫn dạy dùng MD5 hoặc SHA-1. Lời khuyên của mình là: Bỏ ngay! Một GPU hiện đại có thể thử hàng tỷ hash MD5 mỗi giây. Hacker chỉ mất vài phút để dò ra mật khẩu của bạn bằng Rainbow Tables.

Lựa chọn an toàn hiện nay là SHA-256 kết hợp với Salt (muối) và cơ chế lặp (iterations). Mình thường dùng pbkdf2_hmac để làm chậm quá trình băm. Việc này khiến các cuộc tấn công brute-force trở nên cực kỳ tốn kém và bất khả thi.

def hash_password(password: str):
    # Tạo muối ngẫu nhiên 16 bytes
    salt = secrets.token_bytes(16)
    # Dùng PBKDF2 với 100.000 vòng lặp
    pw_hash = hashlib.pbkdf2_hmac(
        'sha256', 
        password.encode('utf-8'), 
        salt, 
        100000
    )
    return salt + pw_hash

# Test thử
raw_pw = "PasswordManh2024!"
stored_value = hash_password(raw_pw)
print(f"Lưu vào DB: {stored_value.hex()}")

Khi cần đối chiếu nhanh kết quả băm mà không muốn viết code, mình hay dùng Hash Generator. Công cụ này chạy hoàn toàn ở trình duyệt (client-side). Dữ liệu không bao giờ gửi về server nên rất an toàn để test các chuỗi nhạy cảm.

2. Tạo Token và mật khẩu tạm bằng secrets

Khi làm tính năng “Quên mật khẩu”, bạn cần một token gửi qua email. Đừng dùng random.randint() vì nó rất dễ bị đoán định. Hãy dùng secrets để tạo ra các chuỗi không thể dự đoán.

# Tạo token an toàn cho URL
reset_token = secrets.token_urlsafe(32)
print(f"Token email: {reset_token}")

# Tạo mật khẩu tạm 16 ký tự
temp_pass = secrets.token_hex(8)
print(f"Mật khẩu tạm: {temp_pass}")

Nếu bạn là admin và cần tạo nhanh danh sách mật khẩu mạnh cho nhân viên, hãy thử Password Generator. Nó cho phép tùy chỉnh độ dài, ký tự đặc biệt và đo luôn độ mạnh của mật khẩu rất trực quan.

3. Xác thực dữ liệu với HMAC

HMAC giúp bạn đảm bảo dữ liệu không bị chỉnh sửa khi truyền giữa các hệ thống (như Webhook). Nó giống như việc dán một cái tem niêm phong lên gói hàng của bạn.

KEY = b'bi-mat-quan-trong'
msg = b"Action: Transfer; Amount: 1000"

# Tạo chữ ký
sig = hmac.new(KEY, msg, hashlib.sha256).hexdigest()

# Kiểm tra tính toàn vẹn
def verify(message, received_sig):
    expected = hmac.new(KEY, message, hashlib.sha256).hexdigest()
    # Chống Timing Attack bằng compare_digest
    return hmac.compare_digest(expected, received_sig)

Kinh nghiệm kiểm tra và giám sát

Một lỗi sơ đẳng nhưng nguy hiểm là dùng toán tử == để so sánh hash. Hacker có thể đo thời gian phản hồi của server để đoán từng ký tự (Timing Attack). Luôn dùng hmac.compare_digest() để đảm bảo thời gian xử lý luôn bằng nhau.

Lưu ý quan trọng:

  • Tuyệt đối không log mật khẩu chưa băm hoặc chuỗi Salt vào hệ thống.
  • Nếu thấy yêu cầu reset mật khẩu tăng đột biến (ví dụ >100 lần/phút), hãy kích hoạt cảnh báo tấn công.
  • Tăng số vòng lặp PBKDF2 sau mỗi 2 năm để bắt kịp tốc độ phần cứng.

Trong lúc debug API, nếu gặp các chuỗi Base64 lạ, mình thường dùng Base64 Decoder để kiểm tra nội dung nhanh. Công cụ này xử lý tại máy khách nên mình yên tâm không bị lộ API Key hay Token.

Bảo mật không phải là cài đặt một lần là xong. Đó là thói quen viết code cẩn thận mỗi ngày. Chúc các bạn xây dựng được những ứng dụng Python an toàn!

Share: