導語:html
早年在瀏覽器大戰期間,有遠見的Chrome認爲要運行現代Web應用,瀏覽器必須有一個性能很是強勁的Java引擎,因而Google本身開發了一個高性能的開源的Java引擎,名字叫V8。在2009年,Ryan正式推出了基於Java語言和V8引擎的開源Web服務器項目,命名爲Node.js。node
對於任何web開發人員來講,不安全或未經驗證的重定向都是重要的安全考慮因素。Express爲重定向提供了本地支持,使它們易於實現和使用。Express是一種保持最低程度規模的靈活Node.js Web應用程序框架,爲Web和移動應用程序提供一組強大的web
什麼是不安全的重定向?express
對於任何web開發人員來講,不安全或未經驗證的重定向都是重要的安全考慮因素。Express爲重定向提供了本地支持,使它們易於實現和使用。然而,Express將執行輸入驗證的工做留給了開發人員。Express是一種保持最低程度規模的靈活Node.js Web應用程序框架,爲Web和移動應用程序提供一組強大的功能。瀏覽器
下面是OWASP.org網站給出的「未經驗證的重定向和轉發」的定義:安全
若是web應用程序接受不可信的輸入,可能致使web應用程序將請求重定向到不可信輸入中包含的URL,則能夠進行未經驗證的重定向和轉發。服務器
重定向一般在登陸和身份驗證過程當中使用,所以能夠在登陸以前將用戶重定向到他們所在的頁面。但根據業務需求或應用程序類型而有所不一樣,也存在其餘重定向狀況。網絡
爲何要避免重定向?session
不驗證用戶輸入的重定向,可使攻擊者具有發起網絡釣魚詐騙的條件,從而竊取用戶憑據並執行其餘惡意操做。app
注意:當在Node.js或Express中實現重定向時,在服務器端執行輸入驗證很重要。
若是攻擊者發現用戶沒有驗證外部用戶提供的輸入,他們可能會利用這個漏洞在論壇、社交媒體和其餘公共場所發佈專門設計的連接,讓用戶點擊它。
從表面上看,這些URL看起來合法且對用戶來講並沒有威脅,這是由於全部這些要重定向的URL都包含目標的主機名:
https://example.com/login?url=http://examp1e.com/bad/things
可是,若是服務器端重定向邏輯未驗證輸入url參數的數據,則用戶可能最終會訪問黑客所提早設置的網站(examp1e.com),知足攻擊的需求!以上只是攻擊者如何利用不安全重定向邏輯的一個例子。
不安全重定向例子並將其直接傳遞到Express res.redirect()方法中。所以,只要用戶經過身份驗證,Express就會將用戶重定向到輸入或提供的URL。
var express = require('express'); var port = process.env.PORT || 3000; var app = express(); app.get('/login', function (req, res, next) { if(req.session.isAuthenticated()) { res.redirect(req.query.url); } }); app.get('/account', function (req, res, next) { res.send('Account page'); }); app.get('/profile', function (req, res, next) { res.send('Profile page'); }); app.listen(port, function() { console.log('Server listening on port ' + port); });
輸入驗證有助於防止不安全的重定向
一般,最好避免在代碼中使用重定向和轉發。若是你必定須要在代碼中使用重定向,則首選的方法是使用映射到特定目標的預約義輸入,這被稱爲白名單方法。如下就是實現這種方法的一個具體樣本步驟:
1.baseHostname會確保任何重定向都將用戶保留在研究人員的主機上;
2.redirectMapping是一個對象,它將預約義的輸入(例如,傳遞給url paramer的內容)映射到服務器上的特定路徑;
3.validateRedirect()方法會判斷預約義的輸入是否存在,若是它們存在,則返回要重定向的適當路徑;
4.研究人員修改了/login邏輯,而後將baseHostname+redirectPath變量鏈接在一塊兒,這就避免了任何用戶提供的輸入內容直接傳遞到Express res.redirect()方法中;
5.最後,研究人員使用encodeURI()方法做爲額外的安全保證,確保鏈接字符串的URI部分被正確編碼,以容許乾淨的重定向。
//Configure your whitelist var baseHostname = "https://example.com"; var redirectMapping = { 'account': '/account', 'profile': '/profile' } //Create a function to validate whitelist function validateRedirect(key) { if(key in redirectMapping) { return redirectMapping[key]; }else{ return false; } } app.get('/login', function (req, res, next) { if(req.session.isAuthenticated()) { redirectPath = validateRedirect(req.query.url); if(redirectPath) { res.redirect(encodeURI(baseHostname + redirectPath)); }else{ res.send('Not a valid redirect!'); } } });
其餘重定向場景
在某些狀況下,將每一個組合列入白名單是不切實際的,不過有些安全平臺仍然但願重定向用戶並將其保留在域內某些邊界內。當外部提供的值遵循特定模式(例如16個字符的字母數字字符串)時,最好這樣作。字母數字字符串是理想的,由於它們不包含任何可能引入其餘攻擊的特殊字符,例如目錄/路徑遍歷(依賴於諸如…和向後/向前斜槓之類的字符)。
例如,安全平臺可能但願在用戶登陸後將其重定向回電子商務網站上的特定產品。因爲電子商務網站對每種產品都有惟一的字母數字值,所以安全平臺能夠經過始終根據RegEx白名單驗證外部輸入來實現安全重定向。在本文所講的樣本在,研究者用的是productId變量。
//Configure your whitelist var baseHostname = "https://example.com"; app.get('/login', function (req, res, next) { productId = (req.query.productId || ''); whitelistRegEx = /^[a-zA-Z0-9]{16}$/; if(productId) { //Validate the productId is alphanumeric and exactly 16 characters if(whitelistRegEx.test(productId)) { res.redirect(encodeURI(baseHostname + '/item/' + productId)); }else{ //The productId did not meet the RegEx whitelist, so return an error res.send('Invalid product ID'); } }else{ //No productId was provided, so redirect to home page res.redirect('/'); } });
最後,安全平臺發出警告,警告用戶他們正在被自動重定向是值得重視的。若是安全平臺有意將用戶重定向到域外,則可能須要在流程中建立一箇中間頁面,該頁面會發出以下警告,幷包含用戶要重定向到的URL。
注:本文是以Hailstone爲例進行講解的,Hailstone是一個應用程序安全平臺,它有查找代碼中的漏洞功能。
本文翻譯自:https://blog.hailstone.io/how-to-prevent-unsafe-redirects-in-node-js