Trong thế giới bảo mật web không ngừng phát triển, xác thực và ủy quyền là xương sống của hầu hết các hệ thống an toàn. Tuy nhiên, chỉ dựa vào các cơ chế này mà không giải quyết các rủi ro CSRF (Cross-Site Request Forgery) hoặc tấn công brute-force có thể khiến API của bạn bị lộ.
Nghe có vẻ đáng báo động phải không? Ngay cả các nền tảng hàng đầu như X (trước đây là Twitter), Facebook, TikTok và Instagram cũng không tránh khỏi vấn đề này. Vậy, làm thế nào để chúng ta chống lại điều này? Giới thiệu Bảo vệ CSRF Tiên tiến bằng cách sử dụng mã hóa RSA— một cách nhẹ nhưng hiệu quả để ngăn chặn việc sử dụng API trái phép, các nỗ lực brute-force và các yêu cầu cũ.
Kỹ thuật này bổ sung thêm một lớp bảo mật trên hệ thống xác thực hiện có của bạn. Bằng cách mã hóa siêu dữ liệu yêu cầu như phương thức, URL và dấu thời gian bằng RSA, chúng tôi đảm bảo mỗi yêu cầu API được xác thực duy nhất và không thể phát lại hoặc sử dụng lại.
Vậy nó hoạt động như thế nào?
Tôi tận dụng mã hóa bất đối xứng RSA để xác thực tính toàn vẹn của các yêu cầu API. Dưới đây là quy trình:
-
Tạo một cặp khóa RSA (khóa công khai và khóa riêng tư). Bạn có thể sử dụng các công cụ như cryptotools.net/rsagen.
-
Khóa công khai được lưu trữ trên máy khách (lý tưởng nhất là trong các biến môi trường).
-
Trước khi thực hiện yêu cầu API, chúng tôi mã hóa một payload chứa:
- Phương thức yêu cầu
- URL yêu cầu
- Dấu thời gian hiện tại
-
Payload được mã hóa này được gửi dưới dạng tiêu đề csrf tùy chỉnh.
-
Trên backend:
- Payload được giải mã bằng khóa riêng RSA.
- Xác thực đảm bảo:
- Phương thức và URL khớp.
- Dấu thời gian không quá cũ (ví dụ: cũ hơn 15 giây).
- Nếu xác thực thành công, yêu cầu sẽ tiếp tục; ngược lại, nó sẽ bị từ chối.
Cách tiếp cận này đảm bảo rằng mọi yêu cầu API đều được liên kết chặt chẽ với các tham số cụ thể, ngăn chặn các cuộc tấn công replay, các yêu cầu cũ và lạm dụng brute-force.
Triển khai phía Client (React + Axios)
Để hợp lý hóa việc xử lý CSRF và đảm bảo rằng nó sẽ có sẵn cho tất cả các yêu cầu của tôi, tôi sẽ sử dụng Axios interceptors. Dưới đây là thiết lập trong TypeScript:
import axios, { AxiosError } from "axios";
import appConfig from "config"; // Import your app config
import { rsaCrypt } from "Utils/rsaCrypt"; // RSA utility const axiosClient = axios.create({ baseURL: appConfig.BASE_URL, headers: { "Content-Type": "application/json", },
}); // Helper function to create the CSRF payload
const getCsrfPayload = (method: string, url: string) => { return { url, method, timestamp: Date.now(), };
}; // Axios request interceptor
axiosClient.interceptors.request.use( async (config) => { if (config && config.headers) { // Generate CSRF payload const csrfPayload = getCsrfPayload(config.method!, config.url!); // Encrypt the payload const csrfToken = await rsaCrypt.encrypt(JSON.stringify(csrfPayload)); if (csrfToken) { config.headers["csrf"] = csrfToken; // Attach the encrypted token to headers } } return config; }, (error) => { return Promise.reject(error); }
); // Axios response interceptor
axiosClient.interceptors.response.use( function (response) { return response; }, async function (error: AxiosError<{ code: string; message: string }>) { return Promise.reject(error); }
); export default axiosClient;
Triển khai phía Backend (Node.js + Express)
Trên máy chủ, tôi sẽ xác thực mã thông báo CSRF đến bằng khóa riêng RSA và đảm bảo tính toàn vẹn của yêu cầu.
1. Middleware để Xác thực CSRF
import { NextFunction, Request, Response } from "express";
import { BadRequestError } from "../utils/custom.error";
import { rsaEncrypt } from "../utils/rsa.crypt"; type EncPayload = { url: string; method: string; timestamp: number;
}; export async function csrfValidation(req: Request, res: Response, next: NextFunction) { const urlPath = decodeURIComponent(req.originalUrl.split("?")[0]?.trim() || ""); const method = req.method.toLowerCase(); const csrf = req.headers["csrf"]; if (!csrf || Array.isArray(csrf)) { throw new BadRequestError("CSRF token is missing or malformed.", "CSRF_TOKEN_MISSING"); } const now = Date.now(); const payload = decryptPayload(csrf); if (!payload) { throw new BadRequestError("Failed to decrypt or parse CSRF token.", "CSRF_TOKEN_INVALID"); } const payloadUrlPath = decodeURIComponent(payload.url.split("?")[0]?.trim() || ""); if (urlPath !== payloadUrlPath || payload.method.toLowerCase() !== method) { throw new BadRequestError( "CSRF token validation failed: URL or method mismatch.", "CSRF_VALIDATION_FAILED" ); } const queryExpiry = 15 * 1000; // 15 seconds if (now - payload.timestamp > queryExpiry) { throw new BadRequestError("CSRF token has expired.", "CSRF_TOKEN_EXPIRED"); } next();
} function decryptPayload<T = EncPayload>(payload: string): T | undefined { try { return JSON.parse(rsaEncrypt.decrypt(payload)) as T; } catch (error) { console.error("Error decrypting payload:", error); return undefined; }
}
Các tính năng chính:
- Mã hóa bất đối xứng: Chỉ máy chủ biết khóa riêng, đảm bảo mã thông báo không thể bị giả mạo.
- Độ tươi mới của yêu cầu: Dấu thời gian giới hạn thời hạn hiệu lực của mã thông báo là 15 giây, ngăn chặn các cuộc tấn công replay.
- Xác thực phương thức & URL: Đảm bảo mã thông báo được gắn với các hành động cụ thể, chặn các tập lệnh brute-force.
Kết luận
Phương pháp này tăng cường bảo mật API của bạn bên cạnh các cơ chế xác thực và ủy quyền hiện có. Bằng cách giới thiệu các mã thông báo CSRF được mã hóa, có dấu thời gian, bạn có thể ngăn chặn những kẻ tấn công độc hại lạm dụng API của bạn bên ngoài trình duyệt.
Hãy thử cách này và nâng cao bảo mật ứng dụng của bạn!