勉強したことのメモ

webプログラマが勉強したことのメモ。

PHPでログインフォームにひらがな認証を設置する方法

      2022/03/31

ログインフォームに平仮名認証を設置したい。英数字認証だとSecurimageというライブラリがあるようだけど、こちらは日本語は使用不可っぽい。参考サイトがやりたいことドンピシャなものの、そのまま設置しただけでは動かなかったので色々調整したところ動作した。以下に対応方法をメモ。

 

サンプル

https://taitan916.info/sample/captcha_kana/

 

準備

適当に数種類フォントファイルを用意し、フォームを設置するディレクトリにアップロードしておく。今回は以下からダウンロードした。

https://ja.osdn.net/projects/ipafonts/releases/49986

 

ひらがな認証画像作成用プログラム(create_captcha.php)

<?php
// セッション開始
session_start();	

//文字化け防止に、言語と文字コードを明示的に指定。
mb_language("Japanese");
mb_internal_encoding("UTF-8");

// 設定項目
define("LOGIN_ENABLE_SEC", 300);// 表示した画像でログインを許可する期間(秒)
define("IMAGE_SIZE_X",150);// 回転前の画像サイズ(横)
define("IMAGE_SIZE_Y", 80);// 回転前の画像サイズ(縦)
define("STRING_NUM_MIN", 5);// 表示する最低文字数
define("STRING_NUM_MAX", 6);// 表示する最大文字数
define("FONT_SIZE_MIN", 12);// 最低フォントサイズ
define("FONT_SIZE_MAX", 20);// 最大フォントサイズ		
define("CHAR_ANGLE", 15);// 文字の傾き角度の範囲
define("IMAGE_TYPE", "jpeg");// "jpeg" or "png"

//フォントの設置場所
$font_path = '/var/www/html/font/';

//フォント
$arr_font = array("{$font_path}ipag.ttf","{$font_path}ipagp.ttf","{$font_path}ipam.ttf","{$font_path}ipamp.ttf");

// 画像に表示する文字列を作る(ひらがなのみ)
// カタカナや平易な漢字を追加するのもあり
$s = "あいうえおかきくけこさしすせそたちつてとなにぬねのはひふへほまみむめもやゆよらりるれろわをんがぎぐげござじずぜぞだぢづでど";

// 乱数のシードを設定する
mt_srand(hexdec(bin2hex(openssl_random_pseudo_bytes(4))) );

// ランダム文字列を生成
$string = "";
for($i=0; $i<mt_rand(STRING_NUM_MIN, STRING_NUM_MAX); $i++) {
    $string .= mb_substr($s,mt_rand(0,mb_strlen($s)-1),1);
}

//画像表示した文字列(正解)をセッション変数に入れる
 $_SESSION['captcha_auth'] = $string;

// 画像サイズを指定して、画像オブジェクトを生成
$im = imagecreate(IMAGE_SIZE_X, IMAGE_SIZE_Y);
// 背景色を指定(白色)
imagecolorallocate($im, mt_rand(100,255), mt_rand(100,255), mt_rand(100,255));
$black = imagecolorallocate($im, 0, 0, 0);
$white= imagecolorallocate($im, 255, 255, 255);
$gray  = imagecolorallocate($im, 196, 196, 196);
    
    
// 可視性を低くさせるために、文字数の2倍のダミーの線を入れる
for($i=0; $i<mb_strlen($string)*2; $i++) {
    imageline($im, mt_rand(0,IMAGE_SIZE_X - 1),mt_rand(0,IMAGE_SIZE_Y - 1),mt_rand(0,IMAGE_SIZE_X - 1),mt_rand(0,IMAGE_SIZE_Y - 1), $gray);
}

