こんな風に処理すればいいのかな
- 中心となる部分は http://developer.cybozu.co.jp/kazuho/2010/09/twitter-xss-f73.html から拝借
- 入ってきたテキストが中途半端にエスケープされている( http://d.hatena.ne.jp/koseki2/20100414/twitterEscape )ため、入ってくるときに一度 decode_entities() をかける
- 分割された $token 及びそれから派生するデータのみに encode_entities() をかける(コード中のものはエスケープしない)
もっと改良の余地はありそうだが…ぐぬぬ
use common::sense; use HTML::Entities; our %re = ( uri => qr{(http://[\S]+)}, # FIXME reply => qr{(\@[0-9A-Za-z_]+)}, hash => qr{(\#[0-9A-Za-z_]+)}, ); our $regex = qr/$re{uri}|$re{reply}|$re{hash}/; my @tweets = ( "See: http://x.xx/@\"style=\"color:pink\"onmouseover=alert(1)//", q{http://j.mp/dankogai @dankogai こうですか? #XSS わかりません <script>alert('XSS')</script> http://twitter.com/search?q=a&r=b#@"onmouseover="alert(location.href)"/}, q{http://j.mp/dankogai @dankogai こうですか? #XSS わかりません <script>alert('XSS')</script> http://twitter.com/search?q=a&r=b#@"onmouseover="alert(location.href)"/ twitpic! http://twitpic.com/2r2umf}, ); for my $tweet (@tweets) { say '<div>'; say make_link($tweet); say '</div>'; } sub make_link { my $text = decode_entities(shift); my $html = ''; for my $token (split $regex, $text) { if ($token =~ /^$re{uri}/) { if ($token =~ m!http://twitpic\.com/(\w+)!) { my $encoded = encode_entities($1); $html .= qq{ <a href="http://twitpic.com/$encoded"> <img src="http://twitpic.com/show/thumb/$encoded"></a>}; } else { my $encoded = encode_entities($token); $html .= qq{<a href="$encoded">$encoded</a>}; } } elsif ($token =~ m!^\#(.+)$!) { my $hash_ent = encode_entities($1); my $hash_tag = encode_entities($token); $html .= qq{<a href="http://search.twitter.com/search?q=%23$hash_ent">$hash_tag</a>}; } elsif ($token =~ m!^\@(.+)$!) { my $user = encode_entities($1); $html .= qq{<a href="http://twitter.com/$user">\@$user</a>}; } else { $html .= encode_entities($token); } } return $html; } exit; __END__