My Photo

« July 2014 | Main | September 2014 »

August 31, 2014

PHPメモ043:セッションの有効期間の設定

PHPで「セッションの有効期間」というと、「サーバが持つセッションデータそのもの有効期間」と「セッションクッキーの有効期間」(セッションIDをクッキーでやり取りする場合)がある。
アプリケーションの仕様として「何分で確実にセッションが期限切れになってほしい」って場合、セッションクッキーの設定をすればいい。クッキーが使えないガラケー向けのWEBアプリなどではクエリストリングでセッションIDをやりとしているのでセッションクッキーの設定は無意味だが、そういうガラケー向けWEBアプリの開発は今後はなさそうなので考えない事にする。
また、スマホ向けWEBアプリでHTML5を使う場合はクッキーの代わりになる「Web Storage」なるものがあるらしいが、それはまた別の機会に。
#スマホは機種によってクッキー関連が想定どおり動かなくてすごく困ることがある;;

セッションデータの有効期間

セッションデータそのもの有効期間は、php.ini の以下の3つの設定値で決まる。php.ini の設定値なのでアプリケーション毎ではなくサーバ毎の設定になる。

  • session.gc_maxlifetime
  • session.gc_probability
  • session.gc_divisor

session.gc_maxlifetime で指定した秒数を経過したセッションデータは「ゴミ」とみなされ、セッションの初期化の際(session_start() が呼ばれた時?)に (session.gc_probability/session.gc_divisor) の確率でgc(ガーベッジコネクション)プロセスが起動して削除される。
「指定した秒数を経過した」というのは厳密にはどういう意味なのか。セッションデータのファイルの最終更新日時から session.gc_maxlifetime の秒数を経過したらゴミ認定と理解しているが、自信はない。
また、ゴミ認定されたセッションデータはが確率的に削除されるのはなぜなんだろう。負荷の問題だろうか。

前述のようにこの設定はサーバ単位なので、同一のサーバ上でVirtualHostを使用するなどして異なるWEBアプリケーションを実行する場合、セッションデータの有効期限はすべて同じになる。アプリケーション毎には有効期限を変えたい場合には、設定を考えなければならない。らしい。

セッションクッキーの有効期間

アプリケーションの設計・実装ではセッションデータの有効期間よりこちらの方が重要。
次の関数でセッションクッキーの設定を行う。

void session_set_cookie_params(
  int $lifetime [, string $path [, string $domain 
  [, bool $secure = false [, bool $httponly = false ]]]])

lifetime  セッションクッキーの有効期限。秒数で指定。

session_set_cookie_params() は session_start() より前で呼ばなければならない。

参考ページ:
PHP: session_set_cookie_params - Manual
セッション | PHP プログラミング解説
セッション(session)の有効期限を設定するには - spanstyle::monolog
賢者の漬け物石: PHP のセッション寿命の設定
phpでセッションの情報が消える - michihideの日記
セッションの有効期間とか設定とか挙動とかを調べました - [PHP + PHP] ぺんたん info

August 27, 2014

bashのシェルスクリプト

bashのシェルスクリプトの例。
/var/data/test ディレクトリにある拡張子が dat のファイルを /var/data ディレクトリに拡張子を txt にかけてコピーする。
今後、サーバ上で決まった複数のファイルをコピーするとか削除するみたいな単純な定型作業は、このスクリプトを修正して使うことにする。

#!/bin/bash
path1=/var/data

files=(`ls -1 ${path1}/test/*.dat`)
for item in ${files[@]}
do
  item2=`basename ${item} .dat`
  echo ${item2}
  cp ${path1}/test/${item} ${path1}/${item2}.txt
done

4行目ではコマンドをバッククウォートで囲んで、さらにカッコで囲っている。
バッククウォートで囲うとコマンドの出力を変数に代入できる。カッコで囲うと配列になる。つまり4行目はlsの出力からファイル名を要素とする配列を作ってfilesに代入している。
5行目で "@" は配列の特別なインデックスで、配列の要素をスペース区切りで出力する。
7行目のコマンド basename はパスからファイル名の部分を取り出して出力する。第2引数として「ピリオド+拡張子」を指定すると、ファイル名からさらに「ピリオド+拡張子」を取り除いた部分を出力する。

