Một số phương pháp bảo mật dành cho webhook

Một số phương pháp bảo mật dành cho webhook

Tin ngắn hàng ngày dành cho bạn
  • Từ lâu rồi suy nghĩ làm thế nào để tăng sự hiện diện thương hiệu, cũng như người dùng cho blog. Nghĩ đi nghĩ lại thì chỉ có cách chia sẻ lên mạng xã hội hoặc trông chờ họ tìm kiếm, cho đến khi...

    In cái áo này được cái tắc đường khỏi phải lăn tăn, càng đông càng vui vì hàng trăm con mắt nhìn thấy cơ mà 🤓

    (Có tác dụng thật nha 🤭)

    » Xem thêm
  • Một vòng của sự phát triển nhiều dự án khá là thú vị. Tóm tắt lại trong 3 bước: Thấy một cái gì đó phức tạp -> Làm cho nó đơn giản đi -> Thêm thắt tính năng cho đến khi nó phức tạp... -> Quay trở lại vòng lặp mới.

    Tại sao lại như vậy? Để mình lấy 2 ví dụ cho các bạn thấy.

    Markdown ra đời với mục tiêu tạo ra một định dạng văn bản thô "dễ viết, dễ đọc, dễ dàng chuyển thành một dạng gì đó như HTML". Vì thời đó chẳng ai đủ kiên nhẫn mà vừa ngồi viết vừa thêm định dạng cho văn bản hiển thị ở trên web như thế nào. Ấy vậy mà giờ đây người ta đang "nhồi nhét" hoặc tạo ra các biến thể dựa trên markdown để bổ sung thêm nhiều định dạng mới đến mức... chẳng nhớ nổi hết cú pháp.

    React cũng là một ví dụ. Từ thời PHP, việc khát khao tạo ra một cái gì đó tách biệt hẳn giao diện người dùng và phần xử lý logic chính của ứng dụng thành 2 phần riêng biệt cho dễ đọc, dễ viết. Kết quả là các thư viện UI/UX phát triển rất mạnh mẽ, mang lại khả năng tương tác với người dùng rất tốt, còn phần logic ứng dụng thì nằm ở một máy chủ riêng biệt. Bộ đôi Front-end, Back-end cũng từ đấy mà thịnh hành, không thể thiếu anh bồi bàn REST API. Ấy vậy mà giờ đây React trông cũng không khác biệt gì so với PHP là mấy, kéo theo là cả Vue, Svelte... lại cùng quy tất cả về một mối.

    Cơ mà không phải vòng lặp là xấu, ngược lại vòng lặp này mang tính tiến hoá nhiều hơn là "cải lùi". Nhiều khi lại tạo ra được cái hay hơi cái cũ thế là người ta lại dựa trên cái hay đó để tiếp tục lặp. Nói cách khác là chắc lọc tinh hoa từng tí một tí một á 😁

    » Xem thêm
  • Song song với các dự án chính thức thì thi thoảng mình vẫn thấy các dự án "bên lề" nhằm tối ưu hoặc cải tiến ngôn ngữ theo khía cạnh nào đó. Ví dụ nature-lang/nature là một dự án hướng tới cải tiến Go, mang lại một số thay đổi nhằm giúp cho việc sử dụng Go trở nên thân thiện hơn.

    Nhìn lại mới thấy hao hao JavaScript 😆

    » Xem thêm

Vấn đề

Mấy hôm vừa rồi tôi có công việc nghiên cứu tích hợp App Store Server Notifications là một dạng webhook để nhận thông báo từ Apple về máy chủ của mình. Trong quá trình tích hợp có một vài chi tiết thú vị mà tôi nghĩ nếu kể ra ở đây hẳn sẽ giúp ích được cho bạn đọc. Nếu chưa biết webhook là gì, bạn đọc có thể tham khảo bài viết Webhook là gì? Sử dụng webhook trong những trường hợp nào?.

Chắc hẳn chúng ta ai cũng biết việc mua hàng trong ứng dụng. Khi mua thành công một đơn hàng, Apple sẽ gửi thông báo về máy chủ của chúng ta, trong thông báo có chứa nhiều thông tin về đơn hàng như tên, ngày mua, trạng thái... từ đó làm căn cứ để tiếp tục xử lý đơn hàng của khách. Bởi vì hành động mua hàng trong ứng dụng thành công chỉ khi người dùng mua thành công, mà để biết mua thành công hay không thì chỉ có Apple mới biết vì họ xử lý quá trình cộng trừ tiền cho chúng ta. Sau đó là một thông báo gửi đến một API (webhook) mà chúng ta thiết lập từ trước, cho biết kết quả của quá trình thanh toán thành công hay thất bại.

Trong quá trình làm, tôi phát hiện ra Apple có cách để bảo vệ dữ liệu mà họ gửi sang máy chủ của mình một cách rất "uy tín", nó tiêu tốn một "chút" thời gian tìm hiểu của tôi và do đó khơi mào cho tôi tổng hợp lại một số phương pháp bảo mật của webhook lâu nay tích lũy được.