//ユーザに入力させるための文字を表示
for($i=0; $i<mb_strlen($string); $i++) {
    

    //横軸は順番があるので、文字順にある程度決まっている。
    $x = (IMAGE_SIZE_X / mb_strlen($string)) * $i + mt_rand(-3,3);
    // 縦軸は、中心部分を基準にランダムに表示
    $y = IMAGE_SIZE_Y/2 + mt_rand(-IMAGE_SIZE_Y/4 , IMAGE_SIZE_Y/4);
    // 文字の傾き
    $r = mt_rand(-CHAR_ANGLE, CHAR_ANGLE);	
    
    // 1文字ずつ出力
    imagettftext(
        $im, 									// 文字を書く画像
        mt_rand(FONT_SIZE_MIN, FONT_SIZE_MAX), 	// 文字のフォントサイズ
        $r, 									// 文字の角度
        $x, $y, 								// 文字の座標
        imagecolorallocate(						// 文字の色(ほぼ黒だが、わずかに揺らぎを発生させている)
            $im, 
            mt_rand(0,3), 
            mt_rand(0,3), 
            mt_rand(0,3)), 
        $arr_font[mt_rand(0,count($arr_font)-1)], 	//文字のフォントを指定
        mb_substr(mb_convert_encoding($string, "UTF-8"), $i, 1, "UTF-8")	//書き出す文字そのもの
    );
}


// 可視性を低くさせるために、わずかに画像を回転させる
$im = imagerotate($im, mt_rand(-2,2), $white);

//画像形式に合わせてヘッダ出力
if (IMAGE_TYPE == "jpeg") {
    header('Content-type: image/jpeg');
    imagejpeg($im);
} else {
    header('Content-type: image/png');
    imagepng($im);
}

//最後にリソース廃棄
imagedestroy($im);	
?>

21行目の「$font_path」は適宜変更する。尚、フォントファイルをアップロードしていなかったり、「$font_path」が相対パスの場合は「Warning: Could not find/open font」というエラーが発生するので注意。

 

フォームページ(index.php)

<?php
session_start();

//文字化け防止に
mb_language("Japanese");
mb_internal_encoding("UTF-8");

if( $_POST['captcha_text'] ){ 
    if($_SESSION['captcha_auth'] === $_POST['captcha_text']){
        echo "OK!!";
    }else{
        echo "NG!!";
    }
}
?>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<title>画像認証</title>
</head>
<body>

    <img src="./create_captcha.php" alt="captcha_image" title="captcha_image" id="captcha_image" />
    <form id="captcha_form" name="captcha_form" method="post" action="./">
        <input type="text"  id="captcha_text" name="captcha_text">
        <button type="button" id="reload">リロード</button>
        <button type="submit" value="送信">送信</button>
    </form>

    <script type="text/javascript" src="//code.jquery.com/jquery-3.5.1.js"></script>
    <script>
    $(function(){
        $('#reload').click(function(){
            $('#captcha_image').attr('src', './create_captcha.php?t=' + $.now());
        });
    });
    </script>

</body>
</html>

参考サイトと異なる部分は以下2点。

  • リロードを押した際にページリロードはさせず、ひらがな認証画像部分のみリロードするように変更
  • 送信時に別ページに送信せず自身のページに送信し、認証の結果を表示するよう変更

所感

Google reCAPTCHAを導入する場合、クライアントさんにサイトキー、シークレットキーを発行してもらわないといけないが、このひらがな認証だとすぐ設置できて楽かも。

 

参考サイト

PHPで、ひらがなCAPTCHA(画像認証)を自作してみた。

 - PHP

  関連記事

ディレクトリに設置してあるフォルダをPHPで調べて表示

やりたい事はsampleというディレクトリがあったとして、その直下にあるフォルダ ...

フォルダにリンク制限をかける

imgというフォルダがあり、直接URLを叩いても 中身を見られないけど、同一サー ...

PHPでのcookie

aaa.comにログインフォームがあって、IDパスを入れて ログインするとbbb ...

PHPでCSVファイルを作って開くと「SYLKファイルが云々」のアラートが出た

データベースのログを整形してCSVファイルを生成し、ダウンロード及びエクセルで開 ...

同一サーバの別ディレクトリでセッション振り分け

同じサーバ内にmemberとownerの別ディレクトリがあり、それぞれにsess ...

PHPを使えないサーバから使えるサーバーに値を渡す方法

PHPが使えないサーバーから使えるサーバーに値を渡したい場合があった。ajaxと ...

PHPでのファイル関係

ファイルを呼び出したり書き込んだりとかしばしば行うが、 未だに調べることが多いの ...

PHPでベーシック認証をかける方法

PHPでBasic認証をかけているソースを拝見する機会があり、衝撃だったのでメモ ...

PHPで値が空の配列を削除

やりたかった事は配列でキーは入っていて値が入っていない ものを削除したかった。 ...

GoogleスプレッドシートとPHPの連携

GoogleスプレッドシートとPHPプログラムを連携させたいという案件をたまに見 ...