Mật mã học nghe hàn lâm, nhưng lập trình viên gặp nó mỗi ngày: lưu mật khẩu, ký token, kiểm tra file tải về, và — như sê-ri Git đã thấy — định danh nội dung. Sê-ri này tiếp cận mật mã theo cách thực dụng: mỗi khái niệm demo bằng openssl chạy được, không sa vào toán. Bắt đầu từ viên gạch nền tảng: hàm băm mật mã. Nó biến dữ liệu bất kỳ thành một chuỗi cố định như "dấu vân tay". Điểm cần khắc ngay: băm KHÔNG phải mã hóa — không có khóa, không giải ngược được. Đo thật bằng openssl 3.0.

Bốn tính chất của hàm băm mật mã

Một hàm băm mật mã tốt (như SHA-256) có bốn tính chất:

  • Xác định (deterministic): cùng một đầu vào luôn cho cùng một digest. Nhờ vậy digest dùng để định danh dữ liệu.
  • Kích thước cố định: đầu vào 1 byte hay 10 GB đều cho digest cùng độ dài (SHA-256 luôn 256 bit = 64 ký tự hex).
  • Một chiều (pre-image resistance): tính digest từ đầu vào thì dễ; tìm lại đầu vào từ digest thì bất khả thi trên thực tế.
  • Chống va chạm (collision resistance): khó tìm được hai đầu vào khác nhau cho cùng một digest.

Kèm theo là hiệu ứng avalanche: đổi một bit ở đầu vào làm khoảng một nửa số bit của digest đổi theo — digest mới trông hoàn toàn không liên quan digest cũ.

echo -n "xin chao" | openssl dgst -sha256     # băm một chuỗi
openssl dgst -sha256 tenfile                   # băm nội dung file
sha256sum tenfile                              # tương đương (coreutils)

Ảnh chụp đoạn mã nền tối giải thích hàm băm mật mã dấu vân tay một chiều của dữ liệu, bốn tính chất của một hàm băm mật mã tốt xác định cùng đầu vào luôn cùng digest định danh dữ liệu, kích thước cố định đầu vào 1 byte hay 10 GB digest cùng độ dài, một chiều dễ tính digest bằng f của x bất khả thi tìm x từ digest, chống va chạm khó tìm hai đầu vào khác nhau cùng một digest, avalanche đổi 1 bit đầu vào khoảng nửa số bit digest đổi theo, dùng để làm gì không phải mã hóa hash không giấu dữ liệu không có khóa không giải ngược được dùng kiểm toàn vẹn file định danh nội dung Git chống sửa đổi nền của HMAC chữ ký số lưu mật khẩu, openssl dgst echo -n xin chao openssl dgst -sha256 openssl dgst -sha256 tenfile sha256sum tenfile đừng dùng MD5 SHA-1 cho bảo mật đã có va chạm thực tế

Hình 1: Bốn tính chất của hàm băm mật mã và hiệu ứng avalanche. Hash không phải mã hóa — nó không giấu dữ liệu, dùng để kiểm toàn vẹn, định danh nội dung, và làm nền cho HMAC/chữ ký/lưu mật khẩu. Đo bằng openssl dgst.

Đo thật bốn tính chất

Ảnh chụp bảng kết quả đo thật nền tối chạy openssl 3.0, phần một xác định cùng xin chao luôn cùng digest sha256 xin chao bằng f9b197ffdaa45e4fb217f865fe3a2bd8 chấm chấm decd0b27 lặp lại hai lần giống hệt, phần hai avalanche đổi 1 ký tự o thành a khác hoàn toàn xin chao ra f9b197ff daa45e4fb217f865fe3a2bd8 xin chaa ra f1a981ca 6879417420d176dd6462273f chỉ 2 trên 64 ký tự hex trùng nhau gần như ngẫu nhiên, phần ba kích thước cố định bất kể đầu vào 1 ký tự ra 64 hex 256 bit 10 MB ra 64 hex 256 bit cùng độ dài, phần bốn SHA-256 vs MD5 MD5 đã vỡ MD5 128 bit 098f6bcd4621d373cade4e832627b4f6 SHA-256 256 bit 9f86d081884c7d659a2feaa0c55ad015 chấm chấm b0f00a08

Hình 2: Cùng "xin chao" luôn cho f9b197ff...decd0b27. Đổi một ký tự (o→a): f9b197ff... thành f1a981ca..., chỉ 2/64 ký tự hex trùng — avalanche. 1 ký tự và 10 MB đều ra 64 hex. MD5 128 bit vs SHA-256 256 bit.

