跨站請求偽造(CSRF)攻擊原理及預防手段

陈晓猛發表於2024-07-09

1、什麼是跨站請求偽造?


CSRF(Cross-site Request Forgery,跨站請求偽造)攻擊是一種常見的Web攻擊,它利用使用者在登入某個網站後的有效session來傳送惡意請求。攻擊者透過引導使用者訪問惡意網站,將使用者的資料提交到目標網站,欺騙目標網站相信該請求是使用者傳送的。

CSRF攻擊的關鍵是攻擊者可以在不知情的情況下利用使用者當前已登入的Web應用程式身份進行操作,而簡單的身份驗證只能證明請求是來自某個特定的使用者,但無法證明請求是該使用者自願發出的,這是CSRF攻擊能夠成功的因素之一。

相比較XSS攻擊而言,XSS攻擊主要是透過向目標網站中注入惡意的指令碼程式碼,利用使用者對該網站的信任來獲取使用者資料。因此,它們之間的攻擊方式確實有很大區別。

2、基本原理

舉一個最簡單的CSRF攻擊例子:
使用者A登入某線上電商網站B,保留cookie。
A在網站B上新增了一件價格為100元的商品到購物車,但是沒有結賬。
攻擊者E誘導A訪問危險網站C。
危險網站C上有一個表單,裡面的“提交”按鈕實際上是發起了向網站B的購物車結賬請求,請求購買了一件價格為1000元的商品。
由於A的瀏覽器上有cookie,這樣瀏覽器發出的結賬請求就能得到響應執行。
這樣A最終支付了1000元,而且她自己並不知情,這就是一個CSRF攻擊的例子。
3、CSRF攻擊的物件和預防思路
簡單來說,為了防範CSRF攻擊,我們需要保護那些可以對資料進行增刪改操作的服務,比如說銀行轉賬、新增商品到購物車、發表評論等。而對於那些只涉及資料讀取、展示的服務,則不需要進行CSRF的保護。
在進行防範時,關鍵之處在於如何在請求中放入駭客所不能偽造的資訊,以保證該請求來源的可靠性。常用的防範策略包括隨機Token機制、Referer檢查、限制HTTP方法等。這些策略旨在在請求中加入駭客難以製造的資訊,讓服務端能夠驗證請求的來源合法性並拒絕不合法的請求,從而有效防止CSRF攻擊的發生。
4、預防手段介紹
隨機化Token(CSRF Token):Token是用於驗證網站請求者身份的一種機制,可以防止CSRF攻擊。該Token會在每次訪問頁面時重新整理,以確保每次請求都需要新的Token。例如,在web應用程式中,可以透過hidden field的方式將Token加入到表單中,提交時驗
Referer檢查:在服務端校驗請求頭中的Referer欄位,確保請求是來自合法的來源頁面,常用於輔助Token機制的驗證。
Cookie SameSite 屬性:應用於Cookie,防止跨站請求偽造攻擊,避免惡意網站使用認證(sso)cookie 訪問目標網站或者欺騙cookie 竊取使用者資料。
限制cookies操作:不能在cookie中加入使用者敏感資訊,避免cookie的資訊外洩。
明確HTTP方法:在服務端校驗HTTP請求方法,確保請求的HTTP方法是明確的GET或POST,避免利用瀏覽器預設採用GET方法提交表單的漏洞情況的出現;
5、Referer檢查簡單實現

/**
 * CSRF - Referer 攔截器
 */
@Component
public class RefererInterceptor extends HandlerInterceptorAdapter {
    /**
     * 白名單 指定信任的地址
     */
    private String[] refererDomain = new String[]{"http://127.0.0.1:8080/","https://www.baidu.com/"};
    /**
     * 是否開啟referer校驗
     */
    private Boolean check =true;
 
 
    @Override
    public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception {
        if (!check) {
            return true;
        }
        String referer = req.getHeader("referer");
        String host = req.getServerName();
        // 可以設定判斷過濾get或post請求等
        //if (!"GET".equals(req.getMethod())) {
            if (referer == null) {
                // 狀態置為404
                resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
                return false;
            }
            java.net.URL url = null;
            try {
                url = new java.net.URL(referer);
            } catch (MalformedURLException e) {
                // URL解析異常,也置為404
                resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
                return false;
            }
            // 首先判斷請求域名和referer域名是否相同
            if (!host.equals(url.getHost())) {
                // 如果不等,判斷是否在白名單中
                if (refererDomain != null) {
                    for (String s : refererDomain) {
                        if (s.equals(String.valueOf(url))) {
                            return true;
                        }
                    }
                }
                return false;
            }
        //}
        return true;
    }
}

註冊攔截器

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Autowired
    private RefererInterceptor refererInterceptor;
    /**
     * 註冊過濾器
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(refererInterceptor).addPathPatterns("/**").excludePathPatterns("/", "/login", "/logout");
    }
}

相關文章