参考ページ:
for 文の使用方法 - UNIX & Linux コマンド・シェルスクリプト リファレンス
配列を使用する - UNIX & Linux コマンド・シェルスクリプト リファレンス
bash 配列まとめ - Qiita
[bash] シェルスクリプト入門 ~シェルスクリプトのいろは~ - SUNONE BLOG

August 26, 2014

BASIC認証の設定のAuthNameとRequire

関連記事:.htaccessでBASIC認証

AuthName

以前のBASIC認証についての記事でBASIC認証の設定のAuthNameディレクティブについて

IDとパスワードを入れる画面で表示されるメッセージだと思えばよい。

などと書いたが、あれは間違い。単なるメッセージではない。
AuthNameは認証領域(authentication realm)を表す。
Apacheのドキュメントには以下のように説明されている。

例えば、"Restricted Files" 領域中で 一度認証されれば、同一サーバ上で "Restricted Files" Realm としてマークされたどんな領域でも、クライアントは 自動的に同じパスワードを使おうと試みます。 このおかげで、複数の制限領域に同じ realm を共有させて、 ユーザがパスワードを何度も要求される事態を 防ぐことができます。もちろん、セキュリティ上の理由から、 サーバのホスト名が変わればいつでも必ず、 クライアントは再びパスワードを尋ねる必要があります。

例1:
/var/www の下に test1,test2,test3 の3つのディレクトリがあり、それぞれ .htaccess でBASIC認証がされている。test1/.htaccess と test2/.htaccess の AuthName は "auth1"、test3/.htaccess のAuthName が "auth2" 。この場合、以下のような動きになる。
1.test1 のコンテンツにアクセスしようとするとBASIC認証のダイアログは出る。ユーザID/パスワードを入力して認証を通るとコンテンツにアクセスできる。
2.test2 のコンテンツにアクセスしようとすると、すでに1.でAuthName="auth1" の認証を通っているのでBASIC認証のダイアログは出ない。
3.test3 のコンテンツにアクセスしようとすると、ここのAuthNameはtest1,test2 のAuthNameとは異なるのでBASIC認証のダイアログは出る。

例2:
/var/www/test ディレクトリがあり、その下に sub ディレクトリがある。それぞれ .htaccess でBASIC認証の設定をしている。test/.htaccess のAuthName は "auth"、test/sub/.htaccess のAuthNameは "auth sub"。
この場合、test でBASIC認証を通った後に、sub では AuthName が違うのでまたBASIC認証のダイアログが出るものと思っていたが、実際は sub のコンテンツにアクセスする際にBASIC認証のダイアログは出ない。
これは、.htaccess はサブディレクトリにも影響が及ぶので test で認証を通ればそのサブディレクトリもすべてアクセスでき、サブディレクトリで異なる AuthName のBASIC認証を設定しても意味はないってこと?

Require

BASIC認証の設定のRequireディレクティブは、"Require valid-user" と書いておけばパスワードファイルに記載されたユーザがすべてそのディレクトリ以下にアクセスできる。しかし、

Require user <ユーザID1> [ <ユーザID2>...]

のように許可するユーザを個々に指定してもよい。このように書くと、パスワードファイルに記載されたユーザでも、Require で許可されていなければアクセスが認められない。

例3:
/var/www/test ディレクトリがあり、その下に sub ディレクトリがある。test の .htaccess でBASIC認証の設定をしている。test/.htaccess の Require は "Require valid-user" となっている。パスワードファイルには "user1" と "admin" の2つのユーザが記載されている。
sub ディレクトリの .htaccess には以下の1行のみ。

Require user admin

この場合、 test ディレクトリにおいて "user1" で認証を通った場合、続けて sub ディレクトリのコンテンツにアクセスしようとするとBASIC認証のダイアログが出る。そこでは "user1" ではアクセスできず、"admin" でなければアクセスできない。
一方、test ディレクトリにおいて "admin" で認証を通った場合、subディレクトリであらためてユーザID/パスワードを入力する必要はない。

