Perl - Taint mode の効果的な活用方法
2007-02-12


#!/usr/bin/perl -T -I. # p2h.pl - プレーンテキストを HTML に変換する use strict; use warnings; use Taint qw/tainted/; sub spill { if (tainted @_){ my (undef, $file, $line) = caller; die "Insecure request at $file line $line.\n"; } print @_; } # 改行モードだけ気を使ってみる use open IN => ':crlf'; binmode STDIN, ':crlf'; spill <<ENDLINE; <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html> <head> <title>Output from p2h.pl</title> </head> <body> ENDLINE $/ = ''; # 1 つ以上の連続する空行をレコードの終端として扱う while (<>){ chomp; s/[<>&"]/'&'.{qw(< lt > gt & amp " quot)}->{$&}.';'/ego; # HTML の為のエスケープ処理 spill "<p>$_</p>\n\n"; } # 残りの骨組みを出力 spill <<ENDLINE; </body> </html> ENDLINE __END__

さて、この状態で、さっきと同様にスクリプトを実行しようとした場合、 実行結果は以下のようになり、途中で 〓 まさに汚染された値を出力しようとした段階で 〓 プログラムは異常終了するようになります。 すなわち、汚染チェックはテキストの出力に対しても機能することが、これにて証明されたわけです。

murachi@maha ~ $ ./p2h.pl test.txt
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
    <title>Output from p2h.pl</title>
</head>
<body>

Insecure request at ./p2h.pl line 34.
murachi@maha ~ $

汚染された値の洗浄

汚染チェックは機能するようになりましたが、最終的に、スクリプトが実行できないことには仕方がないので、 汚染されている値の洗浄を試みることにします。 スクリプトの、注目すべき箇所についてのみクローズアップしてみることにしましょう。

$/ = '';    # 1 つ以上の連続する空行をレコードの終端として扱う
while (<>){
    chomp;
    s/[<>&"]/'&'.{qw(< lt > gt & amp " quot)}->{$&}.';'/ego;    # HTML の為のエスケープ処理
    spill "<p>$_</p>\n\n";
}

とはいえ、実質的にはこの中で行われている置換処理で、値の洗浄は十分に行われていることを、あなたは知っています。 そして、以下のようなコーディングでお茶を濁すという行為に魅入られてしまうことになるやも知れません。

$/ = '';    # 1 つ以上の連続する空行をレコードの終端として扱う
while (<>){
    chomp;
    s/[<>&"]/'&'.{qw(< lt > gt & amp " quot)}->{$&}.';'/ego;    # HTML の為のエスケープ処理
    /(.*)/s;    # 。。。おや?
    spill "<p>$1</p>\n\n";
}

このプログラムは正常に動作しますが、このようなコーディングは避けるべきです。 これでは汚染チェックを利用する意味がありません。 以下のように、より厳密なパターンを記述すべきです。

$/ = '';    # 1 つ以上の連続する空行をレコードの終端として扱う
while (<>){
    chomp;
    s/[<>&"]/'&'.{qw(< lt > gt & amp " quot)}->{$&}.';'/ego;    # HTML の為のエスケープ処理
    /((?:[^<>&"]|&(?:lt|gt|amp|quot);)*)/i;     # 抽出の為のパターンは厳密に。
    spill "<p>$1</p>\n\n";
}

もちろん、実体参照は他にもいろんな種類がありますし (&copy; とか &hearts; とか)、文字コードを直接指定する記述法も存在するわけですが (&#126; とか &#x7e; とか)、とりあえずこのプログラムに関してはこれで十分でしょう。 もしも将来的な拡張の可能性を考慮したいのであれば、以下のようにより一般化したパターンにしても良いと思います。

$/ = '';    # 1 つ以上の連続する空行をレコードの終端として扱う
while (<>){
    chomp;
    s/[<>&"]/'&'.{qw(< lt > gt & amp " quot)}->{$&}.';'/ego;    # HTML の為のエスケープ処理
    /((?:[^<>&"]|&(?:\w+|#(?:\d+|x[\da-f]+));)*)/i;     # 抽出の為のパターンは厳密に (実体参照は一般化してみた)。
    spill "<p>$1</p>\n\n";
}

ところで、抽出の為のパターンを見てみましょう。

    /((?:[^<>&"]|&(?:\w+|#(?:\d+|x[\da-f]+));)*)/i;

このパターン、実体参照を表現する部分を除けば、本質的にはブラックリスト方式による検査法です


続きを読む
戻る
[設計・開発]
[個人的メモ]

コメント(全0件)
コメントをする


記事を書く
powered by ASAHIネット