JavaScript >> Javascript 文檔 >  >> Node.js

Express.js 安全提示

TL;DR

本文是我的新書 Pro Express.js:Master Express.js—Web 開發的 Node.js 框架 [Apress,2014 年] 的一部分。安全性很重要,這就是我決定在我的博客上發布這一章的原因。這本書很快就會出版。

本章中的一組技巧涉及 Express.js 應用程序中的安全性。安全性通常是一個被忽視的話題,被推遲到發布前的最後一分鐘。顯然,這種事後才考慮安全的方法很容易給攻擊者留下漏洞。更好的方法是從頭開始考慮和實施安全問題。

瀏覽器 JavaScript 因安全漏洞而聲名狼藉,因此我們需要盡可能保證 Node.js 應用程序的安全!通過本章介紹的簡單修改和中間件,您可以輕鬆解決一些基本的安全問題。

本章涵蓋以下主題:

  • 跨站請求偽造 (CSRF)
  • 進程權限
  • HTTP 安全標頭
  • 輸入驗證

跨站請求偽造

CSRF 和 csurf Pro Express.js 的第 4 章簡要介紹了中間件。 CSRF的定義和解釋請參考該章節。

csurf 中間件完成了匹配來自請求的傳入值的大部分工作。但是,我們仍然需要在響應中公開值並在模板(或 JavaScript XHR)中將它們傳遞回服務器。首先,我們安裝csurf 像任何其他依賴項一樣的模塊:

$ npm install [email protected]

然後,我們應用 csurf 使用 app.use(),如第 4 章所述:

app.use(csrf());

csrf 必須以 cookie-parser 開頭 和 express-session 因為它依賴於這些中間件(即安裝、導入和應用必要的模塊)。

實現驗證的方法之一是使用自定義中間件將 CSRF 令牌傳遞給使用 response.local 的所有模板 .這個自定義中間件必須在路由之前(就像大多數中間件語句的情況一樣):

app.use(function (request, response, next) {
  response.locals.csrftoken = request.csrfToken();
  next();
});

換句話說,我們手動促進令牌在正文(如本例所示)、查詢或標題中的存在。 (根據您的偏好或客戶端之間的約定,您可以使用查詢或標題。)
因此,要將模板中的值呈現為隱藏的表單值,我們可以使用

input(type="hidden", name="_csrf", value="#{csrftoken}")

這個隱藏的輸入字段會將令牌值添加到提交的表單數據中,方便將 CSRF 令牌發送到 /login 路由以及電子郵件和密碼等其他字段。

以下是文件 ch15/index.jade 中的完整 Jade 語言內容:

doctype html
html
  head
    title= title
    link(rel='stylesheet', href='https://m03s6dh33i0jtc3uzfml36au-wpengine.netdna-ssl.com/css/style.css')
  body
    if errors
      each error in errors
        p.error= error.msg
    form(method="post", action="/login")
      input(type="hidden", name="_csrf", value="#{csrftoken}")
      input(type="text", name="email", placeholder="[email protected]")
      input(type="password", name="password", placeholder="Password")
      button(type="submit") Login
    p
      include lorem-ipsum

要在 ch15/app.js 中查看 CSRF 的演示,請像通常使用 $ node app 一樣啟動服務器 .然後導航到位於 http://localhost:3000 的主頁。您應該會在表單的隱藏字段中看到令牌,如圖 15–1 所示。請記住,您的令牌值會有所不同,但其格式將是相同的。

圖 15-1。 csurf 中的 CSRF 令牌 插入到表單中的模塊稍後發送到/login 路線

對於主頁 (/) 的每個請求或頁面的刷新,您將獲得一個新令牌。但是如果你增加令牌來模擬攻擊(你可以直接在 Chrome 開發者工具中做),你會得到這個錯誤:

[旁注]

閱讀博客文章很好,但觀看視頻課程更好,因為它們更具吸引力。

許多開發人員抱怨 Node.js 上缺乏負擔得起的高質量視頻材料。觀看 YouTube 視頻會讓人分心,花 500 美元購買 Node 視頻課程很瘋狂!

去看看 Node University,它有關於 Node 的免費視頻課程:node.university。

[旁注結束]

403 Error: invalid csrf token
  at verifytoken...  

進程權限