参考ページ:
認証、承認、アクセス制御 - Apache HTTP サーバ バージョン 2.4
BUKURO-JIN | Basic認証の認証領域 ~AuthName はただのテキストに非ず~
Basic認証の設定 - tkkochanの日記

August 09, 2014

PHPメモ041:パスワードのSalt付きハッシュ値

Salt(ソルト)とは

Saltはパスワードのハッシュ値を生成する際に元のデータに付与する文字列である。

ハッシュ値 = ハッシュ関数(パスワード+Salt)

Saltをユーザ毎に異なる値にすれば、同じパスワードでもハッシュ値はそれぞれ異なる値となる。
Saltを付与してハッシュ値を生成した場合、ハッシュ値だけでなくSaltも保存しておかなければならない。入力されたパスワードの検証では
ハッシュ関数(入力されたパスワード+Salt)
でハッシュ値を求めて、システムが持つパスワードのハッシュ値と比較する。よって、Saltがないとパスワードの検証ができない。

「パスワード+Salt」から生成したハッシュ値の利点はレインボーテーブルによるオフライン攻撃に対して強くなることである。レインボーテーブルとはハッシュ値とパスワードの、巨大な対応表(逆引き表)である。インターネットで公開されているものもある。
Saltを付与しないで生成したハッシュ値では、このレインボーテーブルを使って高速にハッシュ値から平文のパスワードを見つけられる可能性がある。
対して「パスワード+Salt」のハッシュの場合、Saltはシステムやユーザ毎に違うのでレインボーテーブルの有効性が著しく下がる。
ただし、オフラインの総当り攻撃(ブルートフォース攻撃)にはほとんど効果がない。オフライン攻撃、総当り攻撃については参考サイト1を参照。

PHPでの実装

PHPでハッシュ値を得るにはPHP 5.5以降では password_hash() という関数が用意されているのでそれを使う。5.5より前のPHPでは crypt() を使用する。
どちらの関数も戻り値にSaltが含まれているので、別途Saltを保存しておく必要はなく関数の戻り値を保存しておけばよい。
戻り値の形式は参考サイト2の下の方で説明されている。簡単に説明すると最初の4桁がハッシュを求めるためのアルゴリズムの指定、次の3桁がコスト(ストレッチングの回数)の指定、その後がSaltとハッシュ値になっている。ストレッチングについては参考ページ1を参照。
前記のようにこの値は「<ハッシュを生成する方法の指定>+<Salt>+<ハッシュ値>」となっているが、別にコード中で分解したりくっつけたりする必要なく全体をハッシュ値として扱えばよい。
crypt() でアルゴリズムにBlowfishを指定する場合、最初に渡すSaltは以下のような形式である。

$2y$<2桁数値>$<22桁の文字列>

先頭の4桁 "$2y$" はPHP 5.3.7以降で使用できる。それ以前のPHPでは "$2a$" と指定できるが、セキュリティ上問題があるとPHPのドキュメントでは説明されている。
ただし、自分が使用できる環境(OSはCentOS)では、PHPが5.3.7より古いが "$2y$" でもcrypt() が値を返しており、エラーやワーニングも出ず表立って不都合はなかった。ハッシュ化に関連するモジュールが更新されているんだろうか?
続くコストの指定には4~31を指定できる。固定長2桁で、10未満の値を指定する場合は前ゼロを付ける(4の場合は"04")。
最後の22桁の文字列に使用できる文字は "./0-9A-Za-z" である。

以下のコードで ctypt() を試してみた。

define('SALT_HEADER', '$2a$04$');

function create_salt() {
    // Blowfishのソルトに使用できる文字種
    $chars = array_merge(range('0', '9'), range('a', 'z'), range('A', 'Z'), array('.', '/'));
    
    // ソルトを生成(上記文字種からなるランダムな22文字)
    $salt = '';
    for ($i = 0; $i < 22; $i++) {
        $salt .= $chars[mt_rand(0, count($chars) - 1)];
    }
    
    return SALT_HEADER . $salt;
}

$password = 'abcd1234';

