IPAのページで見つけたperlのロジックがややこしかったのでメモ
安全なウェブサイトの作り方
https://www.ipa.go.jp/files/000017316.pdf
をじっくり読み込むとperlの以下のような関数が出てきます。
### 複数行にわたる文字列から最初の行を返す関数
# 引数: 文字列。2 つ目以降の引数は無視する
# 戻り値: 改行コード(¥r, ¥n, ¥r¥n)以前の文字列。
sub first_line {
$str = shift;
return ($str =~ /^([^¥r¥n]*)/)[0];
}
で、解説によると
「上記は、引数で与えられた文字列の最初の行(改行なし)を返す関数です。外部から入力されるパ
ラメータの値を出力する場合でも、この関数を通すことで、HTTP レスポンスヘッダのフィールド値として
適切な形式となり、脆弱性を解消できます。」
となっているので
先頭から改行までを取り出して返してくれる関数だそうです。
肝は以下の部分です。
return ($str =~ /^([^¥r¥n]*)/)[0];
このままでは判りくいので 改行をコロンにして書き換えた。
$str = "abc:::def:::g";
print "str=$str¥n";
$ans = ($str =~ /^([^:]*)/)[0];
print "1:ans=$ans¥n";
実行結果は
1:ans=abc
となり、見事に:の前部分を取り出す事に成功している。
解析すると単純に以下の部分と
$str =~ /^([^:]*)/
それ以外に分かれます。
(xxxxx)[0]
xxxxxの所に「上の」が入る
xxxxxの部分は判る。 マッチング処理です。
これを括弧で挟んで()[0]とする書き方が良くわからん。
マッチング処理での結果1番目にマッチした部分は$1へ2番目は$2へ3番目は$3へという法則が有るので
以下を実行。
$str =~ /^([^:]*)/;
print "6-1=$1¥n";
print "6-2=$2¥n";
print "6-3=$3¥n";
実行結果は
6-1=abc
6-2=
6-3=
ちゃんとコロンの前が取り出された。
『マッチする部分が1箇所しかないのでこうなっているのかー』と思って
マッチする箇所が2箇所になるようにして実行
$str =~ /^([^:]*)(.*)/;
print "7-1=$1¥n";
print "7-2=$2¥n";
print "7-3=$3¥n";
実行結果は
7-1=abc
7-2=:::def:::g
7-3=
思ったとおり2番目のマッチにも値が入った。
で、最初の疑問「()に入れて最後に[0]とする書き方」ですが、
$ans1 = ($str =~ /^([^:]*)(.*)/)[0];
$ans2 = ($str =~ /^([^:]*)(.*)/)[1];
print "ans1=$ans1¥n";
print "ans2=$ans2¥n";
と書いて実行。
ans1=abc
ans2=:::def:::g
おおーちゃんと値が入っている。
という事で知らなかったけどパターンマッチングの結果を取得するのは「()に入れて最後に[0]」で良いらしいぞ。
でも、俺が書くなら
@ans = split(/[:|:]/, $str);
print "split ans1=$ans[0]¥n";
と書けば『超簡単』なんだけどねー
split ans1=abc
ソース全体
use CGI;
my $q = new CGI;
#$argc = $q->param('argc');
#$upload_flag = $q->param('flag');
# HTML出力
print "Content-type: text/plain¥n¥n";
$abc = "hello";
print "abc=$abc¥n";
$str = "abc:::def:::g";
print "str=$str¥n";
$ans = ($str =~ /^([^:::]*)/)[0];
print "1:ans=$ans¥n";
$ans = ($str =~ /^([^:]*)/)[0];
print "2:ans=$ans¥n";
$ans = ($str =~ /([^:]*)/)[0];
print "3:ans=$ans¥n";
$ans = ($str =~ /([^:])/)[0];
print "4:ans=$ans¥n";
$ans = ($str =~ /^([^:]*)/)[0];
print "5:ans=$ans¥n";
$str =~ /^([^:]*)/;
print "6-1=$1¥n";
print "6-2=$2¥n";
print "6-3=$3¥n";
$str =~ /^([^:]*)(.*)/;
print "7-1=$1¥n";
print "7-2=$2¥n";
print "7-3=$3¥n";
$ans1 = ($str =~ /^([^:]*)(.*)/)[0];
$ans2 = ($str =~ /^([^:]*)(.*)/)[1];
print "ans1=$ans1¥n";
print "ans2=$ans2¥n";
@ans = split(/[:|:]/, $str);
print "split ans1=$ans[0]¥n";
print "split ans2=$ans[3]¥n";
$ans = $ans[0];
print "split ans=$ans¥n";
exit;
全部の実行結果
abc=hello
str=abc:::def:::g
1:ans=abc
2:ans=abc
3:ans=abc
4:ans=a
5:ans=abc
6-1=abc
6-2=
6-3=
7-1=abc
7-2=:::def:::g
7-3=
ans1=abc
ans2=:::def:::g
split ans1=abc
split ans2=def
split ans=abc