顯然,以 root 身份運行 Web 服務通常是個壞主意。運維開發人員可以利用 Ubuntu 的 authbind 綁定特權端口(例如,HTTP 為 80,HTTPS 為 443),而無需授予 root 訪問權限。

或者,可以在綁定到端口後刪除權限。這裡的想法是我們將 GID(組 ID)和 UID(用戶 ID)的值傳遞給 Node.js 應用程序,並使用解析的值來設置進程的組標識和用戶標識。這不適用於 Windows,因此您可能需要使用 if/else 和 process.platformNODE_ENV 使您的代碼跨平台。這是通過使用 process.env.GID 和 process.evn.UID 的屬性設置 GID 和 UID 來刪除權限的示例 環境變量:

// ... Importing modules
var app = express();
// ... Configurations, middleware and routes 
http.createServer(app).listen(app.get('port'), function(){
    console.log("Express server listening on port "
    + app.get('port'));
    process.setgid(parseInt(process.env.GID, 10));
    process.setuid(parseInt(process.env.UID, 10));
});

HTTP 安全標頭

Express.js 中間件稱為 helmet (https://www.npmjs.org/package/helmet;GitHub:https://github.com/helmetjs/helmet)是一個與安全相關的中間件的集合,提供了 Recx 文章中描述的大部分安全標頭“七個免費提高 Web 應用程序安全性的 Web 服務器 HTTP 標頭。”

在撰寫本文時,helmet 版本為 0.4.1,包含以下中間件:

  • crossdomain :服務於 /crossdomain.xml 防止 Flash 加載某些不需要的內容(請參閱 www.adobe.com/devnet/articles/crossdomain_policy_file_spec.html)
  • csp :添加內容安全策略,允許將要加載的內容列入白名單
    (請參閱 content-security-policy.com 和 www.html5rocks.com/en/tutorials/security/content-security-policy)
  • hidePoweredBy :刪除 X-Powered-By 以防止洩露您正在使用 Node.js 和 Express.js
  • hsts :添加 HTTP 嚴格傳輸安全性以防止您的網站在 HTTP(而不是 HTTPS)上被查看
  • ienoopen :為 Internet Explorer 8+ 設置 X-Download-Options 標頭,以防止在 IE 瀏覽器中加載不受信任的 HTML(請參閱 blogs.msdn.com/b/ie/archive/2008/07/02/ie8-security-part- v-comprehensive-protection.aspx)
  • nocache :Cache-Control 和 Pragma 標頭停止緩存(有助於清除用戶瀏覽器中的舊錯誤)
  • nosniff :設置適當的 X-Content-Type-Options 標頭以緩解 MIME 類型嗅探(請參閱 msdn.microsoft.com/en-us/library/gg622941%28v=vs.85%29.aspx)
  • xframe :將 X-Frame-Options 標題設置為 DENY 以防止您的資源被
    放入框架中以進行點擊劫持攻擊(請參閱 en.wikipedia.org/wiki/Clickjacking)
  • xssFilter :為 IE8+ 和 Chrome 設置 X-XSS-Protection 標頭以防止 XSS 攻擊(請參閱 blogs.msdn.com/b/ieinternals/archive/2011/01/31/controlling-the-internet-explorer-xss-filter- with-the-x-xss-protection-http-header.aspx)

安裝 helmet ,只需運行:

$ npm install [email protected]

像往常一樣導入模塊:

var helmet = require('helmet');

然後在路由之前應用中間件。默認用法如下(ch15/app.js):

app.use(helmet());

圖 15–2 顯示了 helmet 使用默認選項時,v0.4.1 HTTP 響應將看起來:

圖 15-2。 helmet v0.4.1 與默認選項一起使用時的 HTTP 響應

輸入驗證

當您使用正文解析器或查詢作為輸入數據時,Express.js 不會執行任何用戶/客戶端輸入清理或驗證。而且,眾所周知,我們永遠不應該相信輸入。惡意代碼可以插入(XSS 或 SQL 注入)到您的系統中。例如,當您在頁面上打印該字符串時,您視為良性字符串的瀏覽器 JavaScript 代碼可能會變成攻擊(尤其是如果您的模板引擎沒有自動轉義特殊字符!)。

第一道防線是在接受外部數據的路由上使用正則表達式手動檢查數據。額外的“防禦”可以加在對象-關係映射層上,比如Mongoose Schema(見Pro Experss.js的第22章)。

請記住,執行前端/瀏覽器驗證僅出於可用性目的(即,它對用戶更友好)——它不會保護您的網站免受任何損害。

例如,在 ch15/app.js 中,我們可以實現在電子郵件字段上使用 RegExp 模式、if-else 語句和 test() 方法將錯誤消息附加到錯誤數組的驗證,如下所示:

app.post('/login-custom', function(request, response){
  var errors = [];
  var emailRegExp = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;

  if (!request.body.password) errors.push({msg: 'Password is required'});
  if (!request.body.email || !emailRegExp.test(request.body.email) ) errors.push({msg: 'A valid
email is required'});
  if (errors)
    response.render('index', {errors: errors});
  else
    response.render('login', {email: request.email});
});

隨著您添加更多路由和輸入字段進行驗證,您最終會得到更多 RegExp 模式和 if/else 語句。雖然這比沒有驗證更好,但推薦的方法是編寫自己的模塊或使用 express-validator .

安裝 express-validator v2.4.0,運行:

$ npm install [email protected]

導入express-validator 在 ch15/app.js 中:

var validator = require('express-validator');

然後應用 express-validatorbody-parser 之後 :

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.use(validator());

現在,在請求處理程序中,我們可以訪問 request.assert
request.validationErrors()

app.post('/login', function(request, response){
  request.assert('password', 'Password is required').notEmpty();
  request.assert('email', 'A valid email is required').notEmpty().isEmail();
  var errors = request.validationErrors();
  if (errors)
    response.render('index', {errors: errors});
  else
    response.render('login', {email: request.email});
});

index.jade 如果有任何錯誤,文件只是從數組中打印錯誤:

if errors
  each error in errors
    p.error= error.msg

login.jade 模板打印電子郵件。僅當驗證成功時才會呈現此模板。

 p= email

為了演示,請轉到主頁並嘗試輸入一些數據。如果有錯誤,您將看到帶有錯誤的主頁,如圖 15-3 所示。雙重“需要有效的電子郵件”消息來自這樣一個事實,即我們對電子郵件字段(notEmpty 和 isEmail)有兩個斷言,並且當電子郵件字段為空時它們都失敗了。

圖 15-3。使用 express-validator 的錯誤消息 斷言表單值

總結

安全是最重要的,但經常被忽視。在開發的早期階段尤其如此。典型的思考過程是這樣的:讓我們專注於交付更多功能,稍後我們將在即將發佈時處理安全問題。這個決定通常是善意的,但很少按計劃進行。因此,系統的安全性會受到影響。

使用 csurf 等中間件庫 , helmet , 和 express-validator ,我們可以在不增加太多開發週期的情況下獲得大量的基本安全性。

在下一章中,我們將換個思路,介紹一些將 Express.js 與 Socket.IO 庫一起用於反應式(即實時更新)視圖的方法……

如果您喜歡這篇文章,那麼您可能想探索 Pro Express.js 的其他摘錄:Master Express.js — 適用於您的 Web 開發的 Node.js 框架,例如:

  • LoopBack 101:類固醇上的 Express.js
  • Sails.js 101
  • 秘密 Express.js 設置

這本書本身很快就會被送去印刷。


下一篇
Tutorial JavaScript 教程
  1. 如何遷移到 react-query v1

  2. 將動畫深色主題切換添加到您的網站

  3. 函數的函數

  4. 使用 React 構建天氣應用

  5. 反應嚴格模式

  6. 在 React Native 中避免使用 Native

  7. 終於發表了我的個人作品集/博客

  1. 5 個 JavaScript 數組提示和技巧

  2. React Import CSS:如何在 React.js 組件中導入 CSS?

  3. 將 Prettier 與 Prisma.io 一起使用(預提交配置)

  4. 函數和新函數的區別

  5. 如何使用 node.js 在標準輸出中編寫阻塞?

  6. Hapi 服務器入門。

  7. 如何在頁面加載時使用 jQuery 設置 4.5 星?

  1. 使用 EmotionJs 和 Vite

  2. 了解服務器端渲染

  3. React Storybook 如何簡化組件測試

  4. 帶暗模式的簡單計算器