カテゴリー : 2009年 8月

FireMobileSimplator 1.1.9 リリース


FireMobileSimulator更新されていました。
ホスト限定で有効にできるようになり、ますます便利になりましたが、ソースコードを追ってみたところこのリリースノートで明言されていない隠された機能がありました。
 
この設定、実は正規表現なんですね。

^example\.com$

のようにホスト名が完全一致するときだけ有効にしたり、

^mobile\.

のように mobile. から始まる全てのホストで有効にする、などかなり自由に設定ができます。

session_regeneration_id とセッションフィクシエーション対策


CSRF 対策のうち、セッションフィクセーションを避けるために使われる
session_regenerate_id() 関数について、ITProで間違い(になる場合がある)記事を見つけたのでメモ。
 
関数の知られざる引数(print_r関数、session_regenerate_id関数)[より

同じように、PHP 5.1からはsession_regenerate_id関数にも第1引数が指定されました。第1引数をtrueに指定すると、古くなったセッションは削除されるようになります。
 
もともとセッション固定攻撃を防ぐために用意された関数ではなく、PHP 5.1 までの同関数には古いセッションファイルを削除する機能が無かったため、セッション固定攻撃への対策としては不十分でした。
 
今後は、session_regenerate_id関数の引数に必ずtrueを指定することを忘れないようにしましょう。また、それまでのPHPで使う場合は、以下のコードを使うことで、セッション固定攻撃を防ぐことができます。

のように、session_regenerate_id() の第一引数に true を常に指定すべきと書かれていますが、session_regenerate_id(true) はむやみに使ってはなりません。
 
ショッピングカートなど限定的な用途でセッションを使っている場合は問題ないのですが、常にログインが必要なサイトなどで非同期で複数のリクエストを実行する可能性がある場合、古いセッションが削除されると問題になりことがあります。
 
たとえば同じ HTML 内に認証が必要な画像などの埋め込みコンテンツが複数あったり AJAX で認証が必要なデータにアクセスする場合、一つのセッションIDで複数のリクエストを行うため、2リクエスト目以降も古いセッションで認証することになるからです。
 
代替案として、session_regenerate_id(false) と session_write_close() を組み合わせて古いセッションのみ有効期間を設定してやるとよい、とsession_regenrate_idのコメント欄に書かれていました。
 
実装例:

// ユーザがリクエストしたセッションIDからセッション情報をロードする。
session_start();
// -----------------------------------------------------
// 以下セッションフィクセーション対策
if(isset($_SESSION['expire']) && $_SESSION['expire'] < time()){
    // 過去に session_regenerate_id() で置き換えられた古いセッションによるアクセス。
    // 有効期限を過ぎていればセッション情報をクリアする。
    $_SESSION = array();
}
 
// 古いセッションは2分間で消えるように設定。
$_SESSION['expire'] = time() + (2*60);
// 有効期限を記録するため、一旦明示的にセッションを閉じ、$_SESSION の中身を古いセッションIDで保存する。
session_write_close();
 
// 閉じたセッションを開きなおす。
session_start();
// セッションIDを更新し、$_SESSION を古いセッションIDから切り離す。
session_regenerate_id();
// 新しいセッションID は有効期限なし。
unset($_SESSION['expire']);
// ——————————————-
// セッションフィクセーション対策ここまで。
// あとは通常通り。
$_SESSION['user_id'] = “1234″;

 
また、session_regenerate_id() だけではセッション所有者のリクエストによる XSRF は防ぐことはできないため、こちら(開発者のための正しいCSRF対策)を参考にワンタイムトークン等も適宜組み合わせましょう。

複数ファイル処理とパイプ


コマンド実行結果の差分を取りたい場合など、
複数のパイプ処理をしたい場合、bash の <( ... ) 構文を使うと便利です。

command <( piped-command1 ) <(piped-command2 ) ...

例:

$ cat <(echo hello) <(echo world)
hello
world

ゴミ(一時ファイル)も作成されないのでエコロジーです。
 
例2: SJIS で書かれたテキストと UTF-8 で書かれたテキストを nkf で SJIS に統一して比較する。

$ diff $(nkf -Se index.shift_jis.html ) $(nkf -We index.utf8.html )

 
PHP で別々に処理した画像を imagemagick で結合する、という時や複数サーバから httpd ログファイルを拾ってきてマージする、なんていうときにも重宝します。
 
内部的にはファイルデスクリプタや FIFO を一時作成しています。
open(2) で開くことができるのでファイルを引数にとるプログラムなら大抵のもので使えますね。
 
検証:
Linux 2.6 の場合の実体はファイルデスクリプタ

$ ls -ld <( echo foo )
lr-x—— 1 user user 64 8月 6 18:42 /dev/fd/63 -> pipe:[61042111]

FreeBSD 6 の場合は fifo (参考 mkfifo)

$ ls -ld <(echo foo)
prw——- 1 user wheel 0 8 6 19:04 /var/tmp//sh-np-2875720493

 
参考:
- Can process substitution be used as an input file to another program?(The UNIX and Linux Forums)
zsh では =( … ) とのこと。

% cat =(echo hello)

みたいなかんじで。
- 一時ファイルを作らずにプログラムの出力を diff
diff の利用例。
- Manpage of BASH

シェルで標準出力と標準エラー出力を入れ替える


$ command3>&2 2>&1 1>&3 | …

または

$ var=`command 3>&2 2>&1 1>&3`

とすると STDOUT と STDERR を入れ替えることが出来ます。

$ err=`grep “Tarou” address1 address2`
address1: Tarou 090-xxxx-xxxx
$ echo $err
grep: address2: No such file or directory

 
参考:
いずれもオンライン書籍です。
Swap Standard Output and Standard Error(UNIX POWER TOOLS)
I/O Redirection(Advanced Bash Shell Scripting Guide)

任意のファイルデスクリプタを開く


PHP には fdopen がないので、子プロセスについては /dev/fd/<FD番号> で代用してます。
PHP の機能ではありませんが。。

<?php
// child.php: 子プロセス(parent.php から実行される)
$fd = 4; // 親プロセスとのやり取りに使うファイルデスクリプタ(0:stdin, 1:stdout, 2:stderr)
$fdfile = “/dev/fd/$fd”;
if(!file_exists($fdfile)){
   die(“ファイルデスクリプタが開かれていません”);
}
 
$pipe = fopen($fdfile, “r”);
while(!feof($pipe)){
  echo fread($pipe, 1024);
}
fclose($pipe);

 

<?php
// parent.php: 親プロセス
$fd = 4; // 子プロセスとの通信に使うファイルデスクリプタ
$fp = proc_open(“php child.php”, array(
  $fd=>array(‘pipe’, r),
),$pipes);
 
fwrite($pipes[$fd], “Hello world!!”);
fclose($pipes[$fd]);
 
proc_close($fp);

 
FreeBSD の場合、標準では /dev/fd に 0,1,2 しかないため動作しません。
次のようにしてfdescfs を /dev/fd にマウントすればプロセスごとのファイルデスクリプタを自動作成してくれるようになります。

# echo “fdescfs /dev/fd fdescfs rw 0 0″ >> /etc/fstab
# mount /dev/fd

または /dev/fd を使わない実装に置き換える事でも動作します。
この場合、ファイルデスクリプタが開いているかの確認には fstat [-p PID] を使うといいかもしれません。

// child.php: FreeBSD で devfs を使っている場合の代替実装。
$fd = 4;
$pipe = popen(‘cat <&'.$fd, 'r');
echo fread($pipe, 1024)
pclose();

proc_open の落とし穴


http://d.hatena.ne.jp/ichii386/20071116/1195153132
 
proc_open()の説明に書いてある例通りやると問題があるので [[stream_select()|]http://jp.php.net/manual/ja/function.stream-select.php] をあわせて使いましょう、という話。
一つのストリームを読み書きしてる間に他のストリームからの入力がたまるとバッファがいっぱいになってしまい、そこで書き出し側の処理がブロックしてしまう可能性がある。
stream_select を使うと、複数のストリームの中から入出力要求がきているストリームのみを選択して読み書きすることが出来ると。
 

// http://d.hatena.ne.jp/ichii386/20071116/1195153132 から引用:
$stdout = $stderr = ”;
while (feof($pipes[1])= false || feof($pipes[2]) = false) {
    $ret = stream_select(
        $read = array($pipes[1], $pipes[2]),
        $write = null,
        $except = null,
        $timeout = 1
    );
    if ($ret= false) {
        echo “error\n”;
        break;
    } else if ($ret
= 0) {
        echo “timeout\n”;
        continue;
    } else {
        foreach ($read as $sock) {
            if ($sock= $pipes[1]) {
                $stdout .= fread($sock, 4096);
            } else if ($sock
= $pipes[2]) {
                $stderr .= fread($sock, 4096);
            }
        }
    }
}

「ファイル名を指定して実行」をランチャー化するカスタマイズ方法と起動コマンド一覧


http://it.kndb.jp/entry/show/id/146
 
4年くらい前から、この記事と同じようにファイル名を指定して実行(Windows+R)をランチャーとして使っています。
いちいちランチャーをインストールするより Windows の標準機能を使おうということで始めたもののこれが存外に便利。
「ファイル名を指定して実行」に慣れると Windows標準のコマンドも覚えられるし、引数対応のプログラムもフル活用できて一石二鳥、です。
 
既存のコマンドとかぶらないよう、自分ルールで頭に記号をつけたものをショートカットで作って使っています。
 
・ “+” から始まるものはディレクトリ(+bin でランチャー用ディレクトリ, +home でマイドキュメントなど)
・”@” から始まるものはプログラム短縮名(@f で firefox, @i で iexplorer(Internet Explorer), @e で Eclipse, @p で PuTTY, @n でテキストエディタ, @fz で FileZilla, @ooo で OpenOffice.org などなど)
 
とくにキーボード派にオススメです。おためしあれ。