我搜尋了大量關于 Node.js/Express.js 認證的教程。所有這些都是不完整的,甚至以某種方式造成安全錯誤,可能會傷害新使用者。當其他教程不再幫助你時,你或許可以看看這篇文章,這篇文章探讨了如何避免一些常見的身份驗證陷阱。同時我也一直在 Node/Express 中尋找強大的、一體化的解決方案,來與 Rails 的
devise競争。
更新 (8.7): 在他們的教程中,RisingStack 已經聲明, 不要再以明文存儲密碼 ,在示例代碼和教程中選擇使用了 bcrypt。
更新 (8.8): 編輯标題 關于 Node.js 的認證方面的教程(很可能)是有誤的,這篇文章已經對這些教程中的一些錯誤點進行了改正。
在業餘時間,我一直在挖掘各種 Node.js 教程,似乎每個 Node.js 開發人員都有一個部落格用來釋出自己的教程,講述如何以正确的方式做事,或者更準确地說,他們做事的方式。數以千計的前端開發人員被投入到伺服器端的 JS 漩渦中,試圖通過拷貝式的操作或無償使用的 npm install 将這些教程中的可操作的知識拼湊在一起,進而在外包經理或廣告代理商給出的期限内完成開發。
Node.js 開發中一個更有問題的事情就是身份驗證的程式很大程度上是開發人員在摸索中完成開發的。事實上 Express.js 世界中的認證解決方案是
Passport,它提供了許多用于身份驗證的政策。如果你想要一個類似于
Plataformatec 的 devise的 Ruby on Rails 的強大的解決方案,你可能會對
Auth0感興趣,它是一個使認證成為服務的開創項目。
與 Devise 相比,Passport 隻是身份驗證中間件,不會處理任何其他身份驗證:這意味着 Node.js 開發人員可能會定制自己的 API 令牌機制、密碼重置令牌機制、使用者認證路由、端點、多種模闆語言,是以,有很多教程專門為你的 Express.js 應用程式設定 Passport,但是幾乎沒有完全正确的教程,沒有一個正确地實作出 Web 應用程式所需的完整堆棧。
請注意: 我不是故意針對這些教程的開發人員,而是使用他們的身份驗證所存在的漏洞後會讓自己的身份驗證系統産生安全問題。如果你是教程作者,請在更新教程後随時與我聯系。讓 Node/Express 成為開發人員使用的更安全的生态系統。
錯誤一:憑證存儲
讓我們從憑證存儲開始。存儲和調用憑證對于身份管理來說是非常标準的,而傳統的方法是在你自己的資料庫或應用程式中進行存儲或者調用。憑證,作為中間件,簡單地說就是“這個使用者可以通過”或“這個使用者不可以通過”,需要
passport-local子產品來處理在你自己的資料庫密碼存儲,這個子產品也是由 Passport.js 作者寫的。
在我們進入這個教程的兔子洞之前,請記住 OWASP 的
密碼存儲作弊表,它歸結為“存儲具有獨特鹽和單向自适應成本函數的高熵密碼”。或者先看下 Coda Hale 的
bcrypt meme,即使
有一些争論。
作為一個新的 Express.js 和 Passport 使用者,我第一個要講的地方将是 passport-local 本身的示例代碼,
十分感謝 passport 官方提供了一個可以克隆和擴充的 Express.js 4.0 應用程式示例,進而我可以克隆和擴充。但是,如果我隻是拷貝這個例子,我講不了太多,因為沒有資料庫支援的例子,它假設我隻是使用一些設定好的帳戶。
沒關系,對吧?這隻是一個内聯網應用程式,開發人員說,下周将配置設定給我另外四個項目。當然,該示例的密碼不會以任何方式散列,
并且與本示例中的驗證邏輯一起存儲在明文中。在這一點上,甚至沒有考慮到憑證存儲。
讓我們來 google 另一個使用 passport-local 的教程。我發現
這個來自 RisingStack 的一個叫“Node Hero”系列的快速教程,但從這個教程中我沒找到很有用的幫助。他們也
在 GitHub 上提供了一個示例應用程式,
但
它與官方的問題相同。(Ed。8/7/17:RisingStack
現在使用 bcrypt在他們的教程應用。)
接下來,
這是第四個結果,來自寫于 2015 年的 Google 産出的 express js passport-local 教程。它使用 Mongoose ODM,實際上從我的資料庫讀取憑據。 這一個教程算是比較完整的,包括內建測試,是的,你可以使用另一個樣闆。但是,Mongoose ODM
也存儲類型為 String 的密碼,是以這些密碼也存儲在明文中,隻是這一次在 MongoDB 執行個體上。(
人人都知道 MongoDB 執行個體通常是非常安全的)
你可以指責我擇優挑選教程,如果擇優挑選意味着從 Google 搜尋結果的第一頁進行選擇,那麼你會是對的。讓我們選擇
TutsPlus 上更高排名的 passport-local 教程。這一個更好,因為
它使用 brypt 的因子為 10 的密碼哈希,并使用 process.nextTick 延遲同步 bcrypt 哈希檢查。Google 的最高成績
來自 scotch.io 的教程,也使用
成本因子較低為 8 的 bcrypt。這兩個值都很小,但是 8 真的很小。大多數 bcrypt 庫現在使用 12。
選擇 8 作為成本因子是因為管理者帳戶是十八年前的,這個因子數在那時候就能滿足需求了。
除了密碼存儲之外,這些教程都不會實作密碼重置功能,這将作為開發人員的一個挑戰,并且它附帶着自己的陷阱。
錯誤二:密碼重置
密碼存儲的一個姐妹安全問題是密碼重置,并且沒有一個頂級的基礎教程解釋了如何使用 Passport 來完成此操作。你必須另尋他法。
有一千種方法去搞砸這個問題。我見過的最常見人們重新設定密碼錯誤是:
- 可預見的令牌。 基于目前時間的令牌是一個很好的例子。不良僞随機數發生器産生的令牌相對好些。
- 存儲不良。 在資料庫中存儲未加密的密碼重置令牌意味着如果資料庫遭到入侵,那些令牌就是明文密碼。使用加密安全的随機數生成器生成長令牌會阻止對重置令牌的遠端強力攻擊,但不會阻止本地攻擊。重置令牌是憑據,應該這樣處理。
- 無令牌到期。 令牌如果沒有到期時間會給攻擊者更多的時間利用重置視窗。
- 無次要資料驗證。安全問題是重置的事實上的資料驗證。當然,開發商必須選擇一個好的安全問題。 安全問題有自己的問題 。雖然這可能看起來像安全性過度,電子郵件位址是你擁有的,而不是你認識的内容,并且會将身份驗證因素混合在一起。你的電子郵件位址成為每個帳戶的關鍵,隻需将重置令牌發送到電子郵件。
如果你是第一次接觸這些内容,請嘗試 OWASP 的
密碼重置工作表。讓我們回到 Node 中看看它為此提供給我們的東西。
我們将轉移到 npm 一秒鐘,并
重新查找密碼重置,看看是否已有人做到這一點。有一個已有五年曆史的 package(通常意味着它很棒)。在 Node.js 的時間軸上,這個子產品就像是侏羅紀時代的,如果我想要雞蛋裡挑骨頭,
Math.random() 可以在 V8 中預測,是以
它不應該用于令牌生成碼。此外,它不使用 Passport,是以我們繼續前進。
Stack Overflow 上擷取不了太多的幫助,因為一個名叫 Stormpath 的公司的開發人員喜歡在可以想象到的每一個跟這個相關的的文章上都插入他們的 IaaS 啟動教程。
他們的文檔也随處可見,他們也有
關于密碼重置的部落格廣告。但是,所有這一切都随着 Stormpath 的停業已經停止了,它們公司于 2017 年 8 月 17 日
完全關閉好的,回到谷歌,這裡似乎存在唯一的教程。我們找到了 Google 搜尋 express passport 密碼重置的
第一個結果。還是我們的老朋友 bcrypt。文章中使用了更小的成本因子 5,這遠遠低于了現代使用的成本因素。
但是,與其他教程相比,這篇教程相當實用,因為它使用 crypto.randomBytes 來生成真正的随機标記,如果不使用它們,則會過期。然而,上述實踐中的 #2 和 #4 與這個全面的教程不符,是以密碼令牌本身容易受到認證錯誤,憑據存儲的影響。
幸運的是,由于重置到期,這是有限的使用。但是,如果攻擊者通過 BSON 注入對資料庫中的使用者對象進行讀取通路,或由于配置錯誤,可以自由通路 Mongo,這些令牌将非常危險了。攻擊者隻需為每個使用者發出密碼重置,從 DB 讀取未加密的令牌,并為使用者帳戶設定自己的密碼,而不必經曆使用 GPU 裝備對 bcrypt 散列進行的昂貴的字典攻擊過程。
錯誤三:API 令牌
API 令牌是憑據。它們與密碼或重置令牌一樣敏感。大多數開發人員都知道這一點,并嘗試将他們的 AWS 密鑰、Twitter 秘密等保留在他們胸前,但是這似乎并沒有轉移到被編寫的代碼中。
讓我們使用
JSON Web 令牌擷取 API 憑據。擁有一個無狀态的、可添加黑名單的、可自定義的令牌比十年來使用的舊 API 密鑰/私密模式更好。也許我們的初級 Node.js 開發人員曾經聽說過 JWT,或者看到過 passport-jwt,并決定實施 JWT 政策。無論如何,接觸 JWT 的人都會或多或少地受到 Node.js 的影響。(尊敬的
Thomas Ptacek 會認為 JWT 不好,但恐怕船已經在這裡航行。)
我們在 Google 上搜尋 express js jwt,然後找到
Soni Pandey的教程
使用 Node.js 中的 JWT(JSON Web 令牌)進行使用者驗證,。不幸的是,這教程實際上并不幫助我們,因為它沒使用憑證,但是當我們在這裡時,我們會很快注意到憑據存儲中的錯誤:
- 我們将 以明文形式将 JWT 密鑰存儲在存儲庫中
- 使用對稱密碼存儲密碼 。這意味着我可以獲得加密密鑰,并在發生違規時解密所有密碼。加密密鑰與 JWT 秘密共享。
- 我們将使用 AES-256-CTR 進行密碼存儲。我們不應該使用 AES 來啟動,而且這種操作模式沒有什麼幫助。我不知道為什麼選擇這個特别的模式,但是 單一的選擇讓密文具有延展性
讓我們回到 Google,接着尋找下一個教程。Scotch,在 passport-local 教程中做了一個密碼存儲的工作,比如
隻是忽略他們以前告訴你的東西,并将密碼存儲在明文中好吧,我們會給出一個簡短的憑證教程,但這并不能幫助隻是拷貝的開發者。因為更有趣的是,這個教程
将這個 mongoose User 對象序列化到 JWT 中讓我們克隆 Scotch 的這個資源庫,按照說明進行運作。可以無視一些來自 Mongoose 的警告,我們可以輸入
http://localhost:8080/setup來建立使用者,然後通過使用 “Nick Cerminara” 和 “password” 的預設憑證調用 /api/authenticate 拿到令牌。這個令牌傳回并顯示在了 Postman 上。

