10/11/2018, 22:32

Các lỗ hổng thường gặp và cách thực hiện lập trình an toàn trong phát triển ứng dụng web (P1).

- 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ừ ...

- 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:
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:
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:

// 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;

- 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:
| STT | Ký tự | HTML    |
| --- | ----- | ------- |
| 1   |   "   | &quot;  |
| 2   |   &   | &amp;   |
| 3   |   '   | &#x27;  |
| 4   |   /   | &#x2Ft; |
| 5   |   <   | &lt;    |
| 6   |   >   | &gt;    |

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

<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
<%@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>

OK, 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."

0