背景
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>
結果
※あくまで表示例です。実際にはフォームの形式やCSS等の調整が必要です。
OCRで読み取られないか?
↑の画像をChatGPTに投げて、読み取られるか試してみました。
結果は、「こさむけ」。
実際の表示は「ここおくし」なので、現時点(2025年4月20日)のChatGPT4oレベルのOCRであれば対策できている模様です。
※ただし、AI OCR技術は日々進化しており、今回の結果が将来的にも同様に有効とは限りません。定期的に文字数やノイズの量、角度、フォントを変えるなどの対策をおすすめします。
また、より精度を高めたいのであればreCAPTCHA(google公式提供のCAPTCHA機能)の導入をお勧めします。
まとめ
- PHP+GDだけで日本語CAPTCHAを簡単に作れる
- 海外Bot対策として日本語入力は有効
- ChatGPT 4oレベルでも完全には読めないことを確認
- 今後もAI技術の進化を見据え、定期的なメンテナンスが必要
参考
-
PHP公式マニュアル - GD ライブラリ
画像生成・描画処理に関する公式ドキュメント。 -
imagettftext - PHP公式関数リファレンス
TrueTypeフォントを使用してテキストを描画する関数。 -
reCAPTCHA v3(Google公式)
Bot対策として広く使われるGoogle提供のCAPTCHAサービス。
おまけ
今回紹介したCAPTCHAは、バグレポートツール Alterly でも実際に使われている仕組みです。
開発者の作業効率を大きく改善できるサービスなので、ぜひ一度触ってみてください!