$salt = create_salt();
echo "salt={$salt}\n";

$hashed_password = crypt($password, $salt);
echo "{$password}=>{$hashed_password}\n";

$str1 = 'abcd1234';
$hashed_str1 = crypt($str1, $hashed_password);
echo "{$str1}=>{$hashed_str1}: ";
if ($hashed_str1 === $hashed_password) {
	echo "password OK\n";
} else {
	echo "password NG\n";
}

$str2 = 'wxyz7890';
$hashed_str2 = crypt($str2, $hashed_password);
echo "{$str2}=>{$hashed_str2}: ";
if ($hashed_str2 === $hashed_password) {
	echo "password OK\n";
} else {
	echo "password NG\n";
}

出力は以下のようになる。
何度も実行するとほとんどの場合でSaltの最後の1文字が crypt() の戻り値に含まれていない。Saltのアルゴリズムとコストの指定に続く22文字の文字列のうち、実際には21文字しか使われない場合があるということだろうか?

salt=$2a$04$6LX9dypo2YY6IiBEu8Z25E
abcd1234=>$2a$04$6LX9dypo2YY6IiBEu8Z25.Ifd.t6Ai3/xECpUME.B89TOenc9t9Z6
abcd1234=>$2a$04$6LX9dypo2YY6IiBEu8Z25.Ifd.t6Ai3/xECpUME.B89TOenc9t9Z6: password OK
wxyz7890=>$2a$04$6LX9dypo2YY6IiBEu8Z25.rupqqWpAbJmZzQsm4tOMaASf5ngh41i: password NG

参考ページ:
1.本当は怖いパスワードの話 - @IT
2.PHP: パスワードのハッシュ - Manual
3.PHP: crypt - Manual
4.PHP: password_hash - Manual
5.PHP でパスワードを保存するなら Blowfish でハッシュしよう | バシャログ。
6.PHP - [メモ] パスワードのハッシュを強力にする - Qiita

August 05, 2014

PostgreSQLの文字列のエスケープ

SQLの文字列リテラルはシングルクォートで囲われた文字の並びである。文字列リテラルの中にシングルクォートを入れたい場合、PostgreSQLではシングルクォートを続けて2つ書くことで文字列リテラル中にシングルクォートを記述できる。

dbtest=> SELECT 'ab''c';
 ?column?
----------
 ab'c
(1 行)

また、文字列リテラルを記述するときの最初のシングルクォートの前に "E" を書くことによって、C言語の文字列リテラルのようにバックスラッシュでエスケープ文字として使用できる。シングルクォートは "\'"、バックスラッシュ文字自体を "\\" と記述できる。また、"\t", "\n" などの制御文字も記述できる。

dbtest=> SELECT E'a\'b\\c\td\nf';
 ?column?
-----------
 a'b\c   d+
 f
(1 行)

参考サイト:
PostgreSQL 9.1.5文書 語彙の構成
PostgreSQLで「'」(シングルクォーテーション/単一引用符)をエスケープ処理する方法 - r_nobuホームページ
PostgreSQLでエスケープ文字を利用する場合の注意 - takami_hirokiの日記

August 04, 2014

2014八重山旅行記 まとめ

今回の旅行でやりたかったことは以下。
・ピナイサーラの滝に行く→達成
・西表島ドライブ→達成
・石垣島ドライブ→達成

ピナイサーラの滝は本当に良かった。滝の上も下も。行くまでは結構きつかったが、それでももう1回行ってもいいかなと思うくらい楽しかった。
ドライブは、車を擦らなかったらら最高に楽しかったのだが^^; よそ見運転よくない。

