脆弱なパスワードを放置すると、ユーザーのアカウントは常に危険にさらされます。
アカウントの乗っ取りが起こった場合にはウェブサービスの信頼性を疑われることにもなりかねません。
これらの問題に対処するためのライブラリ zxcvbn をつかって、信頼性低下を予防しましょう。
なにができるの?
脆弱なパスワードが設定された場合、以下の画像のように、「このパスワードは数分で破られます」といった具体的なメッセージを表示することができます。
強力なパスワードを使用する理由がユーザーに伝わりやすくなり、モチベーションをあまり低下させずに再入力を促すことができるようになります。
zxcvbn について、より詳しい情報は以下を閲覧してください。
http://tech.dropbox.com/?p=165
https://github.com/lowe/zxcvbn
Rails アプリケーションに組み込んでみる
では、実際に Rails アプリケーションに組み込んでみましょう。
まず zxcvbn-ruby という素晴らしい gem をインストールします。
gem "zxcvbn-ruby", :require => 'zxcvbn'
bundle
モデルから簡単に使えるよう、バリデータつくってみましたんでコピペしてください。
require 'zxcvbn'
class ZxcvbnValidator < ActiveModel::EachValidator
attr_reader :record, :attribute, :value
SCORE_RANGE = (1..4).freeze
def initialize(options)
unless SCORE_RANGE.include? options[:score]
raise ArgumentError, ":range must be a #{SCORE_RANGE.to_s.gsub('..', ' to ')}"
end
super
end
def validate_each(record, attribute, value)
@record, @attribute, @value = record, attribute, value
@score = options[:score]
add_error unless valid?
end
private
def valid?
@messages_options = {}
week_keywords = []
for key, val in @record.attributes
next if %w[password password_confirmation encrypted_password].include?(key)
next unless val.kind_of?(String)
week_keywords.push val if val
for splited_val in val.split(/[@._]/)
week_keywords.push splited_val
end
end
result = Zxcvbn.test(value, week_keywords)
@messages_options[:crack_time] = display_time result.crack_time
result.score >= @score
end
def add_error
if message = options[:message]
record.errors[attribute] << message
else
record.errors.add attribute, :week_password, @messages_options
end
end
def display_time(seconds)
minute = 60
hour = minute * 60
day = hour * 24
month = day * 31
year = month * 12
century = year * 100
case
when seconds < minute
unit = 'x_seconds'
count = 1 + seconds.ceil
when seconds < hour
unit = 'x_minutes'
count = 1 + (seconds / minute).ceil
when seconds < day
unit = 'about_x_hours'
count = 1 + (seconds / hour).ceil
when seconds < month
unit = 'x_days'
count = 1 + (seconds / day).ceil
when seconds < year
unit = 'x_months'
count = 1 + (seconds / month).ceil
when seconds < century
unit = 'x_years'
count = 1 + (seconds / year).ceil
end
I18n.t "#{unit}.other", scope: [:datetime, :distance_in_words], count: count
end
end
パスワードの強度を単純に評価するだけでなく、
モデルに紐付く情報、たとえばユーザ名やメールアドレスなどに含まれる単語から
推測されるパスワードも強度が低いと判定されるようになっています。
メッセージも忘れずにどうぞ。
ja:
errors:
messages:
week_password: より複雑な%{attribute}を入力してください。あなたの%{attribute}は%{crack_time}で破られます。
ユーザモデルに追加する場合。強度は1から4まで指定できます。
validates :password, zxcvbn: {score: 3}