忍者ブログ
[PR]
×

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



2017/03/27 21:39 |
PHP1-61:PHPでセッション書き換えその2

前回の続き。
Pear::HTTP_Session2の中を見てみると、session_set_save_handler()関数を使用しています。
http://jp.php.net/manual/ja/function.session-set-save-handler.php
このsession_set_save_handlerはsession_start()とか$_SESSIONとかの動作を自由に変更できるという素敵関数です。
早速使ってみましょう。

session_handler.class.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
<?php
    
class SessionHandler{
 
    //セッションテーブルの定義
        protected $session_table='user_session';
        protected $session_dsn=array(
            'server'=>'localhost'
            ,'database'=>'session'
            ,'table'=>'sessiondata'
            ,'user'=>'testuser'
            ,'pass'=>'testpass'
        );
    //セッション保持時間(秒)
        protected $session_gc=3600;
    
    //MySQLリソース
        protected $session='';
    
    //コンストラクタ
    public function __construct(){
        //セッション関連ini設定
            ini_set('session.gc_probability', 50);
            ini_set('session.save_handler', 'user');
        //セッションハンドラ
            session_set_save_handler(
                 array($this,'_admin_ses_open')
                ,array($this,'_admin_ses_close')
                ,array($this,'_admin_ses_read')
                ,array($this,'_admin_ses_write')
                ,array($this,'_admin_ses_destroy')
                ,array($this,'_admin_ses_gc')
            );
        //スクリプト終了時にセッション終了
            register_shutdown_function('session_write_close');
        //セッションスタート
            session_start();
        //セッションID変更
            return $this->_regenerateID();
    }
    
    //セッションスタート
    function _admin_ses_open($save_path, $session_name){
        //DB接続
            $this->session=mysql_connect(
                 $this->session_dsn['server']
                ,$this->session_dsn['user']
                ,$this->session_dsn['pass']
            );
            mysql_select_db($this->session_dsn['database'],$this->session);
        return true;
    }
    
    //セッションクローズ
    function _admin_ses_close(){
        return TRUE;
    }
    
    //セッション読み込み
    function _admin_ses_read($id){
        
        //SQL
            $sql ="SELECT * FROM ".$this->session_dsn['table'];
            $sql.=" WHERE id = '".mysql_real_escape_string($id)."'";
            $sql.=" AND expiry > UNIX_TIMESTAMP(now()) ";
            
            $ret =mysql_query($sql,$this->session);
        
        //結果返却
            if($ret && (mysql_num_rows($ret) == 1)){
                $tmp = mysql_fetch_assoc($ret);
                return $tmp['data'];
            }
            return '';
    }
    
    //セッション書き込み
    function _admin_ses_write($id, $session_data){
        //セッション破棄時間
            $expiry=time()+$this->session_gc;
        
        //SQL
            $sql ="REPLACE INTO ".$this->session_dsn['table'];
            $sql.=" (id, data, expiry) ";
            $sql.=" VALUES ( ";
            $sql.="'".mysql_real_escape_string($id)."'";
            $sql.=", '".mysql_real_escape_string($session_data)."'";
            $sql.=", '".mysql_real_escape_string($expiry)."'";
            $sql.=" ) ";
        //書き込み
        return mysql_query($sql,$this->session);
    }
    
    //セッション削除
    function _admin_ses_destroy($id){
        //SQL
            $sql ="DELETE FROM ".$this->session_dsn['table'];
            $sql.=" WHERE id = '".mysql_real_escape_string($id)."'";
        //実行
            return mysql_query($sql,$this->session);
    }
    
    //ガベージコレクション
    function _admin_ses_gc($maxlifetime){
        //本来はmaxlifetimeから有効期限を算出するが、
        //今回は書き込み時に有効期限を記入しているので不要
        //SQL
            $sql =" DELETE FROM ".$this->session_dsn['table'];
            $sql.=" WHERE expiry < UNIX_TIMESTAMP(NOW()) ";
        return mysql_query($sql,$this->session);
    }
    
    //セッションIDを毎回変更
    function _regenerateID(){
        //古いセッションID
            $session_id_old=session_id();
        //初めてなら変更不要
            if(!$session_id_old){
                return true;
            }
        //新しいセッションID
            session_regenerate_id(true);
            $session_id_new=session_id();
            return true;
        //セッション変更    //5.1.0以前でsession_regenerate_idにtrueが使えない場合
            //$sql =" UPDATE ".$this->session_dsn['table'];
            //$sql.=" SET id='".mysql_real_escape_string($session_id_new)."'";
            //$sql.=" WHERE id = '".mysql_real_escape_string($session_id_old)."'";
            //return mysql_query($sql,$this->session);
    }
    
#クラスのおわり
}

データベースは前回のを流用しています。

session_set_save_handlerの引数は、各状態の時に実行する関数名を与えるようになっています。
引数を配列で与えると、クラス名::ファンクション名を実行してくれます。
というわけで各関数を実装してsession_set_save_handlerを呼び出すと、セッションが始まったり終わったり値を入れたり出したりするときにそれらの各関数を実行してくれます。

SessionHandler::_regenerateIDは何をやっているかというと、SessionFixation対策です。

PHPのセッションは存在しないセッションIDがやってきた場合そのIDでセッションを始めてしまいます。
よって、攻撃者が誰かにindex.php?PHPSESSID=abcdefgというアドレスをメールで送りつけ、その人がアドレスをクリックしてログインしたりした場合、攻撃者はindex.php?PHPSESSID=abcdefgからログイン無しで内部にアクセスできてしまいます。
それを防止するため、session_regenerate_id()で毎回セッションIDを変更しています。
が、session_regenerate_idは古いセッションをそのままにしてしまいます。
古いセッションを削除するsession_regenerate_id(true)は5.1.0で搭載されたので、それ以前の場合は手動で削除しなければいけません。

ちなみにリンク先では
>このような動作をする理由がいまいち把握しきれていないのだが、これは明らかにアプリケーションの問題だろう。
とか書いてありますが、PHPの場合は仕様です。

さて、このクラスの使用方法はnewするだけです。
特に引数や返り値は要りません。

session_handler.php
1
2
3
4
5
6
7
8
<?php
    
    //セッション変更
        require_once('session_handler.class.php');
        new SessionHandler();
    
    //以降普通にセッションが使える
        $_SESSION['a']='b';


以後普通にセッションがDBに書き込まれます。
汎用的に使い回し対場合はDSNなんかを引数にすればいいかもしれません。
ちなみに全くどうでもいいのですが、PHPのインスタンス変数の初期値には固定値が使えません。
protected $session_gc=60*60;とか書くと怒られます。

 

PR


2009/03/16 18:40 | Comments(0) | TrackBack(0) | PHP

トラックバック

トラックバックURL:

コメント

コメントを投稿する






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



<<JavaScript2-9:うにょーんと左右に動く画像ビューア | HOME | PHP1-60:PHPでセッション書き換え>>
忍者ブログ[PR]