Kết quả xác nhận từng tính chất:

  • Xác định: băm "xin chao" hai lần đều ra f9b197ffdaa45e4fb217f865fe3a2bd8...decd0b27. Đây là lý do digest định danh được dữ liệu — Git dùng chính điều này (bài git-01).
  • Avalanche: đổi một ký tự (xin chao → xin chaa), digest từ f9b197ff... thành f1a981ca... — chỉ 2/64 ký tự hex trùng nhau (kỳ vọng ngẫu nhiên là ~4). Không hề có "gần giống" — đổi tí xíu là khác hẳn. Nhờ vậy không suy được đầu vào bằng cách "dò dần".
  • Kích thước cố định: 1 ký tự và 10 MB đều cho 64 ký tự hex. Digest không tiết lộ độ dài đầu vào.
  • SHA-256 vs MD5: MD5 cho 128 bit, SHA-256 cho 256 bit. MD5 (và SHA-1) đã bị tìm ra va chạm thực tế — kẻ tấn công tạo được hai file khác nhau cùng digest MD5, nên tuyệt đối không dùng MD5/SHA-1 cho mục đích bảo mật. Dùng SHA-256 (hoặc SHA-3, BLAKE2/3).

Hash dùng để làm gì — và KHÔNG để làm gì

Đúng: kiểm toàn vẹn (tải file kèm digest, băm lại để so — khớp là chưa bị sửa/hỏng); định danh nội dung (Git, dedupe, cache key); làm nền cho HMAC (bài sau), chữ ký số, và lưu mật khẩu (nhưng không dùng SHA trần — bài sau).

Sai: đừng tưởng hash "giấu" dữ liệu. Hash một dữ liệu có không gian nhỏ (số điện thoại, số CMND, "yes/no") thì kẻ tấn công băm hết mọi khả năng rồi so — lộ ngay. Hash không có khóa nên không bí mật; muốn giấu dữ liệu phải mã hóa (bài AES). Và đừng dùng hash trần để lưu mật khẩu (bài kế giải thích vì sao).

Đánh đổi và lưu ý

Chống va chạm phụ thuộc độ dài digest và thuật toán. SHA-256 hiện an toàn; MD5/SHA-1 thì không. Nhưng "an toàn hôm nay" không vĩnh viễn — thiết kế hệ thống nên cho phép đổi thuật toán băm (agility), đừng hardcode MD5 khắp nơi.

Hash nhanh là con dao hai lưỡi. SHA-256 được tối ưu để băm nhanh — tốt cho kiểm toàn vẹn (băm 10 MB tức thì), nhưng chính vì nhanh mà nó tệ cho lưu mật khẩu: kẻ tấn công cũng băm nhanh, thử hàng tỷ mật khẩu/giây. Lưu mật khẩu cần hàm cố ý chậm (bcrypt/argon2 — bài sau).

Encode ≠ hash ≠ mã hóa. Base64 là mã hóa biểu diễn (giải ngược được, không bí mật). Hash là một chiều. Mã hóa cần khóa và giải ngược được với khóa. Ba thứ khác nhau hoàn toàn — nhầm lẫn là nguồn của vô số lỗi bảo mật.

Ba ý mang về

  1. Hàm băm mật mã là dấu vân tay một chiều, kích thước cố định: đo thật, cùng đầu vào luôn cùng digest (định danh), đổi một ký tự cho digest khác hoàn toàn (avalanche, chỉ 2/64 hex trùng), 1 byte hay 10 MB đều ra 64 hex.
  2. Hash KHÔNG phải mã hóa: không khóa, không giải ngược, không giấu được dữ liệu không gian nhỏ — dùng cho toàn vẹn, định danh, nền của HMAC/chữ ký; muốn giấu dữ liệu phải mã hóa.
  3. Chọn đúng thuật toán và ngữ cảnh: MD5/SHA-1 đã vỡ va chạm — dùng SHA-256/SHA-3; và hash nhanh tốt cho toàn vẹn nhưng tệ cho mật khẩu (cần hàm cố ý chậm — bài sau).

Nguồn

Phần sau ta trả lời câu hỏi vừa gợi: vì sao KHÔNG được lưu mật khẩu bằng SHA trần — salt, hàm cố ý chậm (bcrypt/argon2), và tấn công từ điển, đo thật chi phí băm.