Tạo HMAC

Tính HMAC của văn bản, byte hex hoặc tệp bằng một khóa bí mật với SHA-256, SHA-384, SHA-512, SHA-1 hoặc MD5, rồi đối chiếu với chữ ký của webhook hoặc API. Không có gì rời khỏi trình duyệt của bạn.

Chạy hoàn toàn trong trình duyệt của bạn. Không có gì được tải lên.

Cách sử dụng

  1. Chọn Thuật toán. HMAC-SHA256 được chọn sẵn và là thuật toán mà hầu hết webhook và API sử dụng.
  2. Nhập Khóa bí mật và đặt Định dạng khóa là Văn bản (đọc theo UTF-8), Hex hoặc Base64. Hãy dùng Hex hoặc Base64 khi khóa là byte thô chứ không phải một chuỗi giống mật khẩu.
  3. Nhập hoặc dán Thông điệp, chuyển Đầu vào dạng sang Hex để dùng byte thô, hoặc nhấp Mở tệp. HMAC được cập nhật ngay khi bạn gõ.
  4. Chọn Đầu ra dạng hex (chữ thường), HEX (chữ hoa) hoặc Base64 rồi sao chép kết quả, hoặc dán chữ ký nhận được vào ô So sánh với để xem Khớp hay Không khớp.

HMAC hoạt động như thế nào

HMAC (mã xác thực thông điệp dựa trên hàm băm, RFC 2104) kết hợp một hàm băm mật mã với một khóa bí mật. Chỉ người biết khóa mới tạo được giá trị đúng cho một thông điệp, nên một HMAC khớp chứng minh cùng lúc hai điều: thông điệp không bị thay đổi, và nó đến từ người nắm giữ khóa.

Cấu trúc của HMAC là H((K ⊕ opad) ‖ H((K ⊕ ipad) ‖ message)). Khóa được băm trước nếu dài hơn kích thước khối của hàm băm (64 byte với MD5, SHA-1 và SHA-256, 128 byte với SHA-384 và SHA-512), được đệm thêm các số 0 cho đủ kích thước đó, rồi XOR với ipad (các byte 0x36) để tính hàm băm bên trong trên thông điệp, và với opad (các byte 0x5c) để tính hàm băm bên ngoài trên kết quả bên trong. Cách ghép đơn giản SHA-256(key ‖ message) không có hàm băm bên ngoài, nên bất kỳ ai thấy một mã xác thực (tag) đều có thể nối thêm dữ liệu vào thông điệp và tính ra một mã xác thực hợp lệ mới mà không cần khóa (tấn công mở rộng độ dài).

Đầu ra dài bằng đầu ra của hàm băm bên dưới: 32 byte, tức 64 ký tự hex, với HMAC-SHA256. RFC 2104 khuyến nghị khóa dài ít nhất bằng chừng đó, và công cụ tạo Base64 ngẫu nhiên có thể tạo những khóa phù hợp.

Chữ ký HMAC được dùng ở đâu

  • Webhook của GitHub. Header X-Hub-Signature-256 gồm sha256= theo sau là HMAC-SHA256 dạng hex của phần thân yêu cầu thô, tính bằng khóa bí mật của webhook. Bạn có thể dán toàn bộ giá trị header vào ô So sánh với; tiền tố sha256= sẽ được bỏ qua.
  • Webhook của Stripe. Header Stripe-Signature chứa dấu thời gian t và chữ ký v1: HMAC-SHA256 dạng hex của dấu thời gian, một dấu chấm và phần thân thô, tính bằng khóa bí mật của endpoint (whsec_...) dùng ở dạng văn bản.
  • Webhook của Shopify. X-Shopify-Hmac-Sha256 chứa HMAC-SHA256 của phần thân thô ở dạng Base64, tính bằng client secret của ứng dụng, vì vậy hãy chọn đầu ra Base64 hoặc dán giá trị header vào ô So sánh với.
  • AWS Signature Version 4. Khóa ký được suy ra qua một chuỗi phép tính HMAC-SHA256: trước tiên là HMAC của ngày với khóa là AWS4 ghép với secret access key, rồi lần lượt đến vùng (region), dịch vụ và aws4_request, mỗi bước lấy kết quả của bước trước làm khóa. Hãy đặt Định dạng khóa là Hex để đưa đầu ra của bước này vào bước tiếp theo.
  • JSON Web Token. HS256, HS384 và HS512 là HMAC-SHA256, -384 và -512 tính trên header đã mã hóa, một dấu chấm và payload đã mã hóa. Phần thứ ba của token chính là HMAC đó ở dạng Base64 an toàn cho URL, và ô So sánh với chấp nhận nguyên dạng này. Công cụ giải mã JWT xác minh được toàn bộ token.

