如果使用的網站服務需要以密碼登入,而且有忘記密碼的功能,試著使用看看,如果它發送給你的郵件或簡訊通知上,確實地寫著當初你設定的密碼,就可以寫信去罵人了!

加密雜湊演算

若使用明碼儲存密碼,就意謂著,若是資料庫被駭入,或者是被SQL Injection撈出了使用者資料,那在使用者察覺之前,攻擊者就可以登入系統通行無阻了!別以為這應該是FAQ等級的常識,不久前國內就有網路銀行,還將明文的密碼寫到JavaScript裏,發送至使用者的瀏覽器。

例如,有個「我的密碼沒加密」(https://plainpass.com/)網站,就收錄了不少在密碼處理上有問題的網站服務。此外,在2018年5月,也發生過家長監控軟體TeenSafe在AWS上的伺服器,存放用戶資料並未設定密碼防護,而且以明碼儲存密碼;有時,甚至是系統漏洞問題,導致密碼沒加密就直接儲存,像是Twitter,在2018年5月所發生的加密漏洞問題。

在密碼防護方面,不儲存明碼的最基本方式是,將密碼進行加密雜湊(Cryptographic hash)演算,過去常見的做法,是使用MD5產生訊息摘要(Message digest)並儲存,而不是儲存原始密碼;在使用者登入時,輸入的密碼會經由相同的加密雜湊演算,再與儲存的摘要進行比對,若相同表示密碼正確。

不過,MD5生成速度快,這意謂著在今日高速的處理器運算下,就算是暴力攻擊或字典攻擊,也可能能迅速被破解;MD5並沒有避免碰撞,這意味不同密碼有可能生成相同的雜湊值,令查表法攻擊的可能性提高,網路上甚至有許多的彩虹表(Rainbow Tables),為有心人士做出的密碼與雜湊比對表,網路上搜尋「md5 crack」,也可以找到許多破解MD5的服務。

在過去,黑客曾經在暗網出售Yahoo的使用者資料,雖然密碼經MD5處理,不過,在當時就被認為與明碼儲存無異;就算是SHA-1也不見得安全,同樣可使用彩虹表來破解。

雖然SHA的碰撞機率比MD5來得小,然而Google曾於2017年達成了SHA-1碰撞,雖然運算成本高昂,但隨著處理器運算能力等的增加,這樣的運算成本在未來將會越來越低。

加鹽雜湊演算

彩虹表破解密碼的概念就是查表,如果能讓相同的密碼經過雜湊演算時,產生不同的結果,就可以避免彩虹表方式來破解密碼,因此有人想出了,在儲存密碼時產生一個隨機鹽值(Salt),將鹽值混入密碼後進行雜湊,因為每次的鹽值都是隨機的,產生的摘要就會不同。

為了能在使用者登入時,確認密碼是否正確,鹽值也必須儲存下來,而在使用者登入時,取得各自的鹽值,混入使用者輸入的密碼,並進行雜湊,接著,再將結果與儲存下來的摘要進行比對,以便確認密碼是否正確。

事實上,加鹽雜湊時使用的鹽值,不應該重複使用,更不要以使用者名稱、ID或者是手機號碼等,來作為鹽值,因為若有使用者具備相同的密碼,那麼,加鹽雜湊後的結果也會是相同的。

換言之。倘若攻擊者試著事先製作出具有相同摘要的密碼,然後反向查詢,即可找出具有相同摘要的使用者。之所以會出現如此狀況,是因為許多使用者往往使用相同的密碼,而使得這類攻擊手法得以生效。

應該在每次儲存新的密碼時產生新的鹽值,加鹽雜湊時使用的鹽值也不能太短,若是太短,攻擊者就可以事先做出全部鹽值的列表;不要使用普通的隨機數演算來產生鹽值,建議使用和雜湊演算輸出的摘要等長的鹽值,並使用安全偽隨機數生成器(Cryptographically Secure Pseudo-Random Number Generator),像是Java的SecureRandom。

慢得剛好的雜湊演算

有人會對密碼進行多次的雜湊演算,甚至是多次混合不同的雜湊演算組合,試圖讓攻擊者不知道系統使用了哪種雜湊組合,就是他們採取這個動作的目的之一。

然而,在〈Salted Password Hashing - Doing it Right〉(https://goo.gl/xUsuom)就提到,如果攻擊者取得了原始碼(像是透過應用程式開放原始碼的管道),就有可能找出密碼與摘要間的對應關係,進而反向推出演算的過程。

同時,我們採用雜湊演算組合,還有另一個理由,那就是,可以讓破解過程變慢,希望慢到令攻擊者會想放棄。

加密雜湊演算本身可以很快地產生摘要,而由於現代的運算設備速度越來越快,使得攻擊者就算是採用字典攻擊或暴力破解密碼,也成為可能,為此,增加運算的成本,讓破解過程變慢也確實是防禦的一種策略。不過相較於自行使用多重雜湊演算組合來試圖變慢,有些演算就是為了讓加密運算的速度變慢而設計出來的,像是BCrypt、PBKDF2、SCRYPT等。

這類演算通常會有個參數,決定如何減緩摘要的產生過程,然而犧牲的就是效能,若能找出相對適合的參數,也就是不會對使用者造成差異。然而,能在暴力破解時,可極度增大對方的成本,就是這類演算的目的。

以目前廣為被使用的BCrypt為例,我們可以透過cost參數來決定,而且也使用一個隨機加鹽的流程以防禦彩虹表攻擊,就算是相同的密碼,因為每次產生的鹽值不同,編碼後的結果也就不會相同。

此時,試圖讓攻擊者不知道系統使用哪種雜湊組合,其實,是沒有必要的。例如,BCrypt編碼之後,特徵是相當明確的,就像是下列字串:$2a$10$yh5WJetawp2KloUtEoVzRuT4/WEeR5BhPdfRZGoAvnCtKAbFBP8Sa。

而上述內容,一看就知道是BCrypt產生的編碼,其中的$,是分隔符號,2a表示BCrypt版本,10是成本參數(建議採更高的值),實際上,當中還包括了編碼時所產生的鹽值。

像這類BCrypt演算都是公開的祕密,因為這類演算在設計上,目的就是在知道演算過程下也難以破解,像Spring Security甚至還建議在儲存密碼時,加上{bcrypt}、{pbkdf2}之類的前置,以便於資料庫表格間的轉換遷移。

非專家也要有的基本認識

密碼處理上的重要性,從Spring Security 5之後,強制須選用某個密碼編碼器來處理,而在儲存密碼上,也可見一斑。不過,就算是這樣,還是有人試圖用自定的PasswordEncoder,完全不處理編碼來矇混過去,足見許多人對密碼處理的基本認知不足。

實際上,在觀察一些新聞時,也可以察覺密碼相關的問題。例如國內曾有新聞報導,有婦人在半年內,於購物網站購買225次不取貨,而該購物網站清查之後,發現當事人雖申請的帳號不同,使用的密碼卻都一樣,你覺得這代表著什麼呢?

雖然並非密碼學專家,然而,開發者對於密碼也要有基本的認識,特別是這陣子以來密碼處理的歷史演化過程,上列的〈Salted Password Hashing - Doing it Right〉值得一看,另一方面〈用戶密碼加密存儲十問十答〉(https://goo.gl/D39VsN)也值得參考。

專欄作者

熱門新聞

Advertisement