一部のHTMLタグを通すフィルタ
Posted feedbacks - PHP
br以外の通したタグの閉じ忘れにも対応してみた。 (閉じる順番が間違ってる場合は今回は大目に見るとして。)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | <?php
function safehtml($str)
{
$r=array();
$tags=array();
$offs=0;
while(preg_match('!<\s*(/|)\s*(([^>\'"]+|\'[^\']*\'|"[^"]*")*)>!',$str,$m1,PREG_OFFSET_CAPTURE,$offs))
{ $r[]=substr($str,$offs,$m1[0][1]-$offs);
$offs=$m1[0][1]+strlen($m1[0][0]);
preg_match_all('!([a-z0-9_]+)(\s*=\s*("[^"]*"|\'[^\']*\'|[^\s]+)|)!im',$m1[2][0],$m2,PREG_SET_ORDER);
switch(strtolower($m2[0][1]))
{
case 'a':
case 'strong':
if($m1[1][0])
{ if(($i=array_search($m2[0][1],$tags))!==false)
unset($tags[$i]);
}
else
array_unshift($tags,$m2[0][1]);
$t=array($m1[1][0].$m2[0][1]);
if(strtolower($m2[0][1])=='a' && !$m1[1][0])
{ array_shift($m2);
while($param=array_shift($m2))
{ switch(strtolower($param[1]))
{
case 'href':
case 'name':
$t[]=$param[0];
break;
}
}
}
$r[]='<'.implode(" ",$t).'>';
break;
case 'br':
$r[]='<br/>';
break;
default:
$r[]='<'.$m1[1][0].$m1[2][0].'>';
break;
}
}
$r[]=substr($str,$offs);
while($tag=array_shift($tags)) // 閉じわすれタグを閉じる
$r[]="</$tag>";
return implode("",$r);
}
echo safehtml(<<<EOT
<a href='www.google.com' target=_blank>link</a> <blink>and</blink> <strong onClick='alert("NG")'>click<br/>me!</strong>
EOT
);
?>
|
#2727の指摘に対応。 タグの大文字小文字の問題修正と通すタグに関する情報をまとめて汎用化してみた。 タグは開いたのと逆順に閉じることを強制する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | <?php
function safehtml($str)
{
$safetag=array('a'=>array(1,array('href','name')),'strong'=>array(1),'br'=>array(0));
$r=array();
$tags=array();
$offs=0;
while(preg_match('!<(\s*(/|)\s*(([^>\'"/]+|\'[^\']*\'|"[^"]*")*)(/|)\s*)>!',$str,$m1,PREG_OFFSET_CAPTURE,$offs))
{ $r[]=substr($str,$offs,$m1[0][1]-$offs);
$offs=$m1[0][1]+strlen($m1[0][0]);
preg_match_all('!([^\s\'"=]+)(\s*=\s*("[^"]*"|\'[^\']*\'|[^\s]+)|)!im',$m1[3][0],$m2,PREG_SET_ORDER);
$tag=strtolower($m2[0][1]);
if(isset($safetag[$tag]))
{ if($safetag[$tag][0]&1)
{ if($m1[2][0])
{ if(array_search($tag,$tags)===false)
continue; // 開いてないタグは閉じない
while(($t=array_shift($tags))) // 開いたのと逆順に閉じる
{ $r[]="</$t>";
if($t==$tag)
break;
}
continue;
}
if(!$m1[5][0])
array_unshift($tags,$tag);
}
$t=array($tag);
if(isset($safetag[$tag][1]) && !$m1[2][0])
{ array_shift($m2);
while($param=array_shift($m2))
{ if(array_search(strtolower($param[1]),$safetag[$tag][1])!==false)
$t[]=$param[0];
}
}
$r[]='<'.$m1[2][0].implode(" ",$t).$m1[5][0].'>';
}
else
$r[]=str_replace(array('<','>'),array('<','>'),$m1[0][0]);
}
$r[]=substr($str,$offs);
while(($tag=array_shift($tags))) // 閉じわすれタグを閉じる
$r[]="</$tag>";
return implode("",$r);
}
echo safehtml(<<<EOT
<a href='www.google.com' target=_blank>link</a> <blink dummy='<'>and</blink> <strong onClick='alert("NG")'>click<br/>me!</strong> <z foo='<script>alert("Boo")</script>'>
EOT
);
?>
|




にしお
#3410()
Rating0/0=0.00
このお題はperezvonさんの提案を元にしています。ありがとうございました。 ただ、いきなりだと難しいかと思ったので、肝の部分以外を先に出題しました。このお題は続編で徐々に難しくなっていきます。
追記:属性に<や>が含まれてしまうケースに漏れのある解答が多いようなのでテストケースを追加します。 これは「この出力なら十分」という意味です。この出力の通りでなければいけないという意味ではありません。 <script foo="<script>alert('bar')</script>">alert('foo')</script> <script foo="<script>alert('bar')</script>">alert('foo')</script> <script foo="<a href='link'>link</a>">alert('foo')</script> <script foo="<a href='link'>link</a>">alert('foo')</script> <a href='www.g>oogle.com'>link</a> <a href="./www.g%3Eoogle.com">link</a>[ reply ]