以下につられて買ってみた。
TAKESAKO @ Yet another Cybozu Labs: KENTさんとの対談写真が見れるのは日経ソフトウエアだけそして、去年の11月に:: KENT WEB - CGIスクリプト :: で有名な KENT さんと対談させていただいたときの記事が写真つきで載っています。 KENTさんの写真が見れるのは日経ソフトウエア2007.03(1月24日発売)だけです。404 Blog Not Found:日経ソフトウェア2007年1月号 - perlはどこだ!? - おおもりさんのコメント
申し訳ないです。とりあえず3月号をお楽しみに、という感じです。詳しくは竹迫さんに聞いていただければと思います。
確かにKENT的視点はPerl Mongersの対極にあるとも言えるので、実に貴重なインタビューだと思う。
インタビューをはじめ記事は本誌を見てもらう事にして、ここでは竹迫ソースを添削してみることにする。
日経ソフトウェア2007.03 p.45 リスト13#!/usr/bin/perl use strict; use CGI; use CGI::Carp qw(fatalsToBrowser); # [以下略]
ここなのだけど、CGIであれば、さらに厳しくしたいところ。
#!/usr/bin/perl -T use strict; use warnings;
違いは、Taint ModeをOnにしていること。これは本来SUID実行という、セキュリティーが厳しく問われる環境のために用意されたものだけど、-Tを指定することにより、通常のスクリプトでもそれを利用することが出来る。
たとえば、以下のようなCGIを考える。
#!/usr/bin/perl
use strict;
use CGI;
use CGI::Carp qw(fatalsToBrowser);
my $q = CGI->new();
my $domain = $q->param('domain');
print "Content-Type: text/plain\n\n", `whois $domain`;
簡単なwhois gatewayだが、このscriptは危険だ。domain='%60rm%20*%60%20dan.co.jp'などとされたら、whois `rm *` dan.co.jpが実行されてしまう。しかも、後ろに正しいドメイン名が存在するので、まともに実行されたように見えてしまう。
これに-Tを付けるだけで、このscriptは
Insecure dependency in `` while running with -T switch at ./whois.cgi line 7.
と止まってくれるようになる。
しかし、そのままでは正当なアクセスまで出来なくなってしまう。Taint Modeはかなり厳しく、プログラムの外部から来たデータは全て汚染されたものと見なし、危ないとされる操作はできなくなってしまう。これがいやでTaint Modeを使っていない人も多いのだが、これは簡単に回避できる。
#!/usr/bin/perl -T use strict; use CGI; use CGI::Carp qw(fatalsToBrowser); $ENV{'PATH'} = '/bin:/usr/bin'; sub untaint{ my $str = shift; $str =~ m,^([0-9A-Za-z\-\./]+)$,o; $1 or die qq(Couldn't untaint "$str"); return $1; } my $q = CGI->new(); my $domain = untaint $q->param('domain'); print "Content-Type: text/plain\n\n", `whois $domain`;
とすればよいのだ。まず、$ENV{'PATH'} = '/bin:/usr/bin';と明示指定する事で、$ENV{'PATH'}の汚染がなくなり。そしてuntaint()を通す事で、$q->param('domain')の汚染が除去される。そして関係する変数の汚染が全てなくなったので``がきちんと実行されるようになる。
ここで注意して欲しいのは、untaintで使う正規表現。これが汚染された変数から汚染されていない情報を取り出す、言葉を変えると「サニタイズ」の唯一の手段なので、この正規表現が甘ければ当然セキュリティは台無しになる。例えば/(.*)/などとしたら全てが水の泡だ。
もちろんTaint Modeは銀の弾丸ではない。たとえばXSSなどに対しては効果は薄い。しかしそれを言えばuse strict;も銀の弾丸ではないのだ。
だから、CGIのように任意の第三者から実行されうるscriptは、なるべく-Tを指定するよう心がけたい。
もう一つのuse warnings;は、変数の初期化忘れなど、エラーではないが問題となりうるコードを検出してくれる。use CGI::Carp qw(fatalsToBrowser warningsToBrowser);とすれば、警告もブラウザーに表示されるようになる。別解として-wを付ける、すなわち第一行目を#!/usr/bin/perl -Twとするというものもあるが、この場合CGIのみではなく、使われるモジュール全てでwarningsが有効となるので、場合によっては「やかまし」過ぎることになるので、一行余計でもuse warnings;の方を奨める。
その他Perlのセキュリティに関しては、perldoc perlsecも参照されたし。
Dan the Perl Monger
「共通メソッド」って何?
「ある文字列が何かの指定用途において適切か」ってのは、それを適用するモジュールが知っている(べき)ことであって、CGIモジュールという特定のインターフェースに作り込むものではないし、untaint_*()なんて括りで用意すべきものではない。
そんな方法でuntaintして安心するような気休めの使い方なら、taintなんて枠組みはむしろ有害であって、プログラマが何を本当に注意すべきかを隠蔽してしまうだけ。
一方、validationメカニズムだったらほとんどのWebフレームワークにあるでしょう。でも、それはデータの適用時安全性を保証するものではないので、taintやsanitizationといった概念とは無関係。
まあ、そもそもこのエントリ自体がmisleadingなんだよな。