sessionファイルの生成場所、中身、有効期限

sessionファイルの場所
1. php.iniのsession.save_path
2. php.iniのsys_temp_dir
3. /tmp

今回ubuntuの場所では、/var/lib/php/sessionsに格納されていた。
$ sudo cat sess_t8mk08vtmj3vh29s8rhq4tsh22
hoge|s:4:”hoge”;fuga|s:4:”fuga”;
【セッション名】|s:【セッションのID】:【値】で保存される

session_encodeという関数でエンコードしている
なるほど、基礎的な挙動は理解した。

セッションをCookieでもつか、サーバ側のファイルで持つか

### Cookieで持つ場合
■メリット
サーバにデータをため込まないので、サーバサイドの負荷を気にしないで良い
アプリケーションサーバを分散させても一貫性を持たせられる
■デメリット
ユーザ側にデータがあるため、改ざんリスクが高まる(Railsは暗号化によって対策してますが)
Cookieの仕様上、4KBの容量制限がある
Cookieを乗っ取られた場合等でもサーバサイドからセッションを破棄する手段がない
ユーザがcookieを有効にしていない場合、ログイン情報などがうまく保存されない

### サーバ側でファイルで持つ場合
CakePHP、Laravelのでファオルトで、CookieにセッションIDだけ持たせて、サーバサイドに実データをファイルとして置く方法
■メリット
サーバサイドにデータがあるので改ざんを回避できる
データ容量制限がない
サーバサイドでセッションの破棄ができる
■デメリット
アプリケーションサーバを分散させた場合、一貫性が確保できない
1セッションにつき1ファイル生成されるので、大量にセッションファイルが出来てファイルシステムに負担が掛かる

### KVS(NoSQL)
CookieにセッションIDだけ持たせて、Memcached、RedisなどサーバサイドのKVSに実データを置く方法。
■メリット
– サーバサイドにデータがあるので改ざんを回避できる
– データ容量制限がない
– サーバサイドでセッションの破棄ができる
– アプリケーションサーバを分散させても一貫性を持たせられる
– 大量の同時I/Oに強く、アクセスが増大しても大丈夫!
■デメリット
セッション専用にひとつデータベースが追加されるのでコストが増
https://qiita.com/SuguruOoki/items/6ca36ad1d366df6c98af

コストに問題なければredis(NoSQL)が良さそうではある

セッション情報をデータベースで管理するサンプル

create database test;
use test;
create table TBL_SESSION (
SESSION_ID varchar(50) NOT NULL,
SESSION_DATA text,
CRA¥EATE_DATE int(10),
PRIMARY KEY (SESSION_ID));

class MySessionHandler implements SessionHandlerInterface {

	function close(){
		return true;
	}

	function destroy($session_id) {
		$db = get_db();
		if($stmt = $db->prepare("DELETE FROM TBL_SESSION WHERE SESSION_ID = ?")){
			$stmt->bind_param("s", $session_id);
			$stmt->execute();
			$stmt->close();
			$stmt = null;
		}
		$db->close();
		$db = null;
		return true;
	}

	function open($save_path, $name) {
		return true;
	}

	function read($session_id) {
		$session_data = "";
		$db = get_db();
		if($stmt = $db->prepare("SELECT SESSION_DATA FROM TBL_SESSION WHERE SESSION_ID = ?")){
			$stmt->bind_param("s", $session_id);
			$stmt->bind_result($session_data);
			$stmt->execute();
			$stmt->fetch();
			$stmt->close();
			$stmt = null;
		}
		$db->close();
		$db = null;

		if(is_null($session_data)){
			$session_data = "";
		}
		return $session_data;
	}

	function write($session_id, $sesion_data){
		$affect_rows = 0;
		$create_date = time();
		$db = get_db();
		if($stmt = $db->prepare("INSERT INTO TBL_SESSION (SESSION_ID, SESSION_DATA, CREATE_DATE) VALUES(?, ?, ?) ON DUPLICATE KEY UPDATE SESSION_DATA = ?, CREATE_DATE = ?")){
			$stmt->bind_param("ssisi", $session_id, $session_data, $create_date, $session_data, $create_date);
			$stmt->execute();
			$affected_rows = $stmt->affected_rows;
			$stmt->close();
			$stmt = null;
		}
		$db->close();
		$db = null;
		return $affected_rows ? true : false;
	}
}

function get_db(){
	return new mysqli('localhost:3306', 'user', 'password', 'db_name');
}

session_set_save_handler(new MySessionHandler(), true);

session_start();

$_SESSION['data'] = 0;

Railsは全てのセッション情報をcookieに保存

