入力されたパスワード強度を視覚的に分かるUI
概要
ユーザーが入力するパスワードの強度をリアルタイムで視覚的にフィードバックするUIコンポーネントです。
強度メーターと条件チェックリストにより、ユーザーは安全なパスワードを直感的に作成できます。セキュリティ向上とUX改善に繋がり、会員登録フォームなどに最適です。
単一のHTMLファイルで完結しているため、既存のプロジェクトにも容易に組み込むことが可能です。
デモサイト
ソースコード
まずはソースコードをご覧ください。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>オリジナルパスワード強度チェッカー</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css" rel="stylesheet">
<style>
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;700&display=swap');
:root {
--color-weak: #e74c3c;
--color-medium: #f39c12;
--color-strong: #2ecc71;
--color-bg: #f4f7f6;
--color-card-bg: #ffffff;
--color-text: #333;
--color-gray: #bdc3c7;
--border-radius: 8px;
}
body {
font-family: 'Noto Sans JP', sans-serif;
background-color: var(--color-bg);
color: var(--color-text);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
padding: 20px;
box-sizing: border-box;
}
.card {
background-color: var(--color-card-bg);
padding: 30px 40px;
border-radius: var(--border-radius);
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
width: 100%;
max-width: 400px;
}
h1 {
margin: 0 0 10px;
text-align: center;
font-size: 24px;
}
.subtitle {
margin: 0 0 30px;
text-align: center;
color: #7f8c8d;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 8px;
font-weight: 700;
}
.input-wrapper {
position: relative;
}
.input-wrapper input {
width: 100%;
padding: 12px 40px 12px 15px;
border: 1px solid var(--color-gray);
border-radius: var(--border-radius);
font-size: 16px;
box-sizing: border-box;
transition: border-color 0.3s;
}
.input-wrapper input:focus {
outline: none;
border-color: #3498db;
}
.toggle-password {
position: absolute;
top: 50%;
right: 15px;
transform: translateY(-50%);
cursor: pointer;
color: #95a5a6;
}
.strength-meter {
height: 10px;
background-color: #ecf0f1;
border-radius: 10px;
overflow: hidden;
margin-top: -10px;
}
.strength-bar {
height: 100%;
width: 0;
background-color: var(--color-weak);
transition: width 0.3s, background-color 0.3s;
}
.strength-text {
font-size: 14px;
font-weight: 700;
margin: 8px 0 15px;
text-align: right;
height: 1em;
}
.requirements-list {
list-style: none;
padding: 0;
margin: 0 0 25px;
font-size: 14px;
color: #7f8c8d;
}
.requirements-list li {
margin-bottom: 8px;
transition: color 0.3s;
}
.requirements-list li::before {
font-family: "Font Awesome 5 Free";
font-weight: 900;
content: "\f00d";
margin-right: 10px;
color: var(--color-weak);
transition: content 0.3s, color 0.3s;
}
.requirements-list li.valid {
color: var(--color-text);
}
.requirements-list li.valid::before {
content: "\f00c";
color: var(--color-strong);
}
.match-text {
font-size: 14px;
margin-top: 8px;
height: 1em;
font-weight: bold;
}
</style>
</head>
<body>
<main class="card">
<h1>パスワードを新規設定</h1>
<p class="subtitle">安全なパスワードを設定してください</p>
<form action="#" method="post" id="passwordForm">
<div class="form-group">
<label for="password">パスワード</label>
<div class="input-wrapper">
<input type="password" id="password" name="password" placeholder="パスワードを入力" required>
<i class="fa-solid fa-eye-slash toggle-password"></i>
</div>
</div>
<div class="strength-meter">
<div class="strength-bar"></div>
</div>
<p class="strength-text"></p>
<ul class="requirements-list">
<li data-requirement="length">8文字以上</li>
<li data-requirement="lowercase">小文字の英字を1つ以上含む</li>
<li data-requirement="uppercase">大文字の英字を1つ以上含む</li>
<li data-requirement="number">数字を1つ以上含む</li>
<li data-requirement="special">記号 (!, @, #, $, % など) を1つ以上含む</li>
</ul>
<div class="form-group">
<label for="password-cf">パスワード(確認用)</label>
<div class="input-wrapper">
<input type="password" id="password-cf" name="password:cf" placeholder="もう一度パスワードを入力" required>
</div>
<p class="match-text"></p>
</div>
</form>
</main>
<script>
document.addEventListener('DOMContentLoaded', () => {
const passwordInput = document.getElementById('password');
const passwordCfInput = document.getElementById('password-cf');
const strengthBar = document.querySelector('.strength-bar');
const strengthText = document.querySelector('.strength-text');
const requirementsList = document.querySelector('.requirements-list');
const matchText = document.querySelector('.match-text');
const togglePassword = document.querySelector('.toggle-password');
const requirements = [
{ regex: /.{8,}/, element: requirementsList.querySelector('[data-requirement="length"]') },
{ regex: /[a-z]/, element: requirementsList.querySelector('[data-requirement="lowercase"]') },
{ regex: /[A-Z]/, element: requirementsList.querySelector('[data-requirement="uppercase"]') },
{ regex: /[0-9]/, element: requirementsList.querySelector('[data-requirement="number"]') },
{ regex: /[^A-Za-z0-9]/, element: requirementsList.querySelector('[data-requirement="special"]') }
];
passwordInput.addEventListener('input', () => {
const password = passwordInput.value;
// --- 1. UIチェックリストの更新(この部分はUI表示のためだけ) ---
requirements.forEach(req => {
if (req.regex.test(password)) {
req.element.classList.add('valid');
} else {
req.element.classList.remove('valid');
}
});
// --- 2. 強度判定ロジック ---
if (password.length === 0) {
strengthBar.style.width = '0%';
strengthText.textContent = '';
return; // 何も入力されていない場合はここで処理終了
}
// まず、長さが足りなければ無条件で「弱い」
if (password.length < 8) {
strengthBar.style.width = '25%';
strengthBar.style.backgroundColor = 'var(--color-weak)';
strengthText.textContent = '弱い';
strengthText.style.color = 'var(--color-weak)';
return;
}
// 長さが足りている場合、文字の種類の数で判定
let varietyCount = 0;
if (/[a-z]/.test(password)) varietyCount++;
if (/[A-Z]/.test(password)) varietyCount++;
if (/[0-9]/.test(password)) varietyCount++;
if (/[^A-Za-z0-9]/.test(password)) varietyCount++;
switch (varietyCount) {
case 1: // 長くても文字の種類が1つだけなら「弱い」
strengthBar.style.width = '25%';
strengthBar.style.backgroundColor = 'var(--color-weak)';
strengthText.textContent = '弱い';
strengthText.style.color = 'var(--color-weak)';
break;
case 2:
strengthBar.style.width = '50%';
strengthBar.style.backgroundColor = 'var(--color-medium)';
strengthText.textContent = '普通';
strengthText.style.color = 'var(--color-medium)';
break;
case 3:
case 4:
strengthBar.style.width = '100%';
strengthBar.style.backgroundColor = 'var(--color-strong)';
strengthText.textContent = '安全';
strengthText.style.color = 'var(--color-strong)';
break;
}
checkPasswordMatch();
});
passwordCfInput.addEventListener('input', checkPasswordMatch);
function checkPasswordMatch() {
if (passwordCfInput.value.length === 0) {
matchText.textContent = '';
return;
}
if (passwordInput.value === passwordCfInput.value) {
matchText.textContent = '◎ パスワードが一致しました';
matchText.style.color = 'var(--color-strong)';
} else {
matchText.textContent = '× パスワードが一致しません';
matchText.style.color = 'var(--color-weak)';
}
}
togglePassword.addEventListener('click', () => {
const type = passwordInput.getAttribute('type') === 'password' ? 'text' : 'password';
passwordInput.setAttribute('type', type);
togglePassword.classList.toggle('fa-eye');
togglePassword.classList.toggle('fa-eye-slash');
});
});
</script>
</body>
</html>
SPIRALのデフォルトのデザインから少し離れていますが、必要な部分を切り取ってご利用ください。
入力文字強度の詳細
弱い (Weak) と判定される例
パスワードが8文字未満の場合、または8文字以上でも使われている文字の種類が1種類のみの場合に「弱い」と判定されます。
短いパスワード
例:p@ss123 (7文字のため、文字の種類に関わらず「弱い」と判定されます)
1種類の文字だけで構成された長いパスワード
例:passwordlong (8文字以上ですが、使われている文字が小文字の1種類だけのため「弱い」と判定されます)
例:AAAAAAAAAA (8文字以上ですが、使われている文字が大文字の1種類だけのため「弱い」と判定されます)
パスワードが8文字以上で、かつ使われている文字の種類が2種類の場合に「普通」と判定されます。
例: password12345 (小文字 + 数字 の2種類)
例: Passwordtest (大文字 + 小文字 の2種類)
例: password-test (小文字 + 記号 の2種類)
パスワードが8文字以上で、かつ使われている文字の種類が3種類以上の場合に「安全」と判定されます。
3種類利用の例 (大文字 + 小文字 + 数字)
例:Password12345
4種類すべて利用の例 (最も安全)
例:Str0ng-Pa55!word
注意事項
このコンポーネントを利用する上で、特に重要な点は以下の通りです。
1. フロントエンドでの検証について
このツールが行っているのは、あくまでユーザーの利便性を高めるためのフロントエンド(ブラウザ側)でのチェックです。入力されたパスワードがそのままサーバーに送信されるのを防ぐものではありません。
2. 強度の判定基準のカスタマイズ
パスワードの強度を決めるルール(文字数や必須の文字種)は、JavaScript内の配列で定義されています。サイトのセキュリティポリシーに応じて、この基準は自由に変更・強化することが可能です。
// この部分の正規表現を変更することで、ルールをカスタマイズできます
const requirements = [
{ regex: /.{8,}/, element: requirementsList.querySelector('[data-requirement="length"]') },
{ regex: /[a-z]/, element: requirementsList.querySelector('[data-requirement="lowercase"]') },
{ regex: /[A-Z]/, element: requirementsList.querySelector('[data-requirement="uppercase"]') },
{ regex: /[0-9]/, element: requirementsList.querySelector('[data-requirement="number"]') },
{ regex: /[^A-Za-z0-9]/, element: requirementsList.querySelector('[data-requirement="special"]') }
];
3. 外部ライブラリへの依存
チェックリストやパスワード表示/非表示のアイコンは、外部のWebサービスである「Font Awesome」から読み込んでいます。
そのため、オフライン環境や、セキュリティポリシーで外部CDNへのアクセスが制限されている環境では、アイコンが表示されない場合があります。
おわりに
今回は、パスワードの強度を視覚的にチェックし、ユーザーの入力を補助するUIコンポーネントを紹介しました。
複雑なパスワード要件をユーザーに分かりやすく伝え、より安全なパスワードの設定を促すことで、サイト全体のセキュリティ向上とUX改善に直接的に貢献します。
提供したコードは単一ファイルで完結しているため、様々なプロジェクトへ容易に導入できるのが特徴です。
ここで紹介したコードは、あくまで一つのサンプルです。デザインや強度の判定ロジックを、ご自身のサービスの要件に合わせて自由にカスタマイズしてみてください。
【お知らせ】本実装の構築代行・カスタマイズを承ります
記事でご紹介した内容や、貴社の要件に合わせた柔軟なカスタマイズ開発を承っております。
SPIRALの専門チームが要件定義から実装まで一貫してサポートいたしますので、まずはお見積もりをご依頼ください。