忍者ブログ
[PR]
×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。



2024/11/23 05:15 |
PHP1-21:KittenAuthを実装してみる
Text_CAPTCHAは味気ないし対抗技術の進歩であんまり意味がなくなってきているということで次に有望視されているのがKittenAuthゲイツ認証なわけですが、Pearライブラリにはまだありません。
また、紹介やAPIを提供しているサイトはあれど、ソースや実装方法を公開しているところは何故かありません。

というわけで手っ取り早く作ってしまいましょう。
必要なのは沢山の画像。
今回は「みーたんの素材屋」のアイコン素材を使わせて頂きました。

簡単な作成方針としては、OKフォルダとNGフォルダから画像をいくつか選び、OKフォルダの画像であれば正しいと判断するというものです。
ただフォルダ名がOKとかNGのままでは正解がバレバレですので、一旦別のフォルダに移し、ファイル名もランダム化してから表示させることにします。

まずcatフォルダとcat_okフォルダ、cat_ngフォルダを作成し、そちらに各画像を配置します。
そして猫認証クラスを作成。

catAuth.class.php
<?php
    //初期設定
    define('CAT_BASE_DIR',dirname(__FILE__).'/');
    define('CAT_DIR',CAT_BASE_DIR.'cat/');
    define('CAT_OK_DIR',CAT_BASE_DIR.'cat_ok/');
    define('CAT_NG_DIR',CAT_BASE_DIR.'cat_ng/');
   
    class catAuthModel {
        //メンバ変数
        private $options=array();
        private $images=array();
       
        //コンストラクタ
        public function __construct(){
            //前回の画像削除
            $image_file=scandir(CAT_DIR);
            foreach($image_file as $val){
                if($val=='.' || $val=='..'){continue;}
                unlink(CAT_DIR.$val);
            }
           
            //デフォルト指定
            $this->options = array(
                'cat_ok'       => '1',
                'cat_ng'       => '3',
            );
        }
       
        //設定を上書きセット
        public function setOptions($options=array()){
            foreach($options as $key=>$val){
                $this->options[$key]=$val;
            }
            return true;
        }
       
        //出力用画像作成
        public function makeCats(){
            $this->makeOKCats();
            $this->makeNGCats();
            shuffle($this->images);
           
            return $this->images;
        }
       
        //OK画像作成ルーチン
        private function makeOKCats(){
            //OK画像一覧
            $cat_ok_dir=scandir(CAT_OK_DIR);
            foreach($cat_ok_dir as $key=>$val){
                if($val=='.' || $val=='..'){unset($cat_ok_dir[$key]);}
            }
            //OK画像選択
            if($this->options['cat_ok']==1){
                $cat_ok_key[]=array_rand($cat_ok_dir,$this->options['cat_ok']);
            }else{
                $cat_ok_key=array_rand($cat_ok_dir,$this->options['cat_ok']);
            }
            //OK画像コピー
            foreach($cat_ok_key as $val){
                $file_rand='ok_'.rand();
                copy(CAT_OK_DIR.$cat_ok_dir[$val],CAT_DIR.md5($file_rand.$cat_ok_dir[$val]));
                $this->images[]=array('flg'=>'1','filename'=>CAT_DIR.md5($file_rand.$cat_ok_dir[$val]));
            }
        }
       
        //NG画像作成ルーチン
        private function makeNGCats(){
            //NG画像一覧
            $cat_ng_dir=scandir(CAT_NG_DIR);
            foreach($cat_ng_dir as $key=>$val){
                if($val=='.' || $val=='..'){unset($cat_ng_dir[$key]);}
            }
            //NG画像選択
            if($this->options['cat_ng']==1){
                $cat_ng_key[]=array_rand($cat_ng_dir,$this->options['cat_ng']);
            }else{
                $cat_ng_key=array_rand($cat_ng_dir,$this->options['cat_ng']);
            }
            //NG画像コピー
            foreach($cat_ng_key as $val){
                $file_rand='ng_'.rand();
                copy(CAT_NG_DIR.$cat_ng_dir[$val],CAT_DIR.md5($file_rand.$cat_ng_dir[$val]));
                $this->images[]=array('flg'=>'0','filename'=>CAT_DIR.md5($file_rand.$cat_ng_dir[$val]));
            }
        }
       
    #↓クラスのおわり
    }