ログインにおけるセッション管理

<body>
	<form action="login.php" method="post">
		ユーザID: <input type="text" name="userid"/><br>
		パスワード: <input type="password" name="password"/>
		<input type="submit" name="ログイン"/>
	</form>
</body>
session_start();

$userId = $_POST['userid'];

if(!isset($_SESSION[$userId])){
	$_SESSION[$userId] = $userId;
	echo "ログインしました。";
} else {
	echo "ログイン済みです。";
}

なるほど

### セッション情報の保管場所

<?php

phpinfo();

session.save_path /var/lib/php/sessions
セッション情報の場所

cat sess_vikafie7r8km1288sdkn1j5sat
username|s:4:”sato”;count|i:3;hello|s:5:”hello”;

php.iniで指定した場所にセッションファイルが保存される
冗長化の場合は、別に保存する

セッションとは?

– セッションとは、通信を開始してから終了するまで
– セッションとはサーバ側にデータを保存する仕組み

### セッションとクッキーの違い
セッションはサーバにデータを保存するのに対し、クッキーはブラウザ側に保存する
ブラウz側に保存されているデータはユーザ側で内容を書き換えられる
セッションに格納する場合はサーバ側にファイルが作成される
セッションIDはクッキーで保存されている

<?php

session_start();

PHPSESSIDという値でcookieに保存される

セッション情報の取得

<?php

print("セッションIDは".$_COOKIE['PHPSESSID']. ".");

### セッション情報の書き込みと削除

session_start();
print("セッションIDは".$_COOKIE['PHPSESSID']. ".");

$_SESSION['username'] = 'sato';
echo 'ユーザ名は'. $_SESSION['username']. '.';

unset($_SESSION['username']);

if(!isset($_SESSION['username'])){
	echo 'ユーザ名は削除されました';
} else {
	echo 'ユーザ名は'. $_SESSION['username']. '.';
}

セッションはブラウザを閉じるまで保存
unset($_SESSION[‘username’]); でセッションを削除
セッションIDがブラウザに保存される 
通常はランダムのセッションIDを発行する

クッキーは管理したい値をクライアント側で管理するが、セッションはサーバ側で管理する(セッションIDのみクライアント側で管理)
セッションのデフォルト値はphp.iniのsession.cookie_lifetimeに保存されている値

有効期限を上書きできる

session_start([
	'cookie_lifetime' => $300,
]);

なるほど、sessionの値がどこに管理されているかわからんが… なんとなく基礎的なことはわかった

laravel6.x : ELB使用時にElastiCache(Redis)でセッション管理する

AWSのELBで冗長化する場合、TokenMismatchExceptionが発生しないよう、セッション管理をデフォルトのCookieからRedisに変更する。
ALBでStickinessを有効化する事で、Cookieのままでも機能的にはおよそ問題ないとも言えるが、
「インスタンスが死んだ場合にはセッションが切れる」「負荷分散が偏る場合が発生する」とのことで、
要件次第のところもあるが、基本的にはスティッキークッキーではなく、ElastiCacheでの管理に変更する。

### 1.Redis用のsubnet作成
Services->Database->ElastiCache->SubnetGroup
Name:${appName}-redis-subnet-group
Description:${appName}-redis-subnet-group
VPC:dev-vpc
Subnet:dev-subnet-private1(ap-northeast-1a)、dev-subnet-private2(ap-northeast-1c)

### 2.Redis用のSecurityGroup作成
name:redis-security-group
description:redis-security-group
VPC:dev-vpc

Edit Inbound Rule
Custom TCP, 6379port, PRDのSecurityGroup

### 3.Redisの作成
Name: ${appName}-redis
Engine version compatibility: 5.0.6
Port:6379
Parameter group:default.redis5.0
nodetype: t2.medium
replica:0
subnet group: ${appName}-redis-subnet-group
security group: redis-security-group
backup: null