Luôn tính HMAC trên phần thân thô đúng như khi nhận được. Phân tích JSON rồi tuần tự hóa lại sẽ làm thay đổi khoảng trắng hoặc thứ tự các khóa, và chỉ cần thừa một ký tự xuống dòng cũng cho ra HMAC hoàn toàn khác.

HMAC trong mã nguồn

# Python
import hmac, hashlib
sig = hmac.new(b'key', b'message', hashlib.sha256).hexdigest()
hmac.compare_digest(sig, received)        # so sánh trong thời gian không đổi

// Node.js
const crypto = require('crypto');
const sig = crypto.createHmac('sha256', 'key').update('message').digest('hex');
// timingSafeEqual báo lỗi nếu độ dài khác nhau, nên hãy kiểm tra độ dài trước
crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(received));

// PHP: thông điệp đứng trước khóa
$sig = hash_hmac('sha256', 'message', 'key');
hash_equals($sig, $received);

# OpenSSL: khóa dạng văn bản, rồi cùng khóa đó ở dạng hex
printf '%s' 'message' | openssl dgst -sha256 -hmac 'key'
printf '%s' 'message' | openssl dgst -sha256 -mac HMAC -macopt hexkey:6b6579

# Vector kiểm thử: khóa "key", thông điệp "The quick brown fox jumps over the lazy dog"
# HMAC-SHA256 = f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8

Khi xác minh chữ ký trong mã của bạn, hãy dùng phép so sánh trong thời gian không đổi như các ví dụ trên. Phép == thông thường dừng lại ở ký tự khác nhau đầu tiên, và chênh lệch thời gian đó có thể giúp kẻ tấn công dò ra chữ ký hợp lệ từng byte một. Với các cơ chế có dấu thời gian như của Stripe, hãy từ chối cả những yêu cầu có dấu thời gian cũ hơn vài phút để chặn tấn công phát lại.

Câu hỏi thường gặp

HMAC-MD5 hay HMAC-SHA1 có còn an toàn không?

Tấn công xung đột đã phá vỡ MD5 và SHA-1 khi dùng làm hàm băm thông thường, nhưng chúng không mang lại cách thực tế nào để giả mạo HMAC, vốn dựa vào những tính chất khác của hàm băm và vào một khóa bí mật (RFC 6151 phân tích riêng về HMAC-MD5). Những tích hợp bắt buộc dùng chúng, như mã TOTP dựa trên HMAC-SHA1, vẫn có thể giữ nguyên. Với thiết kế mới, hãy dùng HMAC-SHA256.

Vì sao HMAC của tôi không khớp với chữ ký webhook?

Nguyên nhân thường gặp là băm JSON đã được phân tích rồi tuần tự hóa lại thay vì phần thân thô, ký tự xuống dòng ở cuối thông điệp, sai khóa bí mật hoặc sai định dạng khóa (khóa Base64 hoặc hex lại được nhập dưới dạng Văn bản, hoặc ngược lại), và sai thuật toán. Hex hay Base64 thì không thành vấn đề ở đây: ô So sánh với chấp nhận hex chữ hoa lẫn chữ thường, Base64 tiêu chuẩn và Base64 an toàn cho URL.

HMAC khác gì so với mã băm thông thường?
Ai cũng tính được mã băm thông thường như SHA-256, nên nó chỉ cho thấy dữ liệu không bị thay đổi do vô tình. HMAC chỉ tính được khi có khóa bí mật, nên một HMAC hợp lệ còn cho thấy nó được tạo bởi người nắm giữ khóa đó.
HMAC có giống chữ ký số không?

Không. Hai bên dùng HMAC chia sẻ cùng một khóa bí mật, nên ai xác minh được mã xác thực thì cũng tạo được nó. Chữ ký như RSA hay ECDSA (RS256 hoặc ES256 trong JWT) được tạo bằng khóa riêng tư và kiểm tra bằng khóa công khai, nên người xác minh không thể giả mạo chúng.

Khóa bí mật của tôi có bị gửi đến máy chủ không?

Không. HMAC-SHA1 và các biến thể SHA-2 được tính bằng Web Crypto API của trình duyệt, còn HMAC-MD5 được tính bằng JavaScript trên trang. Khóa và thông điệp không bao giờ rời khỏi thiết bị của bạn. Dù vậy, hãy cẩn trọng với khóa bí mật đang dùng thực tế (production) và thay khóa mới nếu khóa có thể đã bị lộ.