5回目となる今回の八重山旅行は今までで一番天気に恵まれた。
過去4回の八重山旅行は4月が3回、6月が1回。去年の旅行(4月)は天候不順で肌寒くてとても海で泳げるような感じではなく(気温は20度くらいだった)、全く南の島気分を味わえなかった。なので今年は「たとえ天気が悪くても海に入れるくらい暖かい時期」ということで6月末の梅雨明け直後にした。
結果としては正解だった。日中に短時間の雨(スコール)が降ることはあったが、それ以外は良く晴れていた。昼間は晴れているが快晴ではなくモクモクした雲が結構あったが、夜は雲が消えてめちゃくちゃ星がきれいだった。写真が取れなかったのが悔やまれる。梅雨明け後ということで懸念していた台風は来なかった。
本当に天気に恵まれた。
ただし、暑さはちょっと厳しかった。最高気温は連日32~33度。アクティビティの時は熱中症・脱水症状に注意しなければならなかった。
6月のデメリットは暑さの他には人の多さ。夏休みシーズンに比べるとまだマシなのかもしれないが、それでも4月のGW前に比べるとびっくりするくらい多い。ピナイサーラへの川ではカヤックが少し渋滞気味だったし^^;
静かにゆったりすごすなら4月の方がいいが、4月は天気が悪いと寒くて海に入れない可能性もあるし。
前に6月に旅行したときのメモがあったので読んでみると
「暑すぎる。やっぱり4月下旬がいい。」
と書いてある。この時は台風が来て石垣に戻るのを1日早めた上に、完全なノープランでだったのであまり充実感のない旅行だった。それで感想の何かネガティブになったのかな^^;
確かに暑かったが、やはり夏の南の島はいいもんだと改めて思った。
4月と6月、それぞれに良さがあるし、結局はその時の天候しだいだなー。

去年は陸っぱり夜釣りツアーに参加して大きなイカが釣れたが、今年はその時のガイドさんがいなくなったので申し込まなかった。代わりに一人で港やリーフエッジに行って釣りをしていたが、結果はボウズ。やっぱりガイドにいいポイントを教えてもらわないとダメかなあ。船を使った夜釣りツアーをやっているところがあるけど、かなり高い(前述の陸っぱりのツアーの3倍、1万円以上)なので躊躇してしまう。

今回の旅行、はじめて石垣島を観光したが、正直もういいかなって思った。自然なら西表島で十分。
次に八重山に旅行するときは、4日目に竹富島でも行こうかな。あまり興味はないが、川平湾と同じく八重山の定番観光スポットだからとりあえず1回行ってみるだけでも。

さて、来年。どうするか。
また西表島もいいけど、そろそろ別のところにも行きたくなってきた。も西表島に飽きたわけではない。まだ見てないところ・ものもあるし。バラス島、イダの浜、マリユドゥの滝、カンピレーの滝、サガリバナ。でも早く見ないとなくなるものでもないし。
行ってみたいのは奄美大島。バニラが就航して羽田からだけど安く行けるし。
色々考えた結果、もし、来年も西表島に行くなら今年より1週間くらい早く行ってみたい。沖縄の梅雨明けは平年は6月下旬と言われている。これは本島付近で梅雨が明けてはじめて「沖縄県全体が梅雨明けしました」ってことで、八重山に限ればそれより早く梅雨明け状態になっていることが多いらしい。
まあ、実際どうするかは今年の年末くらいから考え始めればいいか。

総括としては、予定していたことは出来たし、南の島の夏を味わえたし、いい旅でした。

August 03, 2014

2014八重山旅行記 4日目、5日目

4日目。宿をチェックアウト。今日は石垣に戻り、ドライブする予定。またドライブ、大丈夫か、俺。
9時半の上原発石垣行きに乗るために港に向かう。
港に着いて船を待つ列に並んでいたら、部屋のカギを返却してないことに気づいた。もう船は入ってきてる。やばい!!ダッシュで宿に戻りヘルパーさんにカギを渡し、またダッシュで港に戻った。
#実際は力尽きて途中で歩いたが^^;
港に着くとまだ乗船は始まってなかった。よかった間に合った。
乗船待ちの列の一番後ろに並びなおした。待っている間に、先ほどカギを返しに行った時に桟橋の中を走ったことについて、たぶん島人じゃないどっかのツアーのガイド2人組にネチネチと非難された。その言い方まあ陰湿というか、なんというか。確かに悪かったのは俺だが、あんな言い方しなくても。
乗船が始まった。待っている人数が「これ全部乗れるのか?」という感じだったがギリギリ乗り込むことが出来た。
さよなら西表島。またいつか。

