【PHP】HTMLエスケープ処理を一部タグを除外してエスケープ処理する方法
2023年11月20日2023年11月20日
ウェブ開発において、HTMLエスケープはXSS攻撃などのセキュリティリスクから保護する重要な手法です。
しかし、特定のHTMLタグを許可したい場合があります。
この記事では、HTMLエスケープ処理を行いながら、特定のタグを安全に除外する方法を紹介します。
エスケープ処理で実現したいこと
今回エスケープ処理で実現したかった要件は以下になります。
- セキュリティリスクからすべてのHTMLタグをエスケープする
- 一部のタグを除外できるように汎用的にする
- classやid属性を指定した場合も影響でないようにする
■ エスケープ前
<div class="test"><a href="#" class="btn">詳細はこちら</a></div>
■ エスケープ後
<div class="test"><a href="#" class="btn">詳細はこちら</a></div>
エスケープ処理(サニタイジングまたはサニタイズ)とは、セキュリティリスクを防ぐために、コード内の特殊文字を無害化する処理です。これにより、XSS攻撃などの脆弱性からアプリケーションを保護します。
エスケープ関数を作成する
コピペで使用できるようにしています。下記をコピペして使用してください。
function escape_except_allowed_tags($text, $applyTags = [])
{
if (!is_array($applyTags) || 0 == count($applyTags)) {
return $text;
}
// 許可されたHTMLタグ
$tagsPattern = implode('|', array_map('preg_quote', $applyTags));
// エスケープ
$text = htmlspecialchars($text);
// 許可されたタグを元に戻す(開始タグ)
$text = preg_replace_callback(
"/<(" . $tagsPattern . ")(.*?)>/i",
function ($matches) {
return "<" . $matches[1] . htmlspecialchars_decode($matches[2]) . ">";
},
$text
);
// 許可されたタグを元に戻す(閉じタグ)
$text = preg_replace_callback(
"/<\/(" . $tagsPattern . ")>/i",
function ($matches) {
return "</" . $matches[1] . ">";
},
$text
);
return $text;
}
まず、すべてのタグに対してエスケープ処理を行い、次に引数で指定された特定のHTMLタグについてのみエスケープ処理を解除します。
例えば、「a」、「strong」、「span」タグをエスケープ処理から除外したい場合は、これらを引数として渡してください。
escape_except_allowed_tags($text,['a','strong','span']);
汎用的に使用できるように引数指定できるようにしています。もし固定にしたいのであれば引数を削除してハードコーディングしてください。
出力例
■ a、strong、spanのみを許可
$text = '<div class="test"><strong>太文字</strong><p><span>文章</span>が入ります。</p></div>';
echo escape_except_allowed_tags($text,['a','strong','span']);
// 出力
<div class="test"><strong>太文字</strong><p><span>文章</span>が入ります。</p></div>
■ aタグに属性がある場合
$text = '<div class="test"><a href="/" id="link" class="link">詳細はこちらから</a></div>';
echo escape_except_allowed_tags($text,['a']);
// 出力
<div class="test"><a href="/" id="link" class="link">詳細はこち らから</a></div>
最後に
HTMLエスケープ処理を一部タグを除外してエスケープ処理する方法について紹介しました。
他のブログ等ではHTMLタグは除外はできたけどclass属性やID属性が上手く処理できていなかったりと使えるものがありませんでした。
結局自分で作ったほうが早かったです…。
コメント
コメントを残す