從 Scotch 教程傳回的 JWT 令牌。
請注意,JSON Web 令牌已簽名但未加密。這意味着兩個時期之間的大斑點是一個 Base64 編碼對象。快速解碼後,我們得到一些有趣的東西。
我喜歡在明文的密碼中使用令牌。
現在,任何一個包括存儲在 Mongoose 模型甚至過期的令牌都有你的密碼。鑒于這個來自HTTP,我可以把它從線上找出來。
下一個教程怎麼樣呢?下一個教程,
針對初學者的 Express、Passport 和 JSON Web 令牌(jwt),包含相同的資訊洩露漏洞。下篇教程來自
SlatePeak 的一篇做了同樣的序列化文章。在這一點上,我放棄了閱讀。
錯誤四:限速
如上所述,我沒有在任何這些身份驗證教程中找到關于速率限制或帳戶鎖定的問題。
沒有速率限制,攻擊者可以執行線上字典攻擊,比如運作
Burp Intruder等工具,去獲得擷取通路密碼較弱的帳戶。帳戶鎖定還可以通過在下次登入時要求使用者填寫擴充登入資訊來幫助解決此問題。
請記住,速率限制還有助于可用性。跨平台檔案加密工具是一個 CPU 密集型功能,沒有速率限制功能,使用跨平台檔案加密工具會讓應用程式拒絕服務,特别是在 CPU 高數運作時。比如使用者注冊或檢查登入密碼的多個請求盡管是輕量級的 HTTP 的請求,但是會花費伺服器大量的昂貴時間。
雖然我沒有教程可以證明這點,但 Express 有很多速率限制的技術,例如
express-rate-limit express-limiter以及
express-brute。我不能評價這些子產品的安全性,甚至沒有看過它們;無論你的負載平衡用的是什麼,通常我
推薦在生産中運作逆向代理,并允許
由 nginx 限制請求處理速率身份驗證是困難的
我相信這些有錯誤的教程開發人員會辯解說,“這隻是為了解釋基礎!沒有人會在生産中這樣做的!”但是,我再三強調了這是多麼錯誤。當你的教程中的代碼被放在這裡時,人們就會參考并使用你的代碼,畢竟,你比他們有更多的專業知識。
如果你是初學者,請不要信任你的教程。 拷貝教程中的例子可能會讓你、你的公司和你的客戶在 Node.js 世界中遇到身份驗證問題。如果你真的需要強大的生産完善的一體化身份驗證庫,那麼可以使用更好的手段,比如使用具有更好的穩定性,而且更加經驗證的 Rails/Devise。
Node.js 生态系統雖然容易接近,但對需要匆忙編寫部署于生産環境的 Web 應用程式的 JavaScript 開發人員來說,仍然有很多尖銳的未解決的點。如果你有前端的背景,不知道其他的程式設計語言,我個人認為,使用 Ruby 是一個不錯的選擇,畢竟站在巨人的肩膀上比從頭開始學習這些類型的東西要容易。
如果你是教程作者,請更新你的教程,特别是樣闆代碼。這些代碼将可能被其他人拷貝到生産環境中的 web 應用程式。
如果你是一個 Node.js 的鐵杆使用者,希望你在這篇文章中學到一些關于使用用憑證驗證身份的知識。你可能會遇到什麼問題。這篇文章中我還沒有找到完美的方法來完全避免以上錯誤。為你的 Express 應用程式增加憑證驗證不應該是你的工作。應該有更好的辦法。
作者:
牧雲雲出處:
http://www.cnblogs.com/MuYunyun/"本文版權歸作者和部落格園所有,歡迎轉載,轉載請标明出處。
如果您覺得本篇博文對您有所收獲,請點選右下角的 [推薦],謝謝!