石垣港に到着。レンタカーの予約しているレンタカー屋までバスで行かなければならない。バスターミナルに移動し、空港行きのバスに乗った。バスターミナルから乗る場合は事前に乗車券を窓口で買わないといけないのね。途中から乗った場合は下りるときに現金で払う。乗る場所によってお金の払い方が違うってのは、なんか変な感じだ。
沖縄県八重山事務所(八重山支庁)の近くのバス停で降りて、レンタカー屋に。前日のことを踏まえて、一番補償範囲が広い保険に入った。
車に乗って、昼飯を食べに離島ターミナル付近(市街地中心部)に戻る。有料の駐車場に車を止め、郵便局の近くの定食屋で飯を食べた。食べログで事前に決めていた飯屋。なかなか美味しかった。
それから、ユーグレナモールで会社へのお土産でちんすこうを購入。定番買っておけばまあ問題ないだろうと。そして車のところに戻る。
実は、有料の駐車場を使ったのは初めて。出入口にバーがあるやつ。ちょっとどきどきした(笑)

それから車で名蔵アンパル、川平湾、バンナ公園(バンナスカイラン、展望台)、石垣島天文台と回った。
名蔵アンパル、干潟ですな。モーターパラグライダーが飛んでた。
川平湾、確かにきれいな風景。だけど修学旅行の学生とか観光客がいっぱいいて風情がない。
バンナ公園の展望台、いい眺めだった。
石垣島天文台、行くまでの山道が面白かった。対向車が来たら詰むでたな(笑)。運転の練習になった。しかし肝心の天文台は定休日orz
石垣島天文台以外はもう行かなくていいかなと思った。やっぱり俺は「手付かずの自然」とかあんまり観光地されていないところが好きだ。もしまたドライブする機会があったら、今回行けなかった島の北の方に行きたい。島をぐるっと一周ドライブしてみたい。

Anparu

Kabirawan

Banna

天文台の山から降りて、マックスバリューで明日の食料の調達。明日の朝食としてゼブラパンを購入。それから宿に向かった。
宿は中心部からは少し離れている。車は宿の近くに路上駐車しておいて、宿にチェックイン。荷物を部屋に置いてすぐに車の所に戻った路駐してたので駐車違反が心配だったが大丈夫だった。
車をレンタカー屋に返して、またバスで中心部に戻る。レンタカー屋が宿から遠いと不便だ--;
バスを降りて宿に歩いて向かう途中、ゆらてぃく市場で夕飯を買って、宿で食べた。
食べ終わってから、他の泊り客から「ご飯食べに行こう」と誘われた。早まったorz

今日はレンタカーを借りたのが11時過ぎで返したのが17時。石垣への移動があったからスタートが遅いのは仕方がない。返すのをもうちょと遅くすれば良かったかな。もっと運転したかった。

4日目の○:昨日引き続き車の運転したこと
4日目の×:石垣島の観光スポット

5日目、最終日。あとは帰るだけ。
朝食は昨日買ったゼブラパン。全部食べ切れなかった^^;
飛行機は11:50発なので10時過ぎに宿の車で空港まで送ってもらった。自分以外に一人、港まで送ってもらう予定だった人も車に乗っていたのだが、途中で「やっぱり空港まで送ってください」となって、空港に送られる人が2人になった。空港までの送迎は1500円なのだが、複数いる場合は割り勘になる。そういうシステムだったと車に乗ってから知った^^;

空港に着いて、展望台に上がって白保の海を見ながら
「あー、帰りたくねー」
と心の底から思う。が、帰らないわけには行かないよなー。
そして、帰りもスカイマーク。那覇経由で羽田に戻った。

« July 2014 | Main | September 2014 »

May 2017
Sun Mon Tue Wed Thu Fri Sat
  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      
無料ブログはココログ

日本blog村

  • にほんブログ村 IT技術ブログへ
  • にほんブログ村 アニメブログへ
  • にほんブログ村 サッカーブログ アルビレックス新潟へ

好きな音楽家

メモ

XI-Prof