0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PHP+GDで日本語CAPTCHAを作る:ひらがな+ノイズ線でBot対策

Last updated at Posted at 2025-04-20

背景

Alterlyに海外botからと思われるアクセスが多数あり、対策として日本語入力を求めるCAPTCHAを作成することとした。

やりたいこと

・PHP+GD(GD Graphics Library)で入力させる文字列を含む画像を生成する。
・日本語(ひらがな)を入力させる。
・OCR対策にノイズ(ランダムな数本の直線)を加える。

コード

<?php
session_start();

function displayRecaptcha() {
    //いるだけ文字列のパターンを。
    $charset = ['あ','い','う','え','お','か','き','く','け','こ'・・・・・・・・・・・];
    $text = '';
    for ($i = 0; $i < 5; $i++) {
        $text .= $charset[array_rand($charset)];
    }

    // セッションに格納
    $_SESSION['captcha_code'] = $text;

    // 画像生成
    $width = 180;
    $height = 60;
    $image = imagecreatetruecolor($width, $height);
    $bgColor = imagecolorallocate($image, 255, 255, 255);
    $textColor = imagecolorallocate($image, 20, 20, 20);
    $noiseColor = imagecolorallocate($image, 150, 150, 150);

    imagefilledrectangle($image, 0, 0, $width, $height, $bgColor);

    $fontPath = __DIR__ . '/font-sample-file.ttf'; 
    // 日本語表示可能なフォントを配置(例:Google FontsのNoto Sans JPなど)

    imagesetthickness($image, 3);

    // ノイズ線を追加
    for ($i = 0; $i < 5; $i++) {
        imageline(
            $image,
            rand(0, $width), rand(0, $height),
            rand(0, $width), rand(0, $height),
            $noiseColor
        );
    }

    // 文字描画
    for ($i = 0; $i < mb_strlen($text); $i++) {
        $char = mb_substr($text, $i, 1);
        $angle = rand(-25, 25);
        $x = 20 + $i * 30 + rand(-2, 2);
        $y = 40 + rand(-5, 5);

        imagettftext($image, 24, $angle, $x, $y, $textColor, $fontPath, $char);
    }

    ob_start();
    imagepng($image);
    $imageData = ob_get_clean();
    imagedestroy($image);

    return 'data:image/png;base64,' . base64_encode($imageData);
}
?>

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>日本語CAPTCHAテスト</title>
</head>
<body>
    <h1>日本語CAPTCHA テスト</h1>

    <?php
    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        $userInput = $_POST['captcha_input'] ?? '';
        $correct = $_SESSION['captcha_code'] ?? '';

        if ($userInput === $correct) {
            echo '<p style="color: green;">✅ 正解です!</p>';
        } else {
            echo '<p style="color: red;">❌ 間違っています。</p>';
        }

        // 再生成(同じ文字列の再送信防止)
        echo '<hr>';
    }

    $captcha = displayRecaptcha();
    ?>

    <form method="POST">
        <p>
            <img src="<?= $captcha ?>" alt="CAPTCHA" />
        </p>
        <p>
            表示されているひらがなを入力してください:<br>
            <input type="text" name="captcha_input" placeholder="例:あかさたな" required>
        </p>
        <p>
            <button type="submit">送信</button>
        </p>
    </form>
</body>
</html>

結果

こんな感じのフォームができます。
スクリーンショット 2025-04-20 16.26.09.png

※あくまで表示例です。実際にはフォームの形式やCSS等の調整が必要です。

OCRで読み取られないか?

↑の画像をChatGPTに投げて、読み取られるか試してみました。

結果は、「こさむけ」。

実際の表示は「ここおくし」なので、現時点(2025年4月20日)のChatGPT4oレベルのOCRであれば対策できている模様です。

※ただし、AI OCR技術は日々進化しており、今回の結果が将来的にも同様に有効とは限りません。定期的に文字数やノイズの量、角度、フォントを変えるなどの対策をおすすめします。
また、より精度を高めたいのであればreCAPTCHA(google公式提供のCAPTCHA機能)の導入をお勧めします。

まとめ

  • PHP+GDだけで日本語CAPTCHAを簡単に作れる
  • 海外Bot対策として日本語入力は有効
  • ChatGPT 4oレベルでも完全には読めないことを確認
  • 今後もAI技術の進化を見据え、定期的なメンテナンスが必要

参考

おまけ

今回紹介したCAPTCHAは、バグレポートツール Alterly でも実際に使われている仕組みです。
開発者の作業効率を大きく改善できるサービスなので、ぜひ一度触ってみてください!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?