--如下內容來自《深刻淺出node.js》javascript
Node提供了相對底層的API,經過它構建各類各樣的Web應用都是相對容易的,但在Web應用中,不得不重視數據上傳相關的安全問題。因爲Node與前端Javascript的近緣性,前端Javascript甚至能夠上傳至服務器直接執行,但在這裏咱們並不討論這樣危險的動做,而是介紹內存和CSRF相關的安全問題。前端
1. 內存限制java
在解析用戶提交的表單、JSON和XML的時候,咱們採起的策略是先保存全部數據,而後再解析處理,最後才傳遞給業務邏輯。這種策略存在潛在的問題是,它僅僅適合數據量小的提交請求, 一旦數據量過大,將發生內存被佔光的狀況。攻擊者經過客戶端可以十分容易地模擬僞造大量數 據,若是攻擊者每次提交1 MB的內容,那麼只要併發請求數量一大,內存就會很快地被吃光。node
要解決這個問題主要有兩個方案。數據庫
限制上傳內容的大小,一旦超過限制,中止接收數據,並響應400狀態碼。vim
經過流式解析,將數據流導向到磁盤中,Node只保留文件路徑等小數據。瀏覽器
首先介紹一下Connect框架中採用的上傳數據量的限制方式,以下所示: 安全
const bytes = 1024; (req, res) => { let received = 0, const len = req.headers['content-length'] ? parseInt(req.headers['content-length'], 10) : null; // 若是內容超過長度限制,返回請求實體過長的狀態碼 if (len && len > bytes) { res.writeHead(413); res.end(); return; } // limit req.on('data', function (chunk) { received += chunk.length if (received > bytes) { // 中止接收數據,觸發end() req.destroy(); } }) handle(req, res) }
從上面的代碼中咱們能夠看到,數據是由包含Content-Length的請求報文判斷是否長度超過限制的,超過則直接響應413狀態碼。對於沒有Content-Length的請求報文,則更爲簡略些,在每一個data事件中判斷便可。一旦超過限制值,服務器中止接收新的數據片斷。若是是JSON文件或 XML文件,極有可能沒法完成解析。對於上線的Web應用,添加一個上傳大小限制十分有利於保 護服務器,在遭遇攻擊時,能鎮定從容應對。服務器
2. CSRFsession
CSRF的全稱是Cross-Site Request Forgery,中文意思爲跨站請求僞造。一般而言,服務器端與客戶端經過Cookie來標識和認證用戶,用戶經過瀏覽器訪問服務器端的Session ID 是沒法被第三方知道的,可是CSRF的攻擊者並不須要知道Session ID就能讓用戶中招。
爲了詳細解釋CSRF攻擊是怎樣一個過程,這裏以一個留言的例子來講明。假設某個網站有 這樣一個留言程序,提交留言的接口以下所示:
http://domain_a.com/guestbook
用戶經過POST提交content字段就能成功留言。服務器端會自動從Session數據中判斷是誰提 交的數據,補足username和updatedAt兩個字段後向數據庫中寫入數據,以下所示:
(req, res) => { // req.body.content來自connect框架 const content = req.body.content || ''; // session需自行實現,此處假設上文已實現session const username = req.session.username; const feedback = { username: username, content: content, updatedAt: Date.now() }; // 此處根據本身使用的數據庫類型自行修改 db.save(feedback, err => { res.writeHead(200); res.end('Ok'); }); }
正常的狀況下,誰提交的留言,就會在列表中顯示誰的信息。若是某個攻擊者發現了這裏的 接口存在CSRF漏洞,那麼他就能夠在另外一個網站(http://domain_b.com/attack)上構造了一個表 單提交,以下所示:
<form id="test" method="POST" action="http://domain_a.com/guestbook"> <input type="hidden" name="content" value="vim是這個世界上最好的編輯器" /> </form> <script type="text/javascript"> $(function () { $("#test").submit(); }); </script>
這種狀況下,攻擊者只要引誘某個domain_a的登陸用戶訪問這個domain_b的網站,就會自動提交一個留言。因爲在提交到domain_a的過程當中,瀏覽器會將domain_a的Cookie發送到服務器, 儘管這個請求是來自domain_b的,可是服務器並不知情,用戶也不知情。
以上過程就是一個CSRF攻擊的過程。這裏的示例僅僅是一個留言的漏洞,若是出現漏洞的 是轉帳的接口,那麼其危害程度可想而知。
儘管經過Node接收數據提交十分容易,可是安全問題仍是不容忽視。好在CSRF並不是不可防護,解決CSRF攻擊的方案有添加隨機值的方式,以下所示:
const generateRandom = function (len) { return crypto.randomBytes(Math.ceil(len * 3 / 4)) .toString('base64') .slice(0, len) }
也就是說,爲每一個請求的用戶,在Session中賦予一個隨機值,以下所示:
const token = req.session._csrf || (req.session._csrf = generateRandom(24));
在作頁面渲染的過程當中,將這個_csrf值告以前端,以下所示:
<form id="test" method="POST" action="http://domain_a.com/guestbook"> <input type="hidden" name="content" value="vim是這個世界上最好的編輯器" /> <input type="hidden" name="_csrf" value="<%=_csrf%>" /> </form>
因爲該值是一個隨機值,攻擊者構造出相同的隨機值的難度至關大,因此咱們只須要在接收端作一次校驗就能輕易地識別出該請求是否爲僞造的,以下所示:
(req, res) => { const token = req.session._csrf || (req.session._csrf = generateRandom(24)); const _csrf = req.body._csrf; if (token !== _csrf) { res.writeHead(403); res.end("禁止訪問"); } else { handle(req, res); } }
_csrf字段也能夠存在於查詢字符串或者請求頭中。