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 (P2).

3. Sử dụng token để tránh lỗ hổng CSRF

- Nguy cơ: CSRF (Cross-site request forgery) là phương pháp mượn quyền của người dùng khác để thực hiện một hành động không cho phép. Ví dụ: để có thể xóa một bài viết trên diễn đàn một member có thể mượn tay của admin để làm việc đó, vì member không đủ quyền. Kẻ tấn công lừa admin truy cập vào trang web có chứa đoạn mã xóa bài viết trên diễn đàn (Admin đang đăng nhập vào diễn đàn), như vậy admin đã gửi yêu cầu xóa bài viết trên diễn đàn mà không hề hay biết.

- Phòng chống: Đối với các yêu cầu ảnh hưởng tới dữ liệu, quá trình hoạt động và có khả năng làm mất an toàn thôn tin hệ thống, ví dụ: yêu cầu đọc, ghi, sửa, xóa thông tin, dữ liệu hệ thống phải sử dụng thêm biến token. Trên server sẽ kiểm tra token trong yêu cầu gửi lên từ client, nếu token không hợp lệ thì yêu cầu sẽ không được thực hiện. *Ví dụ 1:*Ứng dụng struts cho phép hiển thị lời chào với tên người dùng nhập từ form: index.jsp

<%@taglib prefix="s" uri="/struts-tags"%>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    </head>
    <body>
        <h2>Hello World!</h2>
        Struts 2 Mesage: <s:property value="message" default="Guest."/>
        <s:form method="GET" action="/HelloStruts2World.action">
        Enter your name: <s:textfield name="userName"/>
        <s:submit value="Submit"/>
    </body>
</html>

struts.xml

<struts>
    <package name="/" extends="struts-default">
        <action name="HelloStruts2World" class="hello.HelloStruts2World">
            <result name="success">/index.jsp</result>
        </action>
     </package>
</struts>

helloStruts2World.java

package hello;
import com.opensymphony.xwork2.ActionSupport;
public class HelloStruts2World extends ActionSupport {
    private String userName;
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    
    private String message;
    public String getMessage() {
        return message;
    }
    
    @Override
    public String execute() {
        message = "Hello, " +userName+ ".";
        return SUCCESS;
    }
}
  • Tuy nhiên, yêu cầu trên có thể được thực hiện mà không cần phải nhập userName từ form bằng cách đưa trực tiếp vào URI http://localhost/TestStruts/HelloStruts2World.action?userName=acb
  • Để khắc phục lỗi trên, ta có thể sử dụng token intercepter đã có sẵn của struts.
  • index.jsp
<%@taglib prefix="s" uri="/struts-tags"%>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    </head>
    <body>
        <h2>Hello World!</h2>
        Struts 2 Mesage: <s:property value="message" default="Guest."/>
        <s:form method="GET" action="/HelloStruts2World.action">
        <s:token/>
        Enter your name: <s:textfield name="userName"/>
        <s:submit value="Submit"/>
    </body>
</html>

struts.xml

<struts>
    <package name="/" extends="struts-default">
        <interceptor>
            <interceptor-stack name="defaultSecurityStack">
                <interceptor-ref name="defaultStack"/>
                <interceptor-ref name="tokenSession">
                    <param name="excludeMethods">*</param>
                </interceptor-ref>
            </interceptor-stack>
        </interceptor>
        <default-interceptor-ref name="defaultSecurityStack"/>
        <global-results>
            <result name="invalid.token">/error.jsp</result>
        </global-results>
        <action name="HelloStruts2World" class="hello.HelloStruts2World">
            <result name="success">/index.jsp</result>
        </action>
     </package>
</struts>

4. Kiểm soát file upload lên hệ thống

- Nguy cơ: Các thao tác với file thường sử dụng tên file, đường dẫn file được gửi lên từ client, nếu ứng dụng không kiểm soát tốt các giá trị này (việc kiểm soát phải được thực hiện phía server) có thể dẫn đến việc download hoặc upload các file không hợp lệ.

- Phòng chống: Kiểm soát phía server tên file, đường dẫn file được gửi lên từ client

  • Kiểm soát phần mở rộng của file, chỉ cho phép thực hiện với các file có định dạng theo yêu cầu. Không bắt buộc phải kiểm tra nội dung file.
  • Các hàm liên quan đọc ghi file, biến đường dẫn file phải được lọc /,\ và kí tự null.
  • Phần filename ban đầu người dùng upload lên server phải bỏ đi, dùng 1 chuỗi mới ngẫu nhiên thay thế cho tên file. Tên này được sinh ra ngẫu nhiên không được dùng các thuật toán md5, sha256 ... Thay vào đó có thể sử dụng các hàm sinh chuỗi ngẫu nhiên có sẵn trong ngôn ngữ lập trình để sinh ra tên. Ví dụ 1: Tạo tên file cho file ảnh định dạng jpg với hàm UUID có sẵn như sau:
import uuid
filename='%032x' % uuid.uuid4() + '.jpg'

Tên file ban đầu là shell.php.jpg upload lên server thì đổi thành 6817c84abd3d4c9abfee21a01cb39b98.jpg