你們使用的服務中或多或少是否是都有如下的功能:html
簡單的來講就是經過 URL 抓取其它服務器上數據而後作對應的操做的功能。以 ThinkJS 代碼爲例,咱們的實現方法大概以下:前端
const request = require('request-promise-native');
module.exports = class extends think.Controller {
async indexAction() {
const { url } = this.get();
const ret = await request.get(url);
// 這裏是處理抓取數據的邏輯
// ...
this.ctx.body = ret;
}
}
複製代碼
原本是個不錯的功能,可是當用戶輸入一個服務器可訪問的內網地址,這個狀況下它就會把內網的內容抓取出來展示給外網的用戶。大多數公司會在內網中放置一些與公司相關的資料和關鍵數據,若是應用程序對用戶提供的URL和遠端服務器返回的信息沒有進行合適的驗證和過濾,就可能存在這種服務端請求僞造的缺陷,即 Server-Side Request Forgery,簡稱 SSRF。git
簡單來講若是你的這個功能存在 SSRF 漏洞的話,至關於在攻擊者和內網之間牽了根線,透過該通能攻擊者能夠間接訪問到內網。攻擊者能夠利用 SSRF 實現的攻擊主要有 5 種:github
其中最後一條的實現方式是用戶輸入 file://
本地文件協議地址,若是不作判斷,程序極可能就會把本地文件讀取出來返回給用戶,例如 file:///etc/password
服務器系統密碼。web
首先咱們須要禁用掉不須要的協議,僅容許 HTTP(s) 請求,防止最後一條使用 file://
等其它協議引發的問題,而後咱們須要對輸出內容進行判斷,例如我應該輸出一張圖片,若是抓取返回回來的是一段文本咱們就不該該返回。以及若是抓取遠端地址致使報錯返回的狀況,咱們須要統一處理返回給用戶的內容,而不是直接將遠端服務器的內容返回給用戶,這樣讓攻擊者瞭解到了更多遠端服務器的信息。數據庫
除了輸出內容的處理,咱們還要對輸入地址進行限制,過濾內網 IP,限制訪問內網行爲。以以前的示例代碼爲例,正常咱們會增長以下處理:promise
const ip = require('ip');
const dns = require('dns');
const { parse } = require('url');
const lookupAsync = think.promisify(dns.lookup, dns);
module.exports = class extends think.Logic {
async indexAction() {
const { url } = this.get();
const { protocol, hostname } = parse(url);
// 判斷協議
if( !/https?:/i.test(protocol) ) {
return this.fail();
}
// 判斷內網IP
cost host = await lookupAsync(hostname);
if( ip.isPrivate(host) ) {
return this.fail();
}
}
}
複製代碼
大部分狀況下這樣處理是沒有問題的,不過攻擊者可不是通常人。這裏存在一個兩個能夠繞過的方式,首先是短連接,短連接是先到短連接服務的地址以後再302跳轉到真實服務器上,若是攻擊者對內網地址進行短鏈處理以後以上代碼會判斷短鏈服務的 IP 爲合法 IP 而經過校驗。瀏覽器
針對這種繞過方式,咱們有兩種方法來阻止:緩存
另一種繞過方式是利用 DNS 重綁定攻擊。安全
DNS如何從新綁定的工做
攻擊者註冊一個域名(如attacker.com),並在攻擊者控制下將其代理給DNS服務器。 服務器配置爲很短響應時間的TTL記錄,防止響應被緩存。 當受害者瀏覽到惡意域時,攻擊者的DNS服務器首先用託管惡意客戶端代碼的服務器的IP地址做出響應。 例如,他們能夠將受害者的瀏覽器指向包含旨在在受害者計算機上執行的惡意JavaScript或Flash腳本的網站。
惡意客戶端代碼會對原始域名(例如attacker.com)進行額外訪問。 這些都是由同源政策所容許的。 可是,當受害者的瀏覽器運行該腳本時,它會爲該域建立一個新的DNS請求,而且攻擊者會使用新的IP地址進行回覆。 例如,他們可使用內部IP地址或互聯網上某個目標的IP地址進行回覆。
via: 《DNS 從新綁定攻擊》
簡單來講就是利用 DNS 服務器來使得每次解析返回不一樣的 IP,當在校驗 IP 的時候 DNS 解析返回合法的值,等後續從新請求內容的時候 DNS 解析返回內網 IP。這種利用了屢次 DNS 解析的攻擊方式就是 DNS 從新綁定攻擊。
因爲 DNS 從新綁定攻擊是利用了屢次解析,因此咱們最好將校驗和抓取兩次 DNS 解析合併成一次,這裏咱們也有兩種方法來阻止:
針對以上解決方法,有開發者直接封裝了 ssrf-agent 模塊,使用的時候只要將其傳入便可實現一次解析,屢次判斷的功能,下面是簡單的使用示例:
const ssrfAgent = require('ssrf-agent');
const request = require('request-promise-native');
module.exports = class extends think.Controller {
async indexAction() {
const { url } = this.get();
try {
const ret = await request(url, {agent: ssrfAgent(url)});
this.ctx.body = ret;
} catch(e) {
return this.fail();
}
}
}
複製代碼
SSRF 能夠說是經久不衰的漏洞攻擊了,早些年百度、人人、360搜索等都有過相應的案例。通常如下場景會可能存在 SSRF 問題,咱們須要多加註意:
參考資料: