PHPでBOT対策用の画像認証を作成する

PHPでBOT対策用の画像認証を作成する


まずは試してみる

試してみたい場合は、以下URLをクリック サンプルサイト


PHPのGD2が有効でなければなりません

PHPのphp.iniファイルを編集して拡張機能「GD2」を有効にしてください。
;Linuxの場合
extension=gd2
;Windowsの場合
extension=php_gd2.dll

TrueTypeFontファイルが必要

TrueTypeFontファイルが必要です。
ipaフォント(https://moji.or.jp/ipafont/ipaex00401/)等の無料のフォントをライセンスに準拠してダウンロードして使用しましょう


ソースコード

画像表示と入力フォーム

php_auth_image.php
<?php
  /* php_auth_image.php */
  include("./mam_auth_image.php");
  [$auth_char,$auth_img]=getCharImg();
  //セッションを開始
  session_start();
  //セッション変数に正解の文字列を入れておき、
  //POST後URLで入力値とPOST値を比較して認証する
  $_SESSION["img_auth_char"]=$auth_char;
  if(isset($_GET["result"])&&$_GET["result"]=="failed"){$err="認証失敗<br>";}else{$err="";}
?>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>BOT対策用画像認証入力画面</title>
</head>
<body style="margin:0;background-color:#ffffff">
  <form method="post" action="php_auth_image_confirm.php">
    <?php if(isset($err)){echo "<p style='color:red;'>{$err}</p>";}?>
    <img id="char" src="<?=$auth_img;?>"><br>
    上記画像に表示された数字を入力してください。<br>
    <input type="text" maxlength="10" size="20" style="ime-mode:disabled;" name="input_char"><br>
    <input type="submit" value="認証">
  </form>
</body>
</html>

認証する画面

php_auth_image_confirm.php
<?php
  /* php_auth_image_confirm.php */

  //セッションを開始
  session_start();

  //セッション変数の正解の文字列を取得
  if(isset($_SESSION["img_auth_char"])){$auth_char=$_SESSION["img_auth_char"];}else{$auth_char='';}

  //postされた文字列を取得
  if(!empty($auth_char)&&isset($_POST["input_char"])){$input_char=$_POST["input_char"];}else{$input_char="";}

  //認証の検証
  if($input_char==$auth_char){$auth=true;}else{$auth=false;}
  if($auth==false){header("location: ./php_auth_image.php?result=failed");}
?>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>BOT対策用画像認証結果</title>
</head>
<body style="margin:0;background-color:#ffffff">
  認証しました
</body>
</html>

認証画像を生成する関数

mam_auth_image.php
<?php
/* mam_auth_image.php */

//TrueTypeFontファイルのパスを指定する。
//ipaフォント(https://moji.or.jp/ipafont/ipaex00401/)を
//ライセンスに準拠してダウンロードして使用しましょう
define("TrueTypeFontPath",__DIR__."/ipaexg.ttf");
define("ImageHeight",80);//出力される画像の高さ(px)

function getCharImg(){
  //認証に使用する文字一覧を指定

  //半角数字の場合
  //1,7は回転すると分かりにくいのでコメントアウト
  $carr=[/*"1",*/"2","3","4","5","6",/*"7",*/"8","9","0",];

  //半角大文字アルファベットの場合 I,Lはコメントアウト
  //$carr=[
  //"A","B","C","D","E","F","G","H",/*"I",*/"J","K",/*"L",*/
  //"M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"
  //];

  //全角ひらがなの場合
  //「く,て,へ」は回転すると判別しにくいのでコメントアウト
  //$carr=[
  //"あ","い","う","え","お","か","き",/*"く",*/"け","こ",
  //"さ","し","す","せ","そ","た","ち","つ",/*"て",*/"と",
  //"な","に","ぬ","ね","の","は","ひ","ふ",/*"へ",*/"ほ",
  //"ま","み","む","め","も","や","ゆ","よ",
  //"ら","り","る","れ","ろ","わ","を","ん",
  //];

  $cnum=random_int(6,6);//認証に使用する文字数を指定、とりあえず6桁固定
  $cc='';
  $c=[];
  for($i=0;$i<$cnum;$i++){
      $c[$i]=[];
      $c[$i]['chr']=$carr[random_int(0,count($carr)-1)];
      $cc.=$c[$i]['chr'];
  }

  $lt=floor(ImageHeight*0.1);//総画像幅の計算
  $img=imagecreatetruecolor(100,100);
  $dpi=96;//解像度の設定
  imageresolution($img, $dpi, $dpi);
  $pt= floor(ImageHeight*0.6*72/$dpi);
  $col=imagecolorallocate($img,0,0,0);
  for($i=0;$i<$cnum;$i++){
    //回転角の設定
    $c[$i]['r']=random_int(-45,45);
    $arr=imagettftext(
      $img,$pt,
      $c[$i]['r'],0,0,
      $col,TrueTypeFontPath,$c[$i]['chr']
    );
    $xmin=min($arr[0],$arr[2],$arr[4],$arr[6]);
    $xmax=max($arr[0],$arr[2],$arr[4],$arr[6]);
    $ymin=min($arr[1],$arr[3],$arr[5],$arr[7]);
    $ymax=max($arr[1],$arr[3],$arr[5],$arr[7]);
    $c[$i]['x']=$lt-round($xmin*$dpi/72);
    $char_w=($xmax-$xmin)*$dpi/72;
    $lt=round($c[$i]['x']+$char_w);
    //若干文字をかぶせる
    $lt-=random_int($char_w*0.1,$char_w*0.4);
    $c[$i]['y']=random_int(floor(ImageHeight*0.1),floor(ImageHeight*0.3))-$ymin;
  }
  $lt+=floor(ImageHeight*0.1);//総画像幅の計算
  imagedestroy($img);
  $im=imagecreatetruecolor($lt,ImageHeight);
  imageresolution($im, $dpi, $dpi);

  $tranColor = imagecolorallocate($im, 254, 253, 252);//背景色セット
  imagefill($im, 0, 0, $tranColor);     //背景を塗る
  imagecolortransparent($im,$tranColor);//透明化
  //背景にランダムな四角形を描く
  $rnd=random_int(40,50);
  for($i=0;$i<$rnd;$i++){
    $col=imagecolorallocate($im,random_int(192,255),random_int(192,255),random_int(192,255));
    $x1=random_int(0,$lt);
    $y1=random_int(0,ImageHeight);
    $x2=random_int($x1,$lt);
    $y2=random_int($y1,ImageHeight);
    imagefilledrectangle($im, $x1, $y1, $x2, $y2, $col);
  }
  //変形文字を描画する
  for($i=0;$i<$cnum;$i++){
    $col = imagecolorallocate($im, random_int(0,80), random_int(0,80), random_int(0,80));//色
    imagettftext(
      $im, $pt,
      $c[$i]['r'],$c[$i]['x'],$c[$i]['y'],
      $col, TrueTypeFontPath, $c[$i]['chr']
    );
  }
  //点ノイズの描画
  $rnd=random_int(ImageHeight*6,ImageHeight*8);
  for($i=0;$i<$rnd;$i++){
    $col=imagecolorallocate($im,random_int(0,80),random_int(0,80),random_int(0,80));
    imagesetpixel($im,random_int(0,$lt),random_int(0,ImageHeight),$col);
  }
  //線ノイズの描画
  $rnd=random_int(round(ImageHeight*0.8),round(ImageHeight*0.9));
  for($i=0;$i<$rnd;$i++){
    $col=imagecolorallocate($im,random_int(0,100),random_int(0,100),random_int(0,100));
    imageline($im,random_int(0,$lt),random_int(0,ImageHeight),random_int(0,$lt),random_int(0,ImageHeight),$col);
  }
  //出力のバッファリングからdata URIを取得
  ob_start();
  imagejpeg($im,NULL,30);
  $con = base64_encode(ob_get_contents());
  ob_end_clean();
  imagedestroy($im);
  $src="data:image/jpeg;base64,".$con;
  
  //わざと少し待つ
  usleep(500000);
  
  //正解の文字列と、画像の配列を返す
  return [$cc,$src];
}


Mam's WebSite