在Rails裡面處理密碼加密雖然已經夠簡單了,不過至少需要以下3個步驟:
1. 自己引用演算法(MD5之類的)或裝外掛(例如: Acts as Authenticated)。
2. 用before_filter。
3. 設定方法,例如用authorized或checkpassword檢查密碼。
大部分的做法會把Salt、加密過的密碼一起存入資料庫(hash(password, salt)),雖然被破解的機率不高,但有Salt就是有跡可循,而且利用SQL Injection搞不好可以一次把整個資料庫的密碼、Salt偷出來。
bcrypt-ruby這顆寶石則是將salt跟密碼hash在一起,然後合起來做成一個新的Salt,最後再把加密過的Salt2存入資料庫(salt2(hash(password, salt1)),也就是說資料庫只存Salt2,不存密碼。
將密碼、Salt一起存資料庫其實有很高的風險,不要認為現代多少多少bit的加密在技術上不容易被破解,不存密碼才可以真正大聲說”技術上不容易被破解”。
如果硬要爭辯說BCrypt的方式也是把密碼塞在Salt2裡,但誰知道怎麼從Salt2解出Salt1跟加密過的密碼呢?這遠比有個明顯、等人來偷的叫做password的欄位要安全的多。
用bcrypt-ruby最大的好處是把model作一些修改,就可以直接用==運算子比對密碼(比用authorized直覺多了吧),或用=運算子設定密碼(但密碼不存資料庫)。
bcrypt-ruby可以用以下指令安裝:
sudo gem install bcrypt-ruby使用範例在此: http://pastie.textmate.org/58489。
手術的部份相當簡單,只須在model裡頭去require, include BCrypt module進來,並修改password相關的method,雖然資料庫沒有password欄位,但我必須用一個假的password method來騙ActiveRecord說我有一個password欄位,不然view上的操作會出錯。
bcrypt-ruby原作者Coda Hale所寫的README有些地方描述的不是很清楚,我跟他通過幾次信之後才澄清整個觀念。
基本上,設計須要驗證密碼的model(例如user/account)時,是不需要額外給他一個password欄位的,因為我們不把密碼存在資料庫。
只需要建一個存salt2加密訊息的欄位(secret, what ever),這個欄位名稱必須跟password method裡那個self.xxx一致,因為要把加密訊息存在這裡。
例如:
def password
@password ||= Password.new(secret)
end
def password=(new_password)
@password = Password.create(new_password)
self.secret = @password
end看你存加密訊息的欄位是什麼,self後面跟的就是那個欄位名稱,像我的欄位就叫做”secret”。
千萬別像README所寫的範例那樣把那個欄位名稱定為hash,因為model會從ActiveRecord繼承一個hash method,如此一來會剛好呼叫到那個hash method,而BCrypt會拿hash method的傳回值來產生密碼,這樣一來一定會出現invalid hash的錯誤。
