Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature Request] Support more than 8 characters password #112

Open
LImoritakeU opened this issue Apr 25, 2022 · 10 comments
Open

[Feature Request] Support more than 8 characters password #112

LImoritakeU opened this issue Apr 25, 2022 · 10 comments

Comments

@LImoritakeU
Copy link

LImoritakeU commented Apr 25, 2022

問題描述

目前 ptt 只支援最高 8 個字元的密碼,希望能將上限提升至 32 或 64 字元以提升帳號安全性

#define PASS_INPUT_LEN 8 /* Length of valid input password length.

我沒有寫過 C ,但就 code-level 並沒有特別對 8 個字元限制特別說明,我只能猜想或許限制是在儲存層 ?

@wens
Copy link
Contributor

wens commented Apr 25, 2022

DES crypt...

@IepIweidieng
Copy link
Contributor

IepIweidieng commented Apr 25, 2022

第一限制:fcrypt() 的實作(採用 DES hashing)

在常見的 DES hashing 實作中,密碼明碼僅有前 8 字元的 7-bit 部份有效。

在 PttBBS 中的實作,請見 https://github.com/ptt/pttbbs/blob/master/common/sys/crypt.c

由於 glibc 的 crypt 函式庫爲了最佳化的執行速度而使用的全域加速 table 佔用了太多記憶體空間(下述),PttBBS,恰如同一些 FireBird BBS 分支,改採用其它空間需求較少的函式庫來提供 DES hashing 之功能。

  • FireBird BBS 分支中的範例:https://github.com/bmybbs/bmybbs/blob/master/libytht/crypt.c
  • 在單個使用者的連線過程中,使用次數寥寥無幾的全域加速 table:100KB per 處理程序 ≈ 1GB per 10000 使用者 (據 crypt.c,以線性插值推論)
  • 註:在保留了 MapleBBS 系列甚至其前身的較舊特徵的一些專案中,BBS 伺服器程式會使用 -lcrypt 對 libcrypt 進行 靜態 連結,故每一處理程序中皆存有一個加速 table。
    • 雖然 Linux 的 fork() 採用了 copy-on-write 機制,但由於此加速 table 在 hashing 運算中會被更動(據 crypt.c),仍會觸發複製,浪費無法避免。
  • 註:某些 FireBird BBS 分支在採用此方式前,曾經使用暫時 fork() 出額外的處理程序,並在其中呼叫 crypt() 的方式,來達到釋放加速 table 所佔用的記憶體空間的效果。 (暫時忘了資訊來源 (楓橋驛站《SysSuggest》某傳信文,或?);僅依稀記得原文有「不用再 fork……」什麼的字句)

後來的 glibc 的 crypt() 尚增加了如 MD5、SHA-256、等等較現代化的 hashing 方式,但礙於記憶體空間,此函式無法直接於 PttBBS 使用。
可見 https://man7.org/linux/man-pages/man3/crypt.3.html

※ DreamBBS 直接使用了 glibc 的 crypt() ,故有記憶體使用量上的疑慮。

可能的解決方法擧隅

  • 在專案中新增支援 SHA-256 hashing 的函式庫
  • 若要改用 glibc 的 crypt(),或可嘗試待呼叫時再以動態載入的方式載入 libcrypt 函式庫,呼叫完畢後即解除載入此函式庫,以避免記憶體空間之浪費。

第二限制:userec_t::passwd[] 的大小(= PASSLEN = 14)

如果要用 32 至 64 個 bytes 長的可輸入字串 (假設爲 Big5 編碼) 作爲密碼的話,理論上至少需要 ⌈32 ⋅ log₂(95 + 128) ÷ 8⌉ = 32 至 ⌈64 ⋅ log₂(95 + 128) ÷ 8⌉ = 63 個 bytes 大的空間。

又,因需儲存 hash 資訊,實際需要的空間更大。

※ DreamBBS 儲存 SHA-256-hashed 密碼時,將 hash 資訊與 hashed 密碼分開儲存,其中 hash 資訊沿用欄位 passwd[],而 hashed 密碼的空間 (passhash[]) 則是將已不再使用的大資料欄位變更用途而來的。
- 但 PttBBS 的 userec_t 應無此類不使用的大欄位,故不適用此修改方式。

可能的解決方法擧隅

  • 將使用者密碼資訊以另外的資料結構儲存。
    • 注意在 MapleBBS 3.xx 系統中,使用者資訊是分檔儲存於各使用者目錄下的。使用者索引檔僅儲存使用者 ID 與其編號。若將使用者密碼資訊分開儲存,則將趨於 MapleBBS 3.xx 系統的設計。或可借鏡。

第三限制:使用者(?)

只增加支援 > 8 bytes 8-bit 密碼明碼還不足夠,還需使用者主動修改密碼。

