Bảo mật cho web API thế nào cho tốt ?
Các hệ thống web hiện đại ngày nay đã không còn giữ các mô hình truyền thống nữa (server trả về HTML), mà thay vào đó là tập trung vào tối ưu đường truyền và khả năng phần cứng của client. Do đó, việc phát triển web API ngày càng được chú trọng hơn. Tuy nhiên khi phát triển các API, cũng có nhiều điều cần lưu ý, tránh bị khai thác các lỗ hổng.
Việc viết API thực ra có thể viết đơn giản cũng được, nhưng rồi sẽ đến lúc các API bị phá hoại hay đến lúc database xuất hiện những request "kì dị" thì chúng ta lại cần xem xét lại API. Vậy sao không viết chuẩn luôn ngay từ đầu có hơn không. Dưới đây là một số vấn đề mà chúng ta nên quan tâm khi viết API.
Việc viết API thực ra có thể viết đơn giản cũng được, nhưng rồi sẽ đến lúc các API bị phá hoại hay đến lúc database xuất hiện những request "kì dị" thì chúng ta lại cần xem xét lại API. Vậy sao không viết chuẩn luôn ngay từ đầu có hơn không. Dưới đây là một số vấn đề mà chúng ta nên quan tâm khi viết API.
NOTE: Đây không phải là chuẩn API Restful hay bất kỳ chuẩn gì để mọi người nên theo, mà là những vấn đề về bảo mật liên quan tới API cần lưu ý.
1. Request Injection.
Injection nói chung và SQL Injection là một lỗi rất phổ biến, có lẽ cuộc đời làm dev, ai cũng biết. Nhưng nó biểu hiện như nào thì có rất nhiều trường hợp mà chúng ta khó có thể kiểm soát hết. Trong phạm vi bài viết là web API, thì SQL Injection biểu hiện rõ nhất có lẽ là các tham số của request (của bất kỳ method nào GET,POST,PUT, DELETE,...). Giả sử chúng ta gọi API của server:
Tại phía server, chúng ta xử lý như sau:
GET pageapi.com/1
Tại phía server, chúng ta xử lý như sau:
$sql = "SELECT * FROM POSTS WHERE id='" + $id + "'"
nếu như $id của chúng ta là 1 tham số ngay sau '/' thì nếu người dùng nhập:
pageapi.com/1' or '1'='1
Chúng ta nhận được
$id = "1' or '1'=1'"
Lúc này, $sql trở thành:
$sql = "SELECT * FROM POSTS WHERE id='1' or '1'='1'"
Đây là 1 câu SQL hợp lệ, chỉ có điều nó sẽ show hết các POSTS ra và chúng ta bị lộ hết các dữ liệu nhạy cảm.
=> Cách khắc phục: validate mọi tham số trước khi request. Tuy nhiên validate ở đây không có nghĩa là validate form như phía client, do API chúng ta không điều khiển được người dùng nhập thứ gì vào (thậm chí kể cả đã validate phía client rồi vẫn nen validate lại phía server). Có một số phương pháp sau đây có thể được sử dụng:
- Kiểm tra ký tự nhập vào: Chạy 1 hàm replace ký tự trắng hoặc ký tự đặc biệt. Tuy nhiên điều này gây lãng phí tài nguyên và có thể gây chậm hệ thống do lần nào cũng phải check.
- Sử dụng prepared statement: Đây là cách phổ biến và thường được sử dụng nhất do nó biến các ký tự đặc biệt thành format thống nhất để nhập vào database, như vậy khi tìm kiếm hoặc query với các ký tự đặc biệt, chúng ta sẽ không phải viết validate quá phức tạp.
- Sử dụng ký tự escape: Prepare statement được sử dụng khá phổ biến, tuy nhiên, đối với các hệ thống Database dùng NoSQL không có prepare statement hay lưu trữ text, không lưu vào DB thì phương án này không sử dụng được. Do đó người ta thường sử dụng kí tự escape. Ký tự escape thường không cố định, tùy thuộc vào framwork/library/... và tùy vào định nghĩa của người dùng, tuy nhiên, khi lấy dữ liệu để hiển thị thì nhớ bỏ ký tự escape để hiển thị cho đúng là được.
- Encode: Đối với những dữ liệu kiểu key hay từ khóa mà không có nhu cầu query tương đồng, chỉ query chính xác , có thể encode theo 1 cách nào đó như md5, rsa, ... (Không nhất thiết là hash, mục đích là để không còn ký tự đặc biệt khi đưa vào query chứ không hẳn là che giấu dữ liệu, còn nếu muốn che thì hash).
Ngoài SQL Injection, cũng có những lỗi liên quan tới Injection như NoSQL Injection, Data Injection (XSS), ... nhưng mình chưa thấy gặp nhiều nên chưa đủ cơ sơ để viết. Hoặc nếu có thì sử dụng các phương án khắc phục trên cũng đã đủ về cơ bản.
2. Spam request.
Request để public rất dễ bị spam hoặc tấn công DDOS. Giả dụ một API:
POST pageapi.com/register
POST pageapi.com/register
{ 'username': 'myusername',
'password': 'mypasswordhashed',
...
}
Với API đăng kí 1 bước như trên, người nào đó viết một đoạn script gửi liên tiếp request lên server thì server sẽ đăng kí liên tục, kể cả việc xác thực tài khoản có diễn ra sau đó, server vẫn phải xử lý hết tất cả request gửi lên server.
=> Cách khắc phục: Làm phức tạp hóa các bước lên. Tuy nhiên cần chú ý, làm phức tạp hóa thao tác xác thực không có nghĩa là làm phức tạp hóa cách người dùng sử dụng. (Phức tạp hóa ngầm)
=> Cách khắc phục: Làm phức tạp hóa các bước lên. Tuy nhiên cần chú ý, làm phức tạp hóa thao tác xác thực không có nghĩa là làm phức tạp hóa cách người dùng sử dụng. (Phức tạp hóa ngầm)
- Thêm token key: Thêm token key rất hữu dụng trong việc xử lý các request trên cùng một session. Trước khi đăng kí, có thể cho người dùng nhập câu hỏi bảo mật (câu hỏi bảo mật có thể generate từ 1 bảng trong hệ thống ra và tráo đổi từng ngày nếu cần thiết đề phòng có người viết AI học câu trả lời). Hiện nay viết được một hệ thống AI hiểu được câu hỏi cũng hơi khó, mà trả lời được còn khó hơn nên sẽ hạn chế được spam request.
- Từ chối dịch vụ mềm: Lưu các request vào trong một queue, và khi queue đó đầy, đưa ra thông báo server đang quá tải, thử lại trong giây lát. Cơ mà thực tế là nếu như này thì bọn spam request đã đạt được mục đích rồi. Do vậy, không nên đặt giới hạn cho queue, mà yêu cầu người dùng chờ trong một thời gian rồi mới chuyển sang action tiếp theo.
- Encode tùy biến: Đây là phương án tổng quát từ phương án 1. Xây dựng list những hàm mã hóa khác nhau ở trong code phía client. Tuy nhiên việc sử dụng code nào để mã hóa thì được cung cấp ở 1 API khác với token cho trước. Đối với phương án này, nếu ai đó muốn viết lại script để spam thì họ phải viết lại tất cả những đoạn code mã hóa hoặc sử dụng chính đoạn code đó. Lúc đó phụ thuộc vào trình độ viết code bá đạo của anh em :)), viết cho người ta hiểu thì khó chứ viết cho người ta không hiểu chắc là dễ.
3. Chặn outside request.
Chặn request từ phía ngoài, chỉ cho phép request đến từ app do nội bộ phát triển được sử dụng.
API không cho phép truy cập public, cũng giống như spam request phía trên, tuy nhiên phần phía trên nghiêng về xử lý khi có spam request hơn là ngăn ngừa. Phần phía dưới đây sẽ nêu một số phương án mã hóa token API.
API không cho phép truy cập public, cũng giống như spam request phía trên, tuy nhiên phần phía trên nghiêng về xử lý khi có spam request hơn là ngăn ngừa. Phần phía dưới đây sẽ nêu một số phương án mã hóa token API.
- Đối với App, sử dụng tokenkey + hashkey. Hashkey được sinh ra từ việc hash chuỗi bao gồm url, toàn bộ param và appkey bí mật theo một thứ tự ngầm định sẵn (để bên server sau khi hash chuỗi y như vậy có thể check được). Việc hash như vậy sẽ giúp tránh việc một người dùng khác biết Appkey bí mật có thể tự mã hóa, tuy nhiên họ sẽ khó biết thứ tự mã hóa đã được tạo ra như thế nào để giả mạo API request.
- Sử dụng appkey+TIMESTAMP làm hash key. Appkey có thể bị đánh cắp, tuy nhiên, với cùng một appkey, kết hợp thêm TIMESTAMP sẽ biết được Appkey nào đang sử dụng quá nhiều tài nguyên cùng lúc để có các phương án xử lý như trong phần 2. Tuy nhiên phương án này thường nên dùng đối với webapp và appkey ở đây là session key. Vì đối với app (mobile app), có thể nhiều người cùng truy cập một lúc thì việc 1 appkey sử dụng quá nhiều tài nguyên cũng là bình thường.
Trên là những mẩu kiến thức vụn vặt cóp nhặt và tích lũy được ạ, mong mọi người góp ý.
Nguồn: http://kipalog.kaopiz.com
Nguồn: http://kipalog.kaopiz.com
Comments
Post a Comment