Lưu ý rằng vẫn có rất nhiều phương pháp khác không xuất hiện ở đây. Tôi chỉ đơn giản là thống kê lại một số cách thường gặp hoặc đã biết. Vì thế nếu bạn đọc còn biết cách nào nữa thì vui lòng để lại thông tin trong phần bình luận dưới bài viết nhé.

Một số phương pháp bảo mật webhook

Khi cung cấp một đầu API (endpoint) để nhận dữ liệu, nếu chẳng may kẻ tấn công hoặc những người tò mò biết được, kẻ gian sẽ cố tình khai thác bằng cách gửi nhiều thông tin sai lệch đến máy chủ, từ đó khiến cho nhiều rủi ro có thể xảy ra. Thế nên cách hữu hiệu nhất vẫn là giữ bí mật API, đồng thời luôn luôn xác thực dữ liệu nhận được có phải xuất phát từ bên dịch vụ mà chúng ta tích hợp.

Lấy ví dụ bạn cung cấp một endpoint /webhook/serviceA để nhận dữ liệu từ serviceA thì bằng cách nào đó phải chắc chắn dữ liệu vừa nhận được là từ chính serviceA gửi.

HTTPs

Điều kiện tiên quyết đầu tiên là phải yêu cầu https, tức là serviceA sẽ từ chối gửi dữ liệu nếu endpoint không hỗ trợ https.

Ngày nay https đang dần thay thế http truyền thống vì độ tin cậy và khả năng bảo mật cao hơn. Dữ liệu được mã hóa trên đường truyền và hạn chế các cuộc tấn công Man-in-the-middle.

Vì lẽ đó cho nên https trở thành yêu cầu bắt buộc để truyền dữ liệu thông qua các cuộc gọi API - vốn là cơ chế gửi/nhận dữ liệu của webhook.

Trust IPs

Cách tốt nhất để biết được serviceA gửi thì chắc chắn phải gửi từ đúng địa chỉ IP của nó. serviceA có thể phải cung cấp một danh sách các địa chỉ IP thuộc sở hữu của nó, những địa chỉ đó sẽ được dùng để thực hiện truy vấn đến endpoint của chúng ta. Việc cần làm là xác thực xem địa chỉ IP nhận được có nằm trong danh sách mà chúng ta tin tưởng, nếu không thì khả năng rất cao chúng ta đang bị tấn công.

Phương pháp này nhanh mà hiệu quả, nhưng phải được chính serviceA hỗ trợ bởi vì họ phải cung cấp tất cả địa chỉ IP. Tuy nhiên, trong thời đại công nghệ phân tán và hệ thống thông tin chằng chịt như hiện nay, địa chỉ IP có thể được sửa đổi liên tục cho nên việc triển khai có phần phức tạp và mang lại rủi ro trong quá trình vận hành.

Hãy tưởng tượng một ngày đẹp trời, họ (serviceA) thêm một địa chỉ IP mới vào danh sách mà hệ thống của chúng ta chưa kịp cập nhật thì điều gì sẽ xảy ra?

Mã bí mật

"Hãy tự chọn một mã bí mật, nhập vào trang quản lý của chúng tôi và chúng tôi sẽ gửi nó kèm theo dữ liệu về endpoint của bạn" - đây chính là châm ngôn của phương pháp này.

Vì mã bí mật chỉ có hai bên biết cho nên việc không cung cấp đúng mã bí mật có thể coi là một cuộc tấn công từ bên khác nhằm vào. Mã bí mật thường sẽ được gửi lại thông qua tiêu hề (headers) http. Việc của chúng ta là cần lấy ra và so sánh nó xem có khớp với nhau.

Phương pháp này dễ triển khai và có độ tin cậy nhất định, tuy nhiên nếu chẳng may bị lộ mã bí mật thì...ba chấm. Vì mã này thường nằm dưới dạng văn bản, không mã hóa và phải được ghi lại ở đâu đó cho nên độ tin cậy cũng vì thế mà giảm xuống.

Nằm trong mục này thì Basic Auth cũng là một dạng mã bí mật. Cung cấp username và password cho serviceA biết để xác thực trước khi thực hiện cuộc gọi đến endpoint.

Chữ ký số

Các phương phát trên vẫn có một điểm yếu đó là dữ liệu chưa được mã hóa đúng cách, hay nói cách khác là chưa có cách nào để biết liệu dữ liệu được gửi đến máy chủ của chúng ta có đảm bảo tính toàn vẹn? Vì quá trình truyền dữ liệu không chỉ đơn giản là giữa serviceA và endpint, mà nó còn đi qua rất nhiều điểm khác trước khi đến đích. Giả sử dữ liệu bị sửa đổi ở đâu đó thì phải làm như thế nào để phát hiện?

