Open LImoritakeU opened 2 years ago
DES crypt...
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 之功能。
crypt.c
,以線性插值推論)-lcrypt
對 libcrypt 進行 靜態 連結,故每一處理程序中皆存有一個加速 table。
fork()
採用了 copy-on-write 機制,但由於此加速 table 在 hashing 運算中會被更動(據 crypt.c
),仍會觸發複製,浪費無法避免。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()
,故有記憶體使用量上的疑慮。
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[]
) 則是將已不再使用的大資料欄位變更用途而來的。
userec_t
應無此類不使用的大欄位,故不適用此修改方式。只增加支援 > 8 bytes 8-bit 密碼明碼還不足夠,還需使用者主動修改密碼。
由於依 hashed 密碼直接回推原密碼明碼,再以新方式 hash,需要一定的計算資源(下述),又因爲 DES hashing 只看密碼明碼 7-bit 的部份,如果使用者的密碼明碼包含特殊字元,會回推錯誤,造成使用者以同樣的密碼明碼無法登入之窘況。
因此站方尚需加以宣導,鼓勵使用者利用新密碼 hash 的方式,方可發揮 > 8 bytes 8-bit 密碼的優點。
passwd[]
大小的確不夠, 因此需要擴增欄位, 關站轉換檔案格式. 不過因為只需要存密碼的 salt 跟 hash, 空間不一定需要用到 @IepIweidieng 說的那麼大.
p.s. 如果要做, 記得 "取備份" 這功能要特別處理, 以免拿舊欄位蓋掉新的資料, 可能會造成資料錯亂的問題.
- 基本上不會採用逆推原密碼的方式處理這個問題. 比較簡單的做法是, 同時支援新舊方式, 當使用者上站時, 密碼驗證通過之後當場轉成新的 hash funciton 寫回檔案. 由於帳號太久沒用會被砍掉, 所以理論上幾個月之後全站就能轉換完成.
直接在背後轉換會有問題,因為 crypt 現在只吃 8 碼,但登入可以打超過 8 碼(logind 吃 14 碼),在背後轉無法確認超過的部分是不是使用者想要的,如果在轉換的那次不小心 8 位以後輸入錯誤,之後會無法登入。
直接在背後轉換會有問題,因為 crypt 現在只吃 8 碼,但登入可以打超過 8 碼(logind 吃 14 碼),在背後轉無法確認超過的部分是不是使用者想要的,如果在轉換的那次不小心 8 位以後輸入錯誤,之後會無法登入。
good point. 所以這時要讓使用者再輸入一次確認? 不過這樣大概會讓很多做自動 login 的 app 壞掉 XD
所以這時要讓使用者再輸入一次確認?
我覺得可以有個宣導期,過了之後登入強迫改密碼。
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 分割儲存:
passwd[]
: 3 ($5$
) + 10 (salt) + 1 (\0
) = 14 (= PASSSIZE
);passhash[]
: 1 ($
) + 43 (hashed 密碼) + 1 (\0
) = 45 (= PASSHASHSIZE
)。passwd[]
,忽略 passhash[]
。可參考 https://github.com/ccns/dreambbs/blob/9b3e98f9d7/lib/passwd.c#L89。
若可擴大 passwd[]
的話,大可不必如此切割。
- 基本上不會採用逆推原密碼的方式處理這個問題. 比較簡單的做法是, 同時支援新舊方式, 當使用者上站時, 密碼驗證通過之後當場轉成新的 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。
覺得目前更重要的是如何在 login 成功以後. 在 UI 上如果發現 user 的 password "太簡單". 引導 user 設定"不是太簡單的 password"
對於"不是太簡單的 password"的定義可以再討論. 我的提議是需字數 >= 7 + 含有 non-alnum 的 char. (或是更高級的就是弄個 dict 把簡單 password set 放進去.)
(不論 crypt system 多強~ 只要 user 設定 password 是 1111 這種爛密碼~. 仍然是很容易被破~)
如果要儘可能維持與連線軟體的相容,又要有滾動式密碼自動更新的可靠驗證的話,以下方式或許可行。
假設 PttBBS 將採用 SHA-256 hashing。
sha256.passwd
。
userec_t
,否則宣導期結束後需再次更改資料結構,造成維護上的不便。sha256.passwd
不存在的話,則用原密碼計算對應的 SHA-256 hash 並存入 sha256.passwd
。(尚不更改 userec_t::passwd[]
)sha256.passwd
存在的話,就再以 SHA-256 hashing 驗證。
userec_t::passwd[]
以 sha256.passwd
的內容覆蓋,並刪除 sha256.passwd
。sha256.passwd
,當作 1. 沒發生。sha256.passwd
的話,就以 sha256.passwd
的內容直接覆蓋 userec_t::passwd[]
,並刪除 sha256.passwd
。否則萬一 sha256.passwd
存在的話,直接將之刪除。此外,若以上任一階段被執行時,登入後會顯示相應訊息告知使用者說其密碼發生了什麼變化的話,可幫助使用者掌握其密碼的更新情況。
含有 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.
問題描述
目前 ptt 只支援最高 8 個字元的密碼,希望能將上限提升至 32 或 64 字元以提升帳號安全性
https://github.com/ptt/pttbbs/blob/4d56e77f264960e43e060b77e442e166e5706417/include/pttstruct.h#L49
我沒有寫過 C ,但就 code-level 並沒有特別對 8 個字元限制特別說明,我只能猜想或許限制是在儲存層 ?