パスワードの強度を計測してくれる zxcvbn-ts
Reactのアプリケーションに試しに入れてみたため、実装方法を記載していきます!
先に使った感想を記載しておきます
- 実装めっちゃ簡単。お手軽にパスワードの強度評価してくれる(無料です ☺️)
- TypeScriptの補完しっかり効きます!
検証した環境
1 | @zxcvbn-ts/core | 3.0.3 |
2 | @zxcvbn-ts/language-common | 3.0.3 |
3 | @zxcvbn-ts/language-ja | 3.0.1 |
4 | react | 18.2.0 |
5 | next | 13.4.4 |
zxcvbn-tsの導入
公式サイトにexampleがしっかり書いてあるのでそちらを参考に。
まずパッケージをインストール
yarn add @zxcvbn-ts/core @zxcvbn-ts/language-common @zxcvbn-ts/language-ja
指定可能な言語はGitHubのページに記載されています
公式のコードを参考にconsole.logを使って表示出来る事を確認します
'use client'
import { zxcvbn, zxcvbnOptions } from '@zxcvbn-ts/core'
import * as zxcvbnCommonPackage from '@zxcvbn-ts/language-common'
import * as zxcvbnJaPackage from '@zxcvbn-ts/language-ja'
const SamplePage = () => {
const password = 'somePassword'
const options = {
translations: zxcvbnJaPackage.translations,
graphs: zxcvbnCommonPackage.adjacencyGraphs,
dictionary: {
...zxcvbnCommonPackage.dictionary,
...zxcvbnJaPackage.dictionary,
},
}
zxcvbnOptions.setOptions(options)
const result = zxcvbn(password)
console.log('result: ', result)
// zxcvbnを試したいため画面には何も表示しない
return <div />
}
export default SamplePage
↑はNext.jsのApp Routerを使ったコードになっています。
状況に合わせて読み替えて下さいmm
console.logの中を見てみると
日本語のfeedbackが入ってる…!
その他にも表示に役立ちそうな値が色々入っていそうです。
zxcvbn-tsの返り値の確認
zxcvbnが返す値の詳細は公式のOutputという項目にしっかり記載されています。
重要そうなものを3つピックアップします。
score
パスワードの安全性を計る指標。
0 から 4 の値が返ってくる5段階評価で、0だと安全性が低く4だと安全性が高くなります。
公式の記載内容をDeepLで翻訳すると以下のようになります。日本語が・・
- 0・・推測可能すぎる : 危険なパスワード
- 1・・非常に推測しやすい : オンライン攻撃からの保護
- 2・・ある程度推測できる : スロットルのないオンライン攻撃からの保護
- 3・・安全無比 : オフライン・スローハッシュ・シナリオからの適度な保護
- 4・・推し量れない : オフライン・スローハッシュ・シナリオからの強力な保護
crackTimesSeconds
秒で数えた際の推定クラック時間、以下のような値が返ってきます。
{
"onlineThrottling100PerHour": 18734400,
"onlineNoThrottling10PerSecond": 52040,
"offlineSlowHashing1e4PerSecond": 52.04,
"offlineFastHashing1e10PerSecond": 0.00005204
}
再度DeepL翻訳。下2つは何言ってるか分からない・・😂
- onlineThrottling100PerHour・・パスワード認証の試行を制限するサービスへのオンライン攻撃
- onlineNoThrottling10PerSecond・・レートを制限しないサービスへのオンライン攻撃
- offlineSlowHashing1e4PerSecond・・オフライン攻撃。複数の攻撃者を想定、ユーザー固有の適切なソルティングと低速のハッシュ関数が必要。bcrypt、scrypt、PBKDF2のような、適度な仕事率を持つハッシュ関数。
- offlineFastHashing1e10PerSecond・・ユーザー固有のソルティングを用いたオフライン攻撃。SHA-1、SHA-256またはMD5のような高速ハッシュ関数。幅広い範囲10億から1兆までの妥当な数コア数とマシンによる。10B/秒を目安に。
feedback
入力されたパスワードに対してのフィードバック。
画面上に表示出来るような文字列を返してくれます。
password
の場合(scoreは0)
feedback: {
warning: '一般的に使用されるパスワードです',
suggestions: [ '一般的ではない単語を増やしてください' ]
}
somePassword
の場合(scoreは1)
feedback: {
warning: 'よく使われるパスワードに似ています',
suggestions: [ '一般的ではない単語を増やしてください', '最初の文字以外も大文字にしてください' ]
}
c2DL6J8b
の場合(scoreは2)
feedback: {
warning: null,
suggestions: [ '一般的ではない単語を増やしてください' ]
}
試してみる
パスワード生成ツールを使っていくつか試してみました
パスワード | スコア | offlineSlowHashing1e4PerSecond | メモ |
---|---|---|---|
a | 0 | 0.0012 | |
password | 0 | 0.0003 | |
pV8k | 1 | 1.0001 | ランダム4桁(記号なし) |
kF4J32 | 1 | 100.0001 | ランダム6桁(記号なし) |
c2DL6J8b | 2 | 10000.0001 | ランダム8桁(記号なし) |
t9GeU.x_ | 2 | 10000.0001 | ランダム8桁(記号入) |
PYAWykeD7C | 3 | 1000000.0001 | ランダム10桁(記号なし) |
38Kj(i7cZ! | 3 | 1000000.0001 | ランダム10桁(記号入) |
VY&TbWU#xMD3 | 4 | 100000000.0001 | ランダム12桁(記号入) |
RbHMsVkiVt3KHjsj | 4 | 1000000000000 | ランダム16桁(記号なし) |
12桁までくるとかなり強いんですねーー!
また、この秒数によると桁数が増えれば増えるほど、
記号を含めなくても強くなるみたいです。
Reactで強度を表示する
では本題のReactのコードです!
以下のような環境での実装になります
- App Routerを用いたNext.js
- cssはtailwindを使用
'use client'
import { useEffect, useState } from 'react'
import { zxcvbn, zxcvbnOptions } from '@zxcvbn-ts/core'
import { ZxcvbnResult } from '@zxcvbn-ts/core/src/types'
import * as zxcvbnCommonPackage from '@zxcvbn-ts/language-common'
import * as zxcvbnJaPackage from '@zxcvbn-ts/language-ja'
const SamplePage = () => {
// 入力されたパスワードの値
const [password, setPassword] = useState<string | undefined>(undefined)
// zxcvbnの結果
const [result, setResult] = useState<ZxcvbnResult | undefined>(undefined)
// zxcvbnの設定
const options = {
translations: zxcvbnJaPackage.translations,
graphs: zxcvbnCommonPackage.adjacencyGraphs,
dictionary: {
...zxcvbnCommonPackage.dictionary,
...zxcvbnJaPackage.dictionary,
},
}
zxcvbnOptions.setOptions(options)
useEffect(() => {
// パスワードがない場合はzxcvbnの結果をリセットする
if (!password) {
setResult(undefined)
return
}
// 入力されたパスワードを用いてzxcvbnの結果を取得、useStateに格納する
setResult(zxcvbn(password))
}, [password, result])
return (
<div className="w-[300px]">
<div className="grid gap-[20px]">
<label htmlFor="email">
<div>email</div>
<input
type="text"
id="email"
className="h-[30px] w-full rounded-[4px] border border-[#909090]"
/>
</label>
<label htmlFor="password">
<div>password</div>
<input
type="text"
id="password"
className="h-[30px] w-full rounded-[4px] border border-[#909090]"
onChange={e => setPassword(e.target.value)}
/>
</label>
</div>
<div className="mt-[60px]">
<div className="flex w-full gap-[1%]">
{/* 強度を表す5段階のバーを表示 */}
{[0, 1, 2, 3, 4].map(v => (
<div
className={`h-[4px] w-[24%] ${
result && v <= result.score ? 'bg-[#666]' : 'bg-[#ccc]'
}`}
key={v}
/>
))}
</div>
{result && result.score + 1}
</div>
{/* feedback.warning がある場合は表示 */}
{result?.feedback && <div className="text-[#f00]">{result.feedback.warning}</div>}
</div>
)
}
export default SamplePage
強度を表示出来ました!🎉
ざっくりやっている事としては
- パスワード用のinputの値を useState の
password
に設定(55行目) - 上記値が更新される度に zxcvbn が実行され useState の
result
が更新される(35行目) result
の値を元に結果をHTML上に表示(60 - 76行目)
となります。
簡単に強度が表示が出来て、しっかりTypeScriptの補完も効くので
zxcvbn-ts
かなり使いやすい印象でした!
余談
そもそも今回zxcvbn-tsを使ってみようと思ったきっかけがこちらです。
読みやすく分かりやすい、大変ステキな記事です ☺️
上記記事に書かれている内容としまして
アプリケーションを作る時、皆さんは新規登録フォームで「パスワード再入力」というinputを用意してませんか?
私、今まで特に意識をしてなかったのですが、実は
- コンバージョンが落ちる
- 再入力の部分はパスワードをコピペするだけで再確認になっていない
という研究結果があるそうです
We achieved a 56.3% increase in form conversions by removing “Confirm Password” while not negatively affecting the password reset rate.
DeepL翻訳: パスワードのリセット率に悪影響を与えることなく、「パスワードの確認」を削除することで、フォームコンバージョンを56.3%増加させることができました。
私「パスワード再入力」inputがある新規登録ページの実装何度かしてきたのですが、
この辺りのUXてこんな考える事が色々あるんですね。。
いやぁ難しい…!