Câu 1Khi bạn gửi request có gắn API key, dịch vụ bên ngoài xác định được điều gì?
API key và tích hợp dịch vụ bên ngoài — Key bảo vệ điều gì, và Webhook
Bài viết này nằm trong khóa Kiến thức nền tảng về CNTT, xây dựng từ đầu những kiến thức CNTT thực tế tối thiểu mà bạn cần để lập trình và vibe coding.
API key là chuỗi ký tự cho biết lệnh gọi đó là của ai. Các sơ đồ sẽ cho bạn thấy nơi được phép đặt key và chiều của Webhook, tức request mà dịch vụ bên ngoài gửi tới server của bạn.
Bài viết này bàn về hai thứ: API key bạn dùng khi gọi dịch vụ bên ngoài, và Webhook mà dịch vụ bên ngoài gọi ngược lại từ phía họ.
Bạn sẽ thấy key được phát hành ở đâu, chuỗi đó cho biết điều gì, và nên đặt nó ở đâu để không ai khác đọc được.
- Bạn phát hành API key ở đâu, và gắn nó vào chỗ nào trong lệnh gọi
- Key là chuỗi cho biết ai là chủ của lệnh gọi, và quan hệ của nó với việc tính phí theo mức sử dụng
- Nơi được phép đặt key, và điều gì xảy ra nếu bạn viết nó vào chỗ đến được trình duyệt
- Webhook được gọi từ phía dịch vụ bên ngoài, và việc đối chiếu chữ ký
Bạn phát hành API key trong trang quản trị của dịch vụ bên ngoài mà bạn dùng
Để gọi một dịch vụ bên ngoài, trước hết bạn nhận từ nhà cung cấp một API key (API key: chuỗi ký tự cho nhà cung cấp biết lệnh gọi thuộc về người dùng nào).
Đây không phải giá trị do bạn tự đặt, mà là giá trị do chính dịch vụ bạn dùng phát hành và giao cho bạn.
Dù là thanh toán, bản đồ hay sinh văn bản, trình tự đăng ký, phát hành key, đặt lên server của mình vẫn không đổi.
Hãy sao chép chuỗi vừa phát hành ngay tại chỗ và đặt vào file .env trên server của bạn.
Phần lớn dịch vụ bên ngoài dùng cách tính phí theo mức sử dụng (dùng bao nhiêu trả bấy nhiêu), tính tiền theo số lần gọi và lượng dữ liệu gửi nhận.
Nếu key lọt ra ngoài, phần người khác gọi cũng được tính vào hóa đơn của chủ key.
API key là chuỗi ký tự cho biết lệnh gọi thuộc về người dùng nào
API của dịch vụ thanh toán là một URL công khai, nên nó cần một cách để biết lệnh gọi vừa đến là của ai.
Chỗ để gắn key không phải URL, mà là header của request (request header: phần tách riêng khỏi phần thân, liệt kê từng cặp tên và giá trị như người gửi hay định dạng).
- POST https://api.pay.example.com/v1/charges
- Chỉ ghi đi tới đâu và làm gì ở đó
- API key được ghi ở đây
- Authorization: Bearer sk_live_a1b2
- Mỗi dòng một cặp tên và giá trị
- Dữ liệu bạn gửi, ở dạng JSON
- Thường để trống với lệnh gọi chỉ để lấy dữ liệu
Dòng thứ hai trong khung giữa là dòng ghi key.
Authorization là tên header, Bearer là từ chỉ cách truyền giá trị, và phần phía sau là giá trị của key.
Cả tên này lẫn cách viết này đều do nhà cung cấp quy định.
Từ key nhận được, dịch vụ thanh toán xác định ba điều: chủ sở hữu, thao tác được phép, và số lần gọi.
| my-app đã làm gì | Header Authorization | Dịch vụ thanh toán làm gì | Thứ trả về |
|---|---|---|---|
| Lấy danh sách thanh toán | Bearer sk_live_a1b2 | Xác định chủ sở hữu và kiểm tra phạm vi | 200 và JSON |
| Lấy danh sách thanh toán | Quên gắn key | Không xác định được chủ sở hữu | Trả về 401 |
| Tạo một khoản thanh toán | Bearer sk_read_9f3c | Chỉ cho phép đọc | Trả về 403 |
| Gọi nhiều lần trong thời gian ngắn | Bearer sk_live_a1b2 | Đếm số lần gọi của từng chủ sở hữu | Trả về 429 |
Gặp 401 thì kiểm tra giá trị key và tên header; gặp 403 thì kiểm tra xem thao tác nào được phép.
Giới hạn ở dòng thứ tư là giới hạn tần suất (rate limit: số lần gọi tối đa được tiếp nhận trong một khoảng thời gian nhất định), vượt qua nó thì nhận về 429.
Key là chuỗi cho biết ai là chủ của lệnh gọi
API key là chuỗi ký tự chỉ để nói lên lệnh gọi này là của ai.
Dịch vụ thanh toán nhìn key để xác định chủ sở hữu và thao tác được phép, rồi đếm số lần gọi của người đó, nên không có key sẽ nhận 401, thao tác không được phép sẽ nhận 403, và gọi quá nhiều lần sẽ nhận 429.
Đừng đặt key vào mã đến được trình duyệt, chỉ giữ nó bên trong server của bạn
Bất kỳ ai giữ key đều gọi được với tư cách chủ của key đó.
Không có màn hình nào kiểm tra xem người đó có đúng là chủ hay không.
- PAY_API_KEY=sk_live_a1b2
- Đặt trong file riêng, tách khỏi mã nguồn
- Gắn key đọc từ .env vào lệnh gọi
- Bản thân file này không được gửi tới thiết bị
- index.html và script.js
- Nhờ server của bạn làm, thay vì gọi thẳng dịch vụ thanh toán
- Chỉ đặt những key được phát hành để dùng từ trình duyệt
Cùng một key, nhưng viết ở đâu sẽ quyết định ai đọc được nó.
| Nơi key được viết | Có đến thiết bị không | Ai đọc được | Điều xảy ra |
|---|---|---|---|
| .env (bên trong server) | Không đến | Chỉ người có quyền vào server | Dùng được đúng như hợp đồng |
| script.js | Có đến | Mọi người mở trang | Người khác gọi được với tư cách của bạn |
| server.js đã công khai | Không đến, nhưng bị công khai | Mọi người xem mã nguồn | Không xóa được khỏi lịch sử Git |
Dòng nào càng nhiều người đọc được key thì càng gần với tình trạng người khác gọi được với tư cách của bạn.
Dòng thứ ba không đến được trình duyệt, nhưng một khi đã ghi vào Git thì vẫn đọc được trong lịch sử thay đổi dù sau đó bạn xóa đi.
Khi hướng dẫn nói "hãy đặt vào .env", đó là bảo bạn dùng cách đặt ở dòng thứ nhất.
Với một key đã lọt ra ngoài, bạn làm được hai việc, và hiệu quả của chúng không giống nhau.
Giá trị mà người khác đã đọc vẫn còn hiệu lực, và nó cũng nằm lại trong lịch sử thay đổi của Git.
Bên phải là thu hồi (revoke: nhà cung cấp đưa một key đã phát hành về trạng thái không dùng được nữa).
Chỉ đặt key ở nơi không bao giờ đến thiết bị
Bất kỳ ai giữ key đều dùng được nó với tư cách chủ của nó.
Vì vậy đừng viết key vào file đến được thiết bị của người dùng, cũng đừng viết vào mã nguồn bạn công khai, và khi lỡ để nó lọt ra ngoài thì hãy thu hồi rồi phát hành key mới, thay vì chỉ xóa những ký tự đã viết.
Webhook là cơ chế để dịch vụ bên ngoài gọi tới server của bạn
Thanh toán hoàn tất hay hoàn tiền là những sự kiện mà bên gọi không biết khi nào xảy ra.
Hỏi đi hỏi lại thì làm tăng số lệnh gọi vô ích, và bạn cũng biết muộn hơn.
Webhook (webhook: cơ chế mà khi một sự kiện đã định xảy ra, dịch vụ bên ngoài gửi request tới một URL đã đăng ký) đảo ngược chiều này.
Chỉ Webhook ở dòng thứ ba mới đi theo chiều từ dịch vụ thanh toán tới my-app.
Thứ bạn chuẩn bị chỉ là đăng ký sẵn một URL để nhận thông báo.
Khi thanh toán hoàn tất, khi biên lai được tạo, và khi có hoàn tiền, mỗi lúc lại có một thông báo riêng gửi đến.
Thông báo đó thuộc về sự kiện nào thì được ghi trong phần thân của request.
Mũi tên 1 là chiều bạn gọi đi, mũi tên 2 là chiều dịch vụ bên ngoài gọi đến bạn.
Key chỉ được chuyển cho dịch vụ thanh toán, không đến thiết bị của người dùng.
Webhook là lệnh gọi theo chiều ngược lại
Webhook là lệnh gọi trong đó dịch vụ thanh toán gọi tới my-app.
Nó dùng để bạn được báo ngay khi xảy ra một sự kiện không đoán trước được thời điểm, và thứ bạn chuẩn bị chỉ là tạo sẵn rồi đăng ký một URL để nhận thông báo.
Ai cũng gọi được URL nhận thông báo, nên hãy đối chiếu chữ ký trước khi xử lý
URL nhận thông báo là công khai, nên một người nào đó không phải dịch vụ thanh toán cũng gửi được tới chính URL ấy.
Thứ ngăn được điều này là chữ ký (signature: giá trị tính ra từ một chuỗi ký tự mà chỉ bên gửi và bên nhận biết, được gắn kèm request, rồi bên nhận kiểm tra bằng cách chạy đúng phép tính đó).
- POST https://my-app.example.com/webhooks/payment
- URL do chính bạn tạo và đăng ký
- Chữ ký nằm ở đây
- ID để nhận ra thông báo lặp lại cũng thường nằm ở đây
- Loại sự kiện, viết ở dạng JSON
- ID và trạng thái của khoản thanh toán cũng ở đây
Hãy đối chiếu chữ ký trong header trước khi đọc nội dung.
Khi bạn không trả về thành công, bên gửi sẽ gửi lại đúng thông báo đó, và đó là gửi lại (retry).
Hãy ghi lại ID của từng thông báo và bỏ qua những cái đã xử lý.
Giống như API key, chuỗi dùng để đối chiếu cũng không viết vào mã nguồn, còn cách gắn nó thì bạn tra trong tài liệu API.
Khi tài liệu hướng dẫn nói "hãy xác minh chữ ký", đó chính là phép đối chiếu này.
Đối chiếu trước khi đọc, và chuẩn bị cho lần gửi thứ hai
URL nhận thông báo là công khai, nên chỉ riêng việc nhận được một request thì bạn vẫn chưa biết ai gửi nó.
Hãy đối chiếu chữ ký trước khi đọc phần thân, nếu khớp thì cập nhật bản ghi và trả về mã trạng thái thành công, rồi nhớ ID của những thông báo đã xử lý để lần gửi thứ hai được bỏ qua.
Kiểm tra kiến thức
Hãy trả lời từng câu hỏi một.
Câu 2Viết API key vào mã đến được trình duyệt thì có vấn đề gì?
Câu 3Khi nhận được thông báo từ Webhook, bạn làm gì trước khi đọc nội dung?