CSRF(Cross-Site Request Forgery),即跨站點請求偽造。發起跨站點請求偽造攻擊的關鍵點在於讓目標服務器無法分辨眾多請求的來源是真實用户還是攻擊者。攻擊的一般流程為:首先攻擊者會誘導用户導航至攻擊者提供的網頁上。該網頁包含一個自動發送到目標服務器的請求。然後該網頁正常加載,這個請求就會自動發送至服務器。在服務器看來,這個請求和用户正常發送的請求一模一樣,殊不知這是由攻擊者發起,而用户卻毫不知情。由於該請求攜帶了用户的一些憑據,攻擊者通過解析這些憑據,就可以獲取用户信息,進而產生安全風險。
本文介紹了 Apache APISIX 的 CSRF 安全插件 csrf,並詳細説明如何在 Apache APISIX 中藉助 csrf 插件來保護您的 API 信息安全。
插件介紹
csrf 插件基於 Double Submit Cookie 方案實現。根據 RFC 7231#section-4.2.1 的定義,我們把 GET、HEAD 和 OPTIONS 這三種方法稱為安全方法。按照這一約定,csrf 插件會直接放行這三種方法,但會對其他的方法做檢查並攔截其中的不安全請求。
為了抵禦 CSRF 攻擊,我們需要製造一個無法偽造的令牌或標識符,並且保證這個不會與攻擊者的請求一起發送。用户需要將 csrf 插件依賴的 token 攜帶在請求頭,token 使用密鑰進行簽名計算。這樣就保證了 token 無法被他人偽造,從而保證了 API 的安全。
在路由中開啓 csrf 插件後,訪問該路由的所有請求響應中都會包含攜帶了 csrf token 的 Cookie。
用户需要在對該路由的不安全請求中攜帶這一 Cookie,並在請求頭添加額外的字段攜帶 Cookie 的內容。字段為插件配置中的 name 值,這樣的請求才能通過 CSRF 插件的校驗。
用户在插件的配置中提供一個隨機密鑰,插件使用該密鑰對 token 信息進行 sha256 哈希加密,然後生成 CSRF token,從而保證該 token 不可偽造。
如何使用
配置開啓 CSRF 插件的路由
在 APISIX 中使用 Admin API 創建一條路由並啓用 csrf 插件:
curl -i http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"uri": "/hello",
"plugins": {
"csrf": {
"key": "edd1c9f034335f136f87ad84b625c8f1"
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:9001": 1
}
}
}'
其中對於插件有三個配置項:
key:必填項,隨機秘鑰的值。用户需要提供一個隨機密鑰。expires:選填項,隨機秘鑰的過期時間,默認值為 7200 秒。由於 CSRF token 使用 Cookie 下發至客户端,該配置會被放置在 Cookie 的配置中,從而控制 Cookie 的過期時間。另外在插件內部也會計算時間來判斷 token 是否過期。name:選填項, CSRF token 的名稱,默認值為apisix-csrf-token。
發送請求測試
首先使用 POST 請求訪問該路由:
curl -i http://127.0.0.1:9080/hello -X POST
Apache APISIX 會攔截該請求並返回 401 錯誤。在返回的頭部中會發現設置了一個 Cookie,如果沒有配置插件的 name 字段的話,Cookie 內部應該為默認值 apisix-csrf-token=.... 。這就是 CSRF 插件生成的 CSRF token。在請求中,需要確保請求攜帶該 Cookie 並且在請求頭寫入該 token。
HTTP/1.1 401 Unauthorized
Set-Cookie: apisix-csrf-token=eyJyYW5kb20iOjAuNjg4OTcyMzA4ODM1NDMsImV4cGlyZXMiOjcyMDAsInNpZ24iOiJcL09uZEF4WUZDZGYwSnBiNDlKREtnbzVoYkJjbzhkS0JRZXVDQm44MG9ldz0ifQ==;path=/;Expires=Mon, 13-Dec-21 09:33:55 GMT
{"error_msg":"no csrf token in headers"}
客户端使用 JavaScript 示例:使用 js-cookie 讀取 Cookie 並使用 axios 發送請求。
const token = Cookie.get('apisix-csrf-token');
const instance = axios.create({
headers: {'apisix-csrf-token': token}
});
如果 Cookie 中的 token 和請求頭中的 token 不一致,請求會被 csrf 插件攔截,示例如下:
curl -i http://127.0.0.1:9080/hello -X POST -H 'apisix-csrf-token: differenteyJyYW5kb20iOjAuNjg4OTcyMzA4ODM1NDMsImV4cGlyZXMiOjcyMDAsInNpZ24iOiJcL09uZEF4WUZDZGYwSnBiNDlKREtnbzVoYkJjbzhkS0JRZXVDQm44MG9ldz0ifQ==' -b 'apisix-csrf-token=eyJyYW5kb20iOjAuNjg4OTcyMzA4ODM1NDMsImV4cGlyZXMiOjcyMDAsInNpZ24iOiJcL09uZEF4WUZDZGYwSnBiNDlKREtnbzVoYkJjbzhkS0JRZXVDQm44MG9ldz0ifQ=='
HTTP/1.1 401 Unauthorized
Set-Cookie: apisix-csrf-token=eyJyYW5kb20iOjAuNjg4OTcyMzA4ODM1NDMsImV4cGlyZXMiOjcyMDAsInNpZ24iOiJcL09uZEF4WUZDZGYwSnBiNDlKREtnbzVoYkJjbzhkS0JRZXVDQm44MG9ldz0ifQ==;path=/;Expires=Mon, 13-Dec-21 09:33:55 GMT
{"error_msg":"csrf token mismatch"}
最後使用 curl 驗證正常的訪問:
curl -i http://127.0.0.1:9080/hello -X POST -H 'apisix-csrf-token: eyJyYW5kb20iOjAuNjg4OTcyMzA4ODM1NDMsImV4cGlyZXMiOjcyMDAsInNpZ24iOiJcL09uZEF4WUZDZGYwSnBiNDlKREtnbzVoYkJjbzhkS0JRZXVDQm44MG9ldz0ifQ==' -b 'apisix-csrf-token=eyJyYW5kb20iOjAuNjg4OTcyMzA4ODM1NDMsImV4cGlyZXMiOjcyMDAsInNpZ24iOiJcL09uZEF4WUZDZGYwSnBiNDlKREtnbzVoYkJjbzhkS0JRZXVDQm44MG9ldz0ifQ=='
HTTP/1.1 200 OK
插件在內部需要校驗 Cookie 中的 token 與 請求頭中攜帶 token 的是否一致,並對其進行重新計算簽名來驗證該 token 是否有效。
禁用插件
移除 csrf 插件的相關配置信息,然後發送一個更新路由的請求,即可停用該插件。
curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"uri": "/hello",
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:1980": 1
}
}
}'
總結
本文詳細描述了 csrf 插件的工作方式以及使用方法,希望通過本文可以讓大家對在 Apache APISIX 中使用插件攔截 CSRF 攻擊有更清晰的認識,方便在實際場景中應用。