關於 Node.js 的認證方面的教程(極可能)是有誤的

我搜索了大量關於 Node.js/Express.js 認證的教程。全部這些都是不完整的,甚至以某種方式形成安全錯誤,可能會傷害新用戶。當其餘教程再也不幫助你時,你或許能夠看看這篇文章,這篇文章探討了如何避免一些常見的身份驗證陷阱。同時我也一直在 Node/Express 中尋找強大的、一體化的解決方案,來與 Rails 的 devise 競爭。php

更新 (8.7): 在他們的教程中,RisingStack 已經聲明,不要再以明文存儲密碼,在示例代碼和教程中選擇使用了 bcrypt。html

更新 (8.8): 編輯標題 關於 Node.js 的認證方面的教程(極可能)是有誤的,這篇文章已經對這些教程中的一些錯誤點進行了改正。前端

在業餘時間,我一直在挖掘各類 Node.js 教程,彷佛每一個 Node.js 開發人員都有一個博客用來發布本身的教程,講述如何以正確的方式作事,或者更準確地說,他們作事的方式。數以千計的前端開發人員被投入到服務器端的 JS 漩渦中,試圖經過拷貝式的操做或免費使用的 npm install 將這些教程中的可操做的知識拼湊在一塊兒,從而在外包經理或廣告代理商給出的期限內完成開發。node

Node.js 開發中一個更有問題的事情就是身份驗證的程序很大程度上是開發人員在摸索中完成開發的。事實上 Express.js 世界中的認證解決方案是 Passport,它提供了許多用於身份驗證的策略。若是你想要一個相似於 Plataformatec 的 devise 的 Ruby on Rails 的強大的解決方案,你可能會對 Auth0 感興趣,它是一個使認證成爲服務的開創項目。nginx

與 Devise 相比,Passport 只是身份驗證中間件,不會處理任何其餘身份驗證:這意味着 Node.js 開發人員可能會定製本身的 API 令牌機制、密碼重置令牌機制、用戶認證路由、端點、多種模板語言,所以,有不少教程專門爲你的 Express.js 應用程序設置 Passport,可是幾乎沒有徹底正確的教程,沒有一個正確地實現出 Web 應用程序所需的完整堆棧。git

請注意: 我不是故意針對這些教程的開發人員,而是使用他們的身份驗證所存在的漏洞後會讓本身的身份驗證系統產生安全問題。若是你是教程做者,請在更新教程後隨時與我聯繫。讓 Node/Express 成爲開發人員使用的更安全的生態系統。github

錯誤一:憑證存儲

讓咱們從憑證存儲開始。存儲和調用憑證對於身份管理來講是很是標準的,而傳統的方法是在你本身的數據庫或應用程序中進行存儲或者調用。憑證,做爲中間件,簡單地說就是「這個用戶能夠經過」或「這個用戶不能夠經過」,須要 passport-local 模塊來處理在你本身的數據庫密碼存儲,這個模塊也是由 Passport.js 做者寫的。web

在咱們進入這個教程的兔子洞以前,請記住 OWASP 的密碼存儲做弊表,它歸結爲「存儲具備獨特鹽和單向自適應成本函數的高熵密碼」。或者先看下 Coda Hale 的 bcrypt meme,即便有一些爭論數據庫

做爲一個新的 Express.js 和 Passport 用戶,我第一個要講的地方將是 passport-local 自己的示例代碼,十分感謝 passport 官方提供了一個能夠克隆和擴展的 Express.js 4.0 應用程序示例,從而我能夠克隆和擴展。可是,若是我只是拷貝這個例子,我講不了太多,由於沒有數據庫支持的例子,它假設我只是使用一些設置好的賬戶。express

不要緊,對吧?這只是一個內聯網應用程序,開發人員說,下週將分配給我另外四個項目。固然,該示例的密碼不會以任何方式散列,而且與本示例中的驗證邏輯一塊兒存儲在明文中。在這一點上,甚至沒有考慮到憑證存儲。

讓咱們來 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 來完成此操做。你必須另尋他法。

有一千種方法去搞砸這個問題。我見過的最多見人們從新設置密碼錯誤是:

  1. 可預見的令牌。 基於當前時間的令牌是一個很好的例子。不良僞隨機數發生器產生的令牌相對好些。
  2. 存儲不良。 在數據庫中存儲未加密的密碼重置令牌意味着若是數據庫遭到入侵,那些令牌就是明文密碼。使用加密安全的隨機數生成器生成長令牌會阻止對重置令牌的遠程強力攻擊,但不會阻止本地攻擊。重置令牌是憑據,應該這樣處理。
  3. 無令牌到期。 令牌若是沒有到期時間會給攻擊者更多的時間利用重置窗口。
  4. 無次要數據驗證。安全問題是重置的事實上的數據驗證。固然,開發商必須選擇一個好的安全問題安全問題有本身的問題。雖然這可能看起來像安全性過分,電子郵件地址是你擁有的,而不是你認識的內容,而且會將身份驗證因素混合在一塊兒。你的電子郵件地址成爲每一個賬戶的關鍵,只需將重置令牌發送到電子郵件。

若是你是第一次接觸這些內容,請嘗試 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 令牌)進行用戶驗證,。不幸的是,這教程實際上並不幫助咱們,由於它沒使用憑證,可是當咱們在這裏時,咱們會很快注意到憑據存儲中的錯誤:

  1. 咱們將 以明文形式將 JWT 密鑰存儲在存儲庫中
  2. 咱們將使用對稱密碼存儲密碼。這意味着我能夠得到加密密鑰,並在發生違規時解密全部密碼。加密密鑰與 JWT 祕密共享。
  3. 咱們將使用 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-limitexpress-limiter 以及 express-brute。我不能評價這些模塊的安全性,甚至沒有看過它們;不管你的負載平衡用的是什麼,一般我推薦在生產中運行逆向代理,並容許由 nginx 限制請求處理速率

身份驗證是困難的

我相信這些有錯誤的教程開發人員會辯解說,「這只是爲了解釋基礎!沒有人會在生產中這樣作的!」可是,我再三強調了這是多麼錯誤。當你的教程中的代碼被放在這裏時,人們就會參考並使用你的代碼,畢竟,你比他們有更多的專業知識。

若是你是初學者,請不要信任你的教程。 拷貝教程中的例子可能會讓你、你的公司和你的客戶在 Node.js 世界中遇到身份驗證問題。若是你真的須要強大的生產完善的一體化身份驗證庫,那麼可使用更好的手段,好比使用具備更好的穩定性,並且更加經驗證的 Rails/Devise。

Node.js 生態系統雖然容易接近,但對須要匆忙編寫部署於生產環境的 Web 應用程序的 JavaScript 開發人員來講,仍然有不少尖銳的未解決的點。若是你有前端的背景,不知道其餘的編程語言,我我的認爲,使用 Ruby 是一個不錯的選擇,畢竟站在巨人的肩膀上比從頭開始學習這些類型的東西要容易。

若是你是教程做者,請更新你的教程,特別是樣板代碼。這些代碼將可能被其餘人拷貝到生產環境中的 web 應用程序。

若是你是一個 Node.js 的鐵桿使用者,但願你在這篇文章中學到一些關於使用用憑證驗證身份的知識。你可能會遇到什麼問題。這篇文章中我尚未找到完美的方法來徹底避免以上錯誤。爲你的 Express 應用程序增長憑證驗證不該該是你的工做。應該有更好的辦法。

相關文章
相關標籤/搜索