### 4. Laravelでredisを使えるようにする
$ ssh ec2-user@*** -i ~/.ssh/***.pem
$ cd /var/www/${appName}

// predisを入れる為、メモリ拡張
$ sudo /bin/dd if=/dev/zero of=/var/swap.1 bs=1M count=1024
$ sudo /sbin/mkswap /var/swap.1
$ sudo /sbin/swapon /var/swap.1
$ free

$ sudo php composer.phar require predis/predis

$ sudo vi .env

CACHE_DRIVER=redis
SESSION_DRIVER=redis
REDIS_HOST=${redis endpoint}

$ php artisan config:clear
$ chown apache:apache /var/www/${appName}/storage/logs/laravel.log

で、動作確認すると、

please make sure the php redis extension is installed and enabled

何いいいいいいいいいいいいいい
「sudo yum install php-redis」でphp-redis入れろって事?

あ、
config/database/php
122行目
phpredis -> predis

'redis' => [

        'client' => env('REDIS_CLIENT', 'predis'),

再度動作確認

php.ini session.save_handler

/etc/php.ini

[Session]
; Handler used to store/retrieve data.
; http://php.net/session.save-handler
session.save_handler = files

session.save_hanlder defines the hanlder to use when saving and retrieving data related to the session. The default is files. Npte that each extension can use its own save_handler.

先ほどElastiCacheで作成したmemcacheのエンドポイントをphp.iniに設定する

;session.save_handler = files
;session.save_path = "/tmp"
session.save_handler = "memcached"
session.save_path = "php-session.xxxxx.cfg.apne1.cache.amazonaws.com:11211""

続いて動作確認

session_start();
echo "This is Web Server 1<br>";

if (isset($_SESSION["username"])){
	echo $_SESSION["username"];
} else {
	$_SESSION["username"] = "hoge";
}

なるほど、sessionを共有する両方のphp.iniファイルで、エンドポイントを指定して共有しているのね。

$m = new Memcached();
$m->addServer('localhost', 11211);
$m->set('foo', 'var',60);
var_dump($m->get('foo'));
$m->add('hoge', 'fuga', 60);
$m->add('hoge', 'piyo', 60);
var_dump($m->get('hoge'));
$m->flush();
var_dump($m->get('foo'));

セッション作成とセッションインスタンス取得

session_start();
$_SESSION['key']="orange";
echo $_SESSION['key'];

うん、ここらへんは基本ですね。

セッション削除はunset

session_start();
$_SESSION['key']="orange";
echo $_SESSION['key'];

unset($_SESSION['key']);
echo $_SESSION['key'];

[Mon Apr 22 23:18:28 2019] PHP Notice: Undefined index: key in /home/vagrant/local/app/test/index.php on line 8

まあ、この辺の応用でしょうね。

session

A session is a sequence of action taken by accessing a Web site.
One session from accessing the website to leaving the site or closing the browser.

The web server has a unique ID for each session, even if it is a Web system that spans multiple pages by using this.
It is possible to save and keep information for each user.

The most typical way to use this session is a web page. The most commonly used function is login/logout.

Think of one session from logging in to logging out. In the meantime, save status such as user ID and password.

sessionがsetされていた時は、そのままsessionIDを使う

sessionは、リクエスト毎に再setしますが、IDだけは、json.parseした値を再度setします。

var unix = Math.floor((new Date).getTime()/1E3);
var visit = "New User";
var char = "abcdefghijklmnopqrstuvwxyz123456789";
        var id = "";
        for(var i=0; i<10; i++){
            id += char[Math.floor(Math.random()*35)];
        }
if(navigator.cookieEnabled){
    var all=document.cookie + ";";
    var cStart = all.indexOf("_na=",0);
    if(cStart == -1){   
        var expire = new Date();
        expire.setTime(expire.getTime() + 1000*3600*24*365*2);
        document.cookie="_na=NA1."+id+"."+unix+";expires=" + expire.toUTCString();
        var data = '{"path":"'+location.pathname+'", "time":"'+unix+'","id":"'+id+'"}';
        window.sessionStorage.setItem(['ScribeTransport'],[data]);
        a.push(['cookie',id]);
        a.push(['vist',visit]);
        a.push(['referrer',document.referrer]);
    } else {
        visit = "Returned User";
        a.push(['visit',visit]);
        var cEnd = all.indexOf(";",cStart);
		var cID = all.substring(cStart+8,cEnd - 11); //_na=NA1.0000000000.
		a.push(['cookie',cID]);
        var c = window.sessionStorage.getItem(['ScribeTransport']);
        if(c){
            d = JSON.parse(c);
            var data = '{"path":"'+location.pathname+'", "time":"'+unix+'","id":"'+d.id+'"}';
            window.sessionStorage.setItem(['ScribeTransport'],[data]);
            a.push(['referrer',d.path]);
            a.push(['avgtime',(unix - d.time)]);        
        }else {
            var data = '{"path":"'+location.pathname+'", "time":"'+unix+'","id":"'+id+'"}';
            window.sessionStorage.setItem(['ScribeTransport'],[data]);
        }       
    }
}

a.push(['page',location.pathname]);
$(function(){
    $(document).ready(function(){
            var postData = {"userdata": a};
            $.post(
                "doubleclick.php",
                 postData,
                 function(data){
                    $(".box").html(data);
                }
            );
        });
});

OK!