由於依 hashed 密碼直接回推原密碼明碼,再以新方式 hash,需要一定的計算資源(下述),又因爲 DES hashing 只看密碼明碼 7-bit 的部份,如果使用者的密碼明碼包含特殊字元,會回推錯誤,造成使用者以同樣的密碼明碼無法登入之窘況。

  • 即使是「脆弱的」 DES hash,暴力破解某一密碼也需最多 128⁸ = 7.2057594×10¹⁶ 次嘗試。若電腦一秒能嘗試 10⁹ 次,最多需約 834.00 天的時間才可破解 單一 使用者的密碼。
    • 保守估計,若使用者平均使用 6 碼英數與空白字元作爲密碼,則平均須嘗試 63⁶ = 6.2523502209×10¹⁰ 次(),而在同樣的運算速度下,平均需 62.5 秒的時間。何況批踢踢可能有超過 100 萬位已註冊使用者,需約 723.38 天才可轉換完成。
    • (註:包含短於 6 碼的情況的話,是 63¹ + 63² + … + 63⁶ 次,但此處不計以求簡潔)
    • 若嘗試使用多機器進行平行運算,則有資訊安全問題之虞。
    • 即使不論以上各點,對使用者密碼的破解本身就有相當大的侵犯隱私之疑慮。

因此站方尚需加以宣導,鼓勵使用者利用新密碼 hash 的方式,方可發揮 > 8 bytes 8-bit 密碼的優點。

@kcwu
Copy link
Contributor

kcwu commented Apr 25, 2022

  1. ptt 的 crypt 函式是我換的, 該函式的確只支援 DES crypt, 不過我想另外找一個 library 來用應該不是太大的問題. 當然 @IepIweidieng 建議的解法也可以嘗試.
  2. passwd[] 大小的確不夠, 因此需要擴增欄位, 關站轉換檔案格式. 不過因為只需要存密碼的 salt 跟 hash, 空間不一定需要用到 @IepIweidieng 說的那麼大.
    p.s. 如果要做, 記得 "取備份" 這功能要特別處理, 以免拿舊欄位蓋掉新的資料, 可能會造成資料錯亂的問題.
  3. 基本上不會採用逆推原密碼的方式處理這個問題. 比較簡單的做法是, 同時支援新舊方式, 當使用者上站時, 密碼驗證通過之後當場轉成新的 hash funciton 寫回檔案. 由於帳號太久沒用會被砍掉, 所以理論上幾個月之後全站就能轉換完成.

@robertabcd
Copy link
Contributor

  1. 基本上不會採用逆推原密碼的方式處理這個問題. 比較簡單的做法是, 同時支援新舊方式, 當使用者上站時, 密碼驗證通過之後當場轉成新的 hash funciton 寫回檔案. 由於帳號太久沒用會被砍掉, 所以理論上幾個月之後全站就能轉換完成.

直接在背後轉換會有問題,因為 crypt 現在只吃 8 碼,但登入可以打超過 8 碼(logind 吃 14 碼),在背後轉無法確認超過的部分是不是使用者想要的,如果在轉換的那次不小心 8 位以後輸入錯誤,之後會無法登入。

@kcwu
Copy link
Contributor

kcwu commented Apr 25, 2022

直接在背後轉換會有問題,因為 crypt 現在只吃 8 碼,但登入可以打超過 8 碼(logind 吃 14 碼),在背後轉無法確認超過的部分是不是使用者想要的,如果在轉換的那次不小心 8 位以後輸入錯誤,之後會無法登入。

good point. 所以這時要讓使用者再輸入一次確認? 不過這樣大概會讓很多做自動 login 的 app 壞掉 XD

@robertabcd
Copy link
Contributor

所以這時要讓使用者再輸入一次確認?

我覺得可以有個宣導期,過了之後登入強迫改密碼。

@IepIweidieng
Copy link
Contributor

  1. passwd[] 大小的確不夠, 因此需要擴增欄位, 關站轉換檔案格式. 不過因為只需要存密碼的 salt 跟 hash, 空間不一定需要用到 @IepIweidieng 說的那麼大.

吾忘 hash 會撞,因此 32–63 實指「無 collisions」所需之 passwd[] 最小空間。
crypt() 各模式之 hash 長度:https://man7.org/linux/man-pages/man3/crypt.3.html#NOTES

因不愼假設資料結構需向舊相容,我未列入擴大其欄位之方法。
DreamBBS 將 crypt() 回傳的總 hash 分割儲存:

  • SHA-256:passwd[]: 3 ($5$) + 10 (salt) + 1 (\0) = 14 (= PASSSIZE);passhash[]: 1 ($) + 43 (hashed 密碼) + 1 (\0) = 45 (= PASSHASHSIZE)。
  • DES:只看 passwd[],忽略 passhash[]

