AIセキュリティ 2025年7月9日

AIが生成する危険なクライアントサイド認証

vibe codingで頻繁に見られる最悪のパターンがあります。 **クライアントサイド認証(Client-Side Authentication)** 認証ロジックをすべてフロントエンド(JavaScript)で実行し、バックエンドサーバーに問い合わせないパターンです。

川西智也

合同会社ロイヤルピース 代表

アンチパターン⑨「クライアントサイド認証」

AI生成コードの最悪の認証パターン

vibe codingで頻繁に見られる最悪のパターンがあります。

クライアントサイド認証(Client-Side Authentication)

認証ロジックをすべてフロントエンド(JavaScript)で実行し、バックエンドサーバーに問い合わせないパターンです。


【要注意】典型的な危険コードパターン

AIが生成しがちな危険なコードを見てみましょう。

危険なコード:

// ❌ JavaScriptにパスワードを埋め込み
const ADMIN_PASSWORD = "secretadmin123";

function checkLogin(password) {
  if (password === ADMIN_PASSWORD) {
    localStorage.setItem('isLoggedIn', 'true');
    return true;
  }
  return false;
}

// ❌ ローカルストレージで認証状態を管理
function isAuthenticated() {
  return localStorage.getItem('isLoggedIn') === 'true';
}

// ❌ クライアントサイドでの権限チェック
function isAdmin() {
  return localStorage.getItem('role') === 'admin';
}

このコードの問題点は明らかです。


なぜこれが危険なのか

問題1: パスワードが丸見え

JavaScriptファイルはブラウザに配信されます。誰でも開発者ツールで確認できます。

問題2: 認証のバイパスが容易

// 攻撃者が開発者コンソールで実行
localStorage.setItem('isLoggedIn', 'true');
localStorage.setItem('role', 'admin');
// これで「認証」完了

問題3: サーバーサイドの検証がない

クライアントの自己申告を信用しているため、改ざんし放題です。


Tea Dating App事件

別記事で紹介したTea Dating App事件を振り返りましょう。

“In the case of Tea, discovering the vulnerability took exactly three clicks in a web browser (View → Developer Tools → Network) and some ingenuity.”

事件概要:

  • デートアプリ「Tea」
  • 72,000件以上のユーザーデータが露出
  • 氏名、連絡先、プライベートな会話

発見方法:

  1. View(表示)
  2. Developer Tools(開発者ツール)
  3. Network(ネットワーク)

たった3クリックで、認証を完全にバイパスできました。


Wiz Researchの報告

Wiz Researchは、この問題について詳しく報告しています。

“A common flaw discovered is applications that handle the entire authentication process on the client side, without contacting any backend server. In these cases, the password is embedded directly in the JavaScript files and visible to anyone who inspects them.”

調査結果:

  • 多くのvibe-codedアプリがこのパターンを採用
  • パスワードがJavaScriptに直接埋め込まれている
  • 誰でも検査できる状態

実践的な解決策:サーバーサイド認証

正しい認証の実装方法を解説します。

実践的な解決策1: サーバーサイドで認証

正しいフロー:

1. ユーザーがログイン情報を入力
2. フロントエンドがサーバーに送信
3. サーバーがデータベースと照合
4. サーバーがセッション/トークンを発行
5. 以降のリクエストでトークンを検証

実装例(Express.js):

// サーバーサイド
app.post('/api/login', async (req, res) => {
  const { email, password } = req.body;
  
  // データベースからユーザーを取得
  const user = await db.users.findByEmail(email);
  if (!user) {
    return res.status(401).json({ error: 'Invalid credentials' });
  }
  
  // パスワードを検証(bcryptでハッシュ化されている)
  const isValid = await bcrypt.compare(password, user.passwordHash);
  if (!isValid) {
    return res.status(401).json({ error: 'Invalid credentials' });
  }
  
  // JWTを発行
  const token = jwt.sign({ userId: user.id }, process.env.JWT_SECRET, {
    expiresIn: '1h'
  });
  
  res.json({ token });
});

実践的な解決策2: すべてのAPIで認証を検証

// 認証ミドルウェア
function authMiddleware(req, res, next) {
  const token = req.headers.authorization?.split(' ')[1];
  
  if (!token) {
    return res.status(401).json({ error: 'No token provided' });
  }
  
  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    req.userId = decoded.userId;
    next();
  } catch (error) {
    return res.status(401).json({ error: 'Invalid token' });
  }
}

// 保護されたAPI
app.get('/api/user/profile', authMiddleware, async (req, res) => {
  const user = await db.users.findById(req.userId);
  res.json(user);
});

実践的な解決策3: 認可もサーバーサイドで

// ❌ クライアントサイドで権限チェック
if (user.role === 'admin') {
  showAdminPanel();
}

// ✅ サーバーサイドで権限チェック
app.get('/api/admin/users', authMiddleware, async (req, res) => {
  const user = await db.users.findById(req.userId);
  
  if (user.role !== 'admin') {
    return res.status(403).json({ error: 'Forbidden' });
  }
  
  const allUsers = await db.users.findAll();
  res.json(allUsers);
});

プロンプトによるセキュリティ対策

AIにログイン機能を依頼する際のプロンプト例です。

良いプロンプト:

ログイン機能を実装して:

## 必須要件
- 認証はサーバーサイドで実行
- パスワードはbcryptでハッシュ化
- JWTでセッション管理
- すべてのAPIで認証を検証
- 認可もサーバーサイドで実装

## AI開発で避けるべき禁止事項
- クライアントサイドでの認証処理
- JavaScriptへのパスワード埋め込み
- localStorageでの認証状態管理(トークン保存は可)

実装前チェックリスト

クライアントサイド認証を防ぐためのチェックリストです。

コードレビュー時:

  • 認証ロジックはサーバーサイドにあるか
  • パスワードがJavaScriptに埋め込まれていないか
  • すべてのAPIで認証が検証されているか
  • 権限チェックはサーバーサイドで行われているか

テスト時:

  • 開発者ツールでパスワードが見えないか
  • localStorageを操作して認証をバイパスできないか
  • APIを直接叩いてアクセスできないか

この事例から学ぶべき教訓と実践ポイント

「クライアントサイド認証」から学ぶべきことは以下の通りです。

  1. JavaScriptは丸見え

    • パスワードを埋め込むな
  2. クライアントは信用できない

    • すべてサーバーで検証
  3. 3クリックでバイパス可能

    • Tea事件の教訓
  4. 解決策は明確

    • サーバーサイド認証、JWT、ミドルウェア
  5. 認可もサーバーサイドで

    • 権限チェックもクライアントに任せるな

まとめ:重要ポイントの振り返り

  • クライアントサイド認証は 最悪のパターン
  • 3クリック でバイパス可能(Tea事件)
  • JavaScriptに埋め込まれたパスワードは 丸見え
  • クライアントは 信用できない
  • 解決策:サーバーサイド認証、JWT、認可もサーバーで
  • プロンプトで 「サーバーサイドで認証」を明示
  • 教訓:認証は絶対にサーバーサイドで

実践的なスキルを習得しませんか?

ブログで学んだ知識を、研修で実践に変えましょう。