Người viết: maiphuoctung
1. Kiểm soát truy vấn cơ sỡ dữ liệu để tránh lỗ hổng SQL Injection
– Nguy cơ: Khi truy vấn tới cơ sở dữ liệu, lập trình viên thường sử dụng cách cộng xâu Input từ người dùng, các câu truy vấn này có thể bị mắc lỗi SQL Injection hoặc HQL Injection (nếu sử dụng Hibernate). Bằng việc lợi dụng các lỗi này, kẻ tấn công có thể xem, sửa, xóa dữ liệu trong database, từ đó chiếm được tài khoản admin, lấy cắp thông tin người dùng ….
– Phòng chống:
- Truy vấn SQL phải dùng PrepareStatement, tất cả tham số phải được add bằng hàm (setParam…), không được sử dụng cách cộng xâu trong truy vấn.
- Với một số trường hợp sử dụng ORDER BY, không thể dùng được hàm setParam thì có thể định nghĩa một mảng chứa toàn bộ các column (field) cần ORDER BY gọi là whitelist. Mỗi khi cần ORDER BY thì kiểm tra lại xem column (field) đó có thuộc mảng whitelist đã định nghĩa không. Ví dụ 1: Đoạn code kiểm tra đăng nhập với username/password do người dùng nhập vào:
1 2 3 4 5 6 7 8 9 10 | String sql = "select * from users where user_name = "' + userName + '" and password = "' + encrypt(password) + '"; Statement statement = connection.createStatement(); ResultSet rs = statement.excuteQuery(sql); if (!rs.next()) { bResult = false; } else { bResult = true; } |
- Với đoạn code trên, khi nhập vào username là test’ or ‘1’=’1 thì câu query sẽ là: select from users where username=’test’ or ‘1’=’1′ and password=’…’. Mệnh đề where sẽ tương đương với user_name = ‘test’. Như vậy, dù không có password vẫn đăng nhập được vào hệ thống.
- Sửa đoạn code trên lại như sau, với userName và password được tham số hóa khi đưa vào câu truy vấn sẽ tránh được lỗi SQL Injection:
1 2 3 4 5 6 7 8 9 10 11 12 | String sql = "select * from users where user_name = ? and password = ?"; PreparedStatement statement = connection.preparedStatement(sql); statement.setString(0, userName); statement.setString(1, encrypt(password)); ResultSet rs = statement.excuteQuery(sql); if (!rs.next()) { bResult = false; } else { bResult = true; } |
Ví dụ 2: Một số trường hợp không thể ngăn chặn được lỗi SQL Injection qua lệnh “order by”. Do không sử dụng được hàm setParam thì có thể sử dụng phương pháp sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | // Mảng lưu danh sách các column (field) của BO cần order by (hay gọi là whitelist) private static List columnSort = new ArrayList(); public static String getColumnSort(String sortField) { // Thực hiện 1 lần và lấy ra toàn bộ mảng column cần order và add vào whitelist if (columnSort.size() == 0) { // Danh sách BO cho phép order by String[] arrTableName = {"ActionLog", "BanPosition", "Category", ...}; }; // Lấy ra toàn bộ các column (field) BO cần order by for (String tableName : arrTableName) { try { Class class = Class.forName("com.demo.DEMO.database.BO." + tableName); Field[] fieldArr = class.getDeclaredFields(); for (int i = 0; i< fieldArr.length; i++) { String fieldName = fieldArr[i].getName(); // add các column vào 1 mảng columnSort.add(fieldName); } } catch (ClassNotFoundException ex) { } } } // Cắt ký tự "-" ở đầu field sort String sort = sortField; if (sortField != null && sortField.startsWith("-")) { sortField = sortField.substring(1); } // Kiểm tra field cần order by có nằm trong danh sách field cho phép sort hay không if (sortField != null && columnSort.contains(sortField)) { return sort; } return null; |
Có thể bạn quan tâm
67 công cụ hữu ích, thư viện và tài nguyên để tiết kiệm thời gian cho web developer
Từ MVC đến các Modern Web Framework
2. Xử lý dữ liệu đầu vào để tránh lỗ hổng XSS
– Nguy cơ: Kết quả server trả về cho người dùng chủ yếu dưới dạng HTML. Nội dung trả về thường bao gồm cả những giá trị mà người dùng nhập vào hệ thống có thể bị mắc lỗi XSS nếu không kiểm soát dữ liệu đầu vào. XSS (Cross-Site Scripting) là một kỹ thuật tấn công bằng cách chèn vào các website động (JSP, ASP, PHP …) những thẻ HTML hay những đoạn mã script nguy hiểm có thể gây nguy hại cho những người sử dụng khác. Trong đó, những đoạn mã nguy hiểm được chèn vào hầu hết được viết bằng Cross-Site Scrip như JavaScript, JScript, DHTML và cũng có thể là cả các thẻ HTML.
– Phòng chống:
- Encode dưới dạng HTML các ký tự đặc biệt do client gửi đến bao gồm: <,>,&,’,”,/ trong các trường hợp
- Dữ liệu client gửi lên máy chủ.
- Dữ liệu lấy ra từ database khi trả về cho client. Bảng chất của việc encode là thay thế các ký tự trên bằng chuỗi tương ứng trong bảng bên dưới:
1 2 3 4 5 6 7 8 9 10 | | STT | Ký tự | HTML | | --- | ----- | ------- | | 1 | " | " | | 2 | & | & | | 3 | ' | ' | | 4 | / | /t; | | 5 | < | < | | 6 | > | > | |
Ví dụ 1: Trang JSP bên dưới hiển thị lên lời chào với tên người dùng được lấy từ client
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <html> <head> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"> <title>JSP Page</title> </head> <body> <% String user = request.getParameter("user"); request.setAttribute("user", user); %> <h1> Hello ${user}! </h1> </body> </html> |
- Khi nhập vào địa chỉ trình duyệt
http://localhost/example?user=abc
thì trên trình duyệt sẽ hiện thị dòng Hello abc! - Khi nhập vào địa chỉ trình duyệt
http://localhost/example?user=abc<script>alert('XSS')</script>
thì trên trình duyệt sẽ thực hiện đoạn JavaScript thông báo XSS. - Để khắc phục lỗi này ta có thể dùng thư viên JSTL để encode HTML biến user
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%> <html> <head> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"> <title>JSP Page</title> </head> <body> <% String user = request.getParameter("user"); request.setAttribute("user", user); %> <h1> Hello ${fn:escapeXml(user)}! </h1> </body> </html> |
Mình xin tạm dừng bài viết tại đây! Trong bài sau, mình sẽ nói tiếp với “Sử dụng token để tránh lỗ hổng CSRF” và “Kiểm soát file upload lên hệ thống.”
Techtalk via Viblo