可參考 https://github.com/ccns/dreambbs/blob/9b3e98f9d7/lib/passwd.c#L89

若可擴大 passwd[] 的話,大可不必如此切割。

  1. 基本上不會採用逆推原密碼的方式處理這個問題. 比較簡單的做法是, 同時支援新舊方式, 當使用者上站時, 密碼驗證通過之後當場轉成新的 hash funciton 寫回檔案. 由於帳號太久沒用會被砍掉, 所以理論上幾個月之後全站就能轉換完成.

function

上述計算顯示暴力逐一破解是 不可行 的(不過忘了寫上結論,還請見諒)。但若破解 6 碼密碼已有一定難度,設定 32–64 碼長的密碼眞的有必要嗎?(不過,由於人造密碼遠弱於隨機密碼,還是需要 > 8 碼密碼以彌補其強度)

DreamBBS 之密碼最長 36 (= PLAINPASSSIZE - 1),一是防 collisions(必要性或可議),二是防 vget() 輸入框溢出畫面(雖後已隱藏顯示),三是防誤按鍵盤過度會連打退格鍵過度(?)。

由於站臺之 社會角色之差異,DreamBBS 未啟用賬號退場機制,也無法有效籍助 忘記密碼 之功能,因此無法採取批踢踢的策略,僅能無限期同時持續支援 DES 與 SHA-256 之 hash 方式,並預設對新密碼使用 SHA-256 hashing。

即使密碼長度不變,改用非 DES hashing 仍有一定作用。考慮 DES hashing 每碼僅 lower 7 bits 有效,8 碼密碼至少可有 2⁸ = 256 種 hash 相同的輸入。超出 8 碼的話,hash 相同的輸入爲 223ⁿ⁻⁸ 倍。
但這也對 滾動式密碼自動更新 造成窒礙。

另外,由於 checkpasswd() 會把原密碼抹除,所以驗證後取不到原密碼(雖然可以修改程式以 類繞過)。見 https://github.com/ptt/pttbbs/blob/2fae7442d4/common/bbs/passwd.c#L224

@chhsiao1981
Copy link
Contributor

chhsiao1981 commented Apr 26, 2022

覺得目前更重要的是如何在 login 成功以後.
在 UI 上如果發現 user 的 password "太簡單".
引導 user 設定"不是太簡單的 password"

對於"不是太簡單的 password"的定義可以再討論. 我的提議是需字數 >= 7 + 含有 non-alnum 的 char.
(或是更高級的就是弄個 dict 把簡單 password set 放進去.)

(不論 crypt system 多強~ 只要 user 設定 password 是 1111 這種爛密碼~. 仍然是很容易被破~)

@IepIweidieng
Copy link
Contributor

IepIweidieng commented Apr 26, 2022

如果要儘可能維持與連線軟體的相容,又要有滾動式密碼自動更新的可靠驗證的話,以下方式或許可行。

假設 PttBBS 將採用 SHA-256 hashing。

  1. 需要允許在各 BBS 使用者家目錄下建立一個檔案,其內容爲 SHA-256 hashing。假設其名稱爲 sha256.passwd
    • 此資料僅於密碼更改的宣導期間使用,因此不宜併入 userec_t,否則宣導期結束後需再次更改資料結構,造成維護上的不便。
  2. 使用者登入時,若密碼驗證成功,爲 DES hashing,且 sha256.passwd 不存在的話,則用原密碼計算對應的 SHA-256 hash 並存入 sha256.passwd。(尚不更改 userec_t::passwd[]
  3. 使用者下次登入時,若密碼以 DES hashing 驗證成功,且 sha256.passwd 存在的話,就再以 SHA-256 hashing 驗證。
    • 若 SHA-256 hash 符合的話,就將 userec_t::passwd[]sha256.passwd 的內容覆蓋,並刪除 sha256.passwd
    • 若 SHA-256 hash 不符合的話,直接刪除 sha256.passwd,當作 1. 沒發生。
  4. 密碼更改的宣導期結束後,如果使用者的密碼仍使用 DES hashing,且存在有效的 sha256.passwd 的話,就以 sha256.passwd 的內容直接覆蓋 userec_t::passwd[],並刪除 sha256.passwd。否則萬一 sha256.passwd 存在的話,直接將之刪除。

此外,若以上任一階段被執行時,登入後會顯示相應訊息告知使用者說其密碼發生了什麼變化的話,可幫助使用者掌握其密碼的更新情況。

@hungte
Copy link
Contributor

hungte commented Jun 8, 2023

含有 non-alnum 的 char.

Latest NIST password guideline explicitly said don't add complexity restrictions. We need to support longer passwords, not enforcing special characters in the pw.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants