Thay signing secret của webhook UnifyPort một cách an toàn
Để thay signing_secret của webhook UnifyPort, trước tiên hãy chuẩn bị mọi instance nhận webhook để xác minh được cả khóa hiện tại lẫn khóa mới. Sau đó cập nhật endpoint, xác nhận một lần gửi mới được xác minh bằng khóa mới, rồi loại bỏ khóa cũ khi đạt các tiêu chí chuyển đổi. Đây là quy trình do ứng dụng quản lý: API công khai quy định một signing secret cho mỗi endpoint, không cam kết giai đoạn dùng song song hai khóa ở máy chủ hay bảo đảm chuyển đổi không mất dữ liệu.
Điểm chính
- Thay khóa ký webhook riêng với khóa REST API.
- Không gửi
signing_secretrỗng làm bước trung gian: thao tác này tắt chữ ký. - Giới hạn bộ khóa xác minh tạm thời theo đúng endpoint và môi trường.
- Không coi các lần gửi lại là thời gian gia hạn cho việc triển khai.
Phân biệt khóa và ranh giới lỗi
X-Api-Key xác thực các yêu cầu bạn gửi tới UnifyPort. signing_secret của endpoint dùng để kiểm tra các lần gửi đến ứng dụng. Thay một giá trị không thay giá trị còn lại. Với phía gọi REST, hãy dùng quy trình thay API key riêng.
Tài liệu gửi webhook định nghĩa X-Device-Signature là HMAC-SHA256 mã hóa hệ thập lục phân, tính trên X-Device-Timestamp theo RFC 3339, một dấu chấm và phần thân yêu cầu nguyên gốc. Việc thay khóa không làm đổi đầu vào này hoặc cấu trúc sự kiện.
Khóa không khớp ở phía nhận có thể khiến sự kiện hợp lệ bị từ chối. Theo hợp đồng hiện tại, lỗi kết nối và HTTP 408, 429, 5xx được thử lại ngay, không có backoff; các mã 4xx khác không được thử lại. Vì vậy, không nên kỳ vọng một đợt triển khai sau sẽ tự khắc phục 401 do đổi khóa quá sớm. Trả về 503 cũng không tạo ra hàng đợi chờ bảo trì đáng tin cậy.
Lập kế hoạch với ba cấu hình
Đây là trình tự triển khai được đề xuất, không phải tính năng thay khóa tích hợp sẵn của UnifyPort.
| Giai đoạn | Cấu hình endpoint | Khóa được phía nhận xác minh |
|---|---|---|
| Chuẩn bị | Khóa hiện tại | Khóa hiện tại và khóa mới |
| Chuyển đổi | Khóa mới | Khóa hiện tại và khóa mới |
| Loại bỏ khóa cũ | Khóa mới | Chỉ khóa mới |
Trước khi bắt đầu, ghi lại endpoint ID, URL, trạng thái, sự kiện đăng ký, chính sách thử lại, các instance nhận và người phụ trách. Lưu giá trị khóa trong hệ thống quản lý bí mật, không đưa vào biên bản thay đổi. Tạo khóa mới độc lập và phân phối qua cơ chế triển khai được bảo vệ mà đội ngũ đang sử dụng.
Giữ nguyên URL, danh sách sự kiện và cấu hình thử lại trong thao tác này. Di chuyển điểm nhận cùng lúc với thay đổi xác thực khiến việc xác định lỗi khó hơn. Nếu nghi ngờ khóa cũ đã bị lộ, không áp dụng giai đoạn song song thông thường: tiếp tục chấp nhận khóa đó đồng nghĩa với tiếp tục rủi ro. Hãy chuyển đổi theo kế hoạch ứng phó sự cố, với quyết định rõ ràng về tính sẵn sàng và đối soát dữ liệu.
Chuẩn bị toàn bộ phía nhận trước khi đổi phía gửi
Đặt một bộ khóa nhỏ, tạm thời trong cấu hình tuyến đáng tin cậy. Không chọn khóa từ provider, account_id chưa được xác minh hoặc một header phiên bản khóa do bạn tự giả định. Các header gửi được công bố không có mã định danh khóa ký.
Hàm minh họa dưới đây kiểm tra mọi khóa ứng viên, không trả về ngay khi tìm thấy khóa đầu tiên khớp. Đây không phải HTTP receiver hoàn chỉnh hay kết quả kiểm thử đã thực hiện. keys phải là mảng không rỗng gồm các chuỗi khóa không rỗng dành riêng cho endpoint; maxAgeMs là dung sai thời gian dương, hữu hạn do ứng dụng chọn.
import { createHmac, timingSafeEqual } from 'node:crypto';
export function verifyDuringRotation({
rawBody, timestamp, signature, keys, maxAgeMs,
}) {
if (!Array.isArray(keys) || keys.length === 0 ||
keys.some(key => typeof key !== 'string' || key.length === 0) ||
!Number.isFinite(maxAgeMs) || maxAgeMs <= 0) {
throw new Error('Invalid webhook verification configuration');
}
if (!Buffer.isBuffer(rawBody) || typeof timestamp !== 'string' ||
typeof signature !== 'string' || !/^[0-9a-f]{64}$/i.test(signature)) {
return false;
}
const signedAt = Date.parse(timestamp);
if (!Number.isFinite(signedAt) ||
Math.abs(Date.now() - signedAt) > maxAgeMs) return false;
const supplied = Buffer.from(signature, 'hex');
let matches = 0;
for (const key of keys) {
const expected = createHmac('sha256', key)
.update(timestamp + '.').update(rawBody).digest();
matches |= Number(timingSafeEqual(supplied, expected));
}
return matches !== 0;
}
Tài liệu Node.js Crypto mô tả các hàm HMAC và so sánh này. Giữ nguyên byte gốc, từ chối yêu cầu thiếu chữ ký và không thêm nhánh dự phòng chấp nhận yêu cầu không ký. Sau khi xác minh, hãy kiểm tra payload và lưu bền vững. Hướng dẫn chống phát lại với HMAC giải thích vì sao vẫn cần kiểm tra độ mới và xử lý trùng lặp dù chữ ký đã khớp.
Trên mỗi nhóm triển khai phía nhận, thử khóa hiện tại, khóa mới, khóa sai, phần thân bị sửa, chữ ký bị thiếu và timestamp quá cũ. Đây là các ca nghiệm thu được đề xuất, không phải tuyên bố mã đã chạy trong môi trường của bạn.
Cập nhật endpoint mà không tắt chữ ký
Đọc cấu hình hiện tại bằng Get webhook endpoint. Sau đó dùng Update webhook endpoint, gọi PATCH /v1/webhook-endpoints/{endpoint_id} và xác thực bằng REST API key.
Tạo yêu cầu cập nhật từ URL, trạng thái active, danh sách sự kiện và chính sách thử lại hiện tại đã được kiểm tra; đặt khóa mới vào signing_secret. Không sao chép secret rỗng hoặc trạng thái inactive trong ví dụ minh họa của tài liệu vào lần chuyển đổi production. Secret rỗng tắt chữ ký, không yêu cầu hệ thống tự thay khóa.
Đọc lại cấu hình và kiểm tra signing_enabled: true cùng các giá trị cần giữ nguyên. Cờ này chỉ chứng minh chữ ký đang bật, không cho biết khóa nào đang được dùng. Gửi tin nhắn kiểm thử có kiểm soát đến tài khoản nhắn tin đã kết nối, rồi theo dõi một lần gửi message.received mới qua bước xác minh bằng khóa mới, lưu bền vững và xử lý nội bộ mong đợi. Không ghi khóa vào log và không dùng nội dung khách hàng làm dữ liệu thử.
Nếu phản hồi cập nhật không rõ ràng, giữ bộ xác minh hai khóa trong lúc kiểm tra cấu hình và thử lần gửi mới. Không loại bỏ khóa cũ chỉ vì đã gửi yêu cầu PATCH.
Đặt tiêu chí loại bỏ khóa, không tự đoán thời gian chờ
Tài liệu công khai không nêu rõ các lần gửi đã nằm trong hàng đợi có giữ cấu hình ký cũ hay không, cũng không quy định thời gian dùng song song tối đa. retry_policy.max_attempts đếm số lần thử lại, không phải số giây gia hạn. Trước khi hứa chuyển đổi liên tục, hãy xác nhận với UnifyPort những hành vi chưa rõ của công việc đang được gửi.
Dùng việc hoàn tất triển khai trên mọi instance, các lần gửi mới được xác minh bằng khóa mới, quan sát lỗi phía nhận và phương án xử lý công việc đang gửi đã thống nhất làm điều kiện kết thúc. Trong chỉ số vận hành, chỉ lưu nhãn phiên bản xác minh không chứa bí mật. Receiver yên lặng không chứng minh các lần gửi dùng khóa cũ đã hết.
Khi đạt điều kiện, xóa khóa cũ khỏi mọi instance nhận và nguồn cấu hình triển khai. Trong môi trường kiểm thử tách biệt, xác nhận khóa cũ nay bị từ chối. Giai đoạn song song phải có điểm kết thúc do vận hành quyết định, không chấp nhận khóa lịch sử vô thời hạn.
Nếu cần rollback và khóa cũ vẫn đáng tin cậy, phối hợp cả cấu hình endpoint lẫn bộ khóa phía nhận. Không khôi phục receiver chỉ chấp nhận khóa cũ khi endpoint vẫn ký bằng khóa mới. Nếu nghi ngờ lộ khóa, không đưa khóa đã lộ trở lại.
Phạm vi và giới hạn
Giao diện không chính thức của UnifyPort chuẩn hóa sự kiện từ các tài khoản nhắn tin được hỗ trợ, bao gồm Zalo và WhatsApp. Quy trình này bảo vệ chặng từ UnifyPort tới receiver của bạn. Nó không thay thông tin xác thực gốc của Telegram hoặc LINE, phiên đăng nhập nền tảng hay API key.
Lưu bền vững sự kiện đã xác thực trước khi trả 2xx. Có thể loại trùng lần gửi lại của sự kiện thông thường bằng event ID; WhatsApp conversation.history cần hợp nhất ở cấp tin nhắn theo hợp đồng, không loại bỏ toàn bộ dựa riêng vào ID cấp cao nhất. UnifyPort không cung cấp REST API đọc lịch sử tin nhắn hoặc bảo đảm phát lại payload đã bỏ lỡ, nên không được mô tả lỗi chuyển khóa là tự động khôi phục được.
Câu hỏi thường gặp
Một endpoint có hỗ trợ hai signing_secret không?
Hợp đồng công khai chỉ có một signing_secret. Bộ xác minh hai khóa tạm thời trong bài thuộc logic ứng dụng, không phải cấu hình hai secret của API.
Có thể tắt chữ ký một lúc để đổi cho đơn giản không?
Không nên. Giữ chữ ký bật và từ chối khi thiếu xác thực. Secret rỗng loại bỏ header chữ ký, không cung cấp cơ chế chuyển tiếp.
Nên chấp nhận khóa cũ trong bao lâu?
Không có khoảng thời gian chung được công bố. Dựa vào kiểm tra triển khai và lần gửi, xác nhận hành vi đang xử lý, rồi đặt tiêu chí kết thúc có giới hạn phù hợp với rủi ro.
Bước tiếp theo và nguồn tham khảo
Đọc Update webhook endpoint và diễn tập ba cấu hình trong môi trường tách biệt trước khi thay đổi production.
Tài liệu được kiểm tra ngày 2026-09-27:
Biến tích hợp nhắn tin thành một pipeline sản phẩm ổn định.
Bắt đầu bằng cách gửi qua một API, rồi đưa mọi tin nhắn inbound trở lại hệ thống kinh doanh bằng sự kiện chuẩn.