設定できるオプションはcat_okとcat_ngのふたつで、それぞれ正解画像の枚数と間違い画像の枚数となります。
makeOKCats()とmakeNGCats()は全く同じことをやってるので引数なりforeachなりで纏められるのは確定的に明らかなのですが、まあいいや。

array_randはエントリが複数の場合配列で返りますが、ひとつの場合は何故か配列ではなくキーだけが返ってくるので一々チェックして配列に揃える作業が必要です。


そのまま使用するには問題となる部分がいくつかあります。

CAT_BASE_DIRは実行環境にあわせて書き換える必要があります。
排他制御をしていないので、同時に複数のアクセスがあった場合どんな挙動をするかわかりません。
cat_okやcat_ngに存在する画像枚数より多い数を指定するとエラーになります。
0以下の値とか文字列とかを入れてもエラーになります。


次にこの猫認証クラスを表示する部分。

cat.php

<?php
    #初期設定
    session_start();
    header("Content-type: text/html; charset=utf-8");
   
    #引数があれば認証チェック
    if(isset($_REQUEST) && is_array($_SESSION['images'])){
        $error=0;
        foreach($_SESSION['images'] as $key=>$val){
            if($val['flg']==0 && isset($_REQUEST[$key])){$error=1;break;}
            if($val['flg']==1 && empty($_REQUEST[$key])){$error=1;break;}
        }
        if($error==1){print("認証失敗\n");}else{print("認証成功\n");}
    }
   
    #猫クラス
    require_once('./catAuth.class.php');
    $cat = new catAuthModel();
   
    #オプション変更方法
    //$cat->setOptions(array('cat_ok'=>'2','cat_ng'=>'4'));
   
    #認証用画像取得
    $_SESSION['images']=$cat->makeCats();
   
    #表示
    print('<form method="POST" action="'.htmlspecialchars($_SERVER["PHP_SELF"]).'">');
        foreach($_SESSION['images'] as $key=>$val){
            print('<input type="checkbox" id="'.$key.'" name="'.$key.'" value="1">');
            print('<label for="'.$key.'"><img src="'.$val['filename'].'"></label>'."\n");
        }
    print('<input type="submit" value="送信">');
    print('</form>');

    print("<pre>");var_dump($_SESSION['images']);
?>

<!--[if IE]><script type="text/javascript">
    window.onload=function(){
        var lbs = document.getElementsByTagName('label');
        for(var i=0;i<lbs.length;i++){
            var cimgs = lbs[i].getElementsByTagName('img');
            for(var j=0;j<cimgs.length;j++){
                cimgs[j].formCtrlId = lbs[i].htmlFor;
                cimgs[j].onclick = function(){document.getElementById(this.formCtrlId).click()};
            }
        }
    }
</script><![endif]-->


最後にくっついているJavaScriptは、IEではlabelタグでimageタグを括っても機能してくれないというバグの対処用です。
↓のスクリプトをそのまま使わせて頂きました。
http://www.nitoka.net/archives/2006/08/ieimglabel.html

このまま実装すると、ひとつだけチェックを入れて認証失敗した場合、リロードすると1/4の確率で認証成功してしまいます。
裏でワンタイムトークンを発行するなど別の対策が必要となります。
適当にセッションに突っ込めばいいだけですが、まあ所詮個人レベルですのでそこまでする必要もないでしょう。

エラー処理とか細かい部分は端折っていますが、とりあえず猫認証が完成しました
あとはレイアウトを揃えるなりJavaScriptで飾り立てるなりしてみてください。
 

PR


2008/08/27 14:04 | Comments(0) | TrackBack() | PHP

トラックバック

トラックバックURL:

コメント

コメントを投稿する






Vodafone絵文字 i-mode絵文字 Ezweb絵文字 (絵文字)



<<PHP1-22:祝日を取得 | HOME | PHP1-20:Pear::Text_CAPTCHA_Numeralのようなもの>>
忍者ブログ[PR]