Nếu đã làm việc với JSON Web Tokens (jwt), bạn đọc sẽ biết cơ chế bảo vệ dữ tính toàn vẹn của dữ liệu bằng cách mã hóa bất đối xứng. Với cấu trúc 3 phần được mã hóa bằng base64 của jwt, phần đầu chứa các chỉ dẫn về thuật toán sử dụng mã hóa, phần thân chứa dữ liệu và phần cuối cùng là một chuỗi được tạo ra bằng cách mã hóa bất đối xứng dữ liệu với khóa bí mật. Để xác thực, chúng ta chỉ cần sử dụng khóa công khai để xem dữ liệu có bị sửa đổi sau khi ký (sign) hay không, vì bất cứ thay đổi nào dù là nhỏ nhất trong phần thân cũng khiến cho chuỗi ký bị sai lệch.

Phương pháp này có phần phức tạp hơn nhưng lại cho độ tin cậy cao hơn hẳn. Khóa bí mật cần được giữ an toàn tuyệt đối để ký dữ liệu trước khi gửi đi, khóa bí mật cũng được mã hóa do đó nguy cơ tấn công thấp hơn so với văn bản thông thường.

Bảo mật webhook của Apple

Về cơ bản Apple cũng lựa chọn cách thức mã hóa bất đối xứng để bảo vệ tính toàn vẹn của dữ liệu mà họ gửi đến, nhưng với độ phức tạp cao hơn một chút.

Đầu tiên dữ liệu được mã hóa hết dưới dạng base64 để tăng độ "nguy hiểm", mã hóa này góp một phần nào đó cho quá trình nhìn trộm nhanh và yêu cầu giải mã để biết được nội dung thật là gì.

Apple ký trên một số đối tượng dữ liệu, theo dạng JWS vì thế trước khi sử dụng cần xác định tính toàn vẹn của dữ liệu. Cách làm có thể tóm lại trong 2 bước:

  • Lấy public key trong headers của chuỗi mã hóa
  • Sử dụng public key đó để xác thực chữ ký

Vì vậy, khi giải mã headers, bạn sẽ thấy thuật toán chữ ký, kèm theo một đối tượng x5c giống như:

{
  "alg": "ES256",  
  "x5c": [
    "MIIEMDCCA...",  
    "MIIDFjCCA...",  
    "MIICQzCCA..."
  ]
}

Khi đó khóa công khai để xác nhận dữ liệu là x5c[0]. Nhưng vậy thì x5c[1]x5c[2] có ý nghĩa gì? Chúng ta biết thuật toán mã hóa bất đối xứng dựa trên ES256 rất khó để bẻ khóa, vì thế chỉ cần sử dụng khóa công khai để xác nhận tính toàn voẹn của dữ liệu gần như là tuyệt đối, bởi vì chỉ có Apple mới biết được khóa bí mật.

Sau một lúc tìm hiểu, thì ra hai khóa còn lại dùng để xác thực khóa công khai. Đúng vậy, tức là x5c[1]x5c[2] được dùng để xác thực x5c[0] có đúng là của Apple hay không.

Như vậy, x5c[2] là chứng chỉ dịch vụ gốc của Apple (Certificate Authority (CA)) đã được tin cậy, trong khi x5c[1] là chứng chỉ trung gian và x5c[0] là chứng chỉ dùng để xác minh chữ ký mà Apple đã ký cho dữ liệu.

CA tin cậy được phân phối thông qua hệ điều hành, nghĩa là máy tính khi xuất xưởng đã được cài đặt sẵn một số chứng chỉ CA đáng tin cậy trên thế giới, trong đó có CA của Apple. Một số công cụ như openssl có thể xác định được liệu một CA có đáng tin hay là không. Vì thế luồng xác thực dữ liệu lúc này là sử dụng công cụ để xác minh CA (x5c[2]) -> xác minh chứng chỉ trung gian (x5c[1]) -> xác minh khóa công khai (x5c[0]). Nếu tất cả đều hợp lệ thì chúng ta có thể tin chắc rằng dữ liệu được gửi từ Apple.

Cao cấp
Hello

Bí mật ngăn xếp của Blog

Là một lập trình viên, bạn có tò mò về bí mật công nghệ hay những khoản nợ kỹ thuật về trang blog này? Tất cả bí mật sẽ được bật mí ngay bài viết dưới đây. Còn chờ đợi gì nữa, hãy bấm vào ngay!

Là một lập trình viên, bạn có tò mò về bí mật công nghệ hay những khoản nợ kỹ thuật về trang blog này? Tất cả bí mật sẽ được bật mí ngay bài viết dưới đây. Còn chờ đợi gì nữa, hãy bấm vào ngay!

Xem tất cả

Đăng ký nhận thông báo bài viết mới

hoặc
* Bản tin tổng hợp được gửi mỗi 1-2 tuần, huỷ bất cứ lúc nào.

Bình luận (0)

Nội dung bình luận...