laravelで、特定のhtmlタグのみサニタイズしない処理を実装したのでまとめます。 ブログの記事投稿機能などで、特定のhtmlタグのみ利用者に使用を許可したい場合などに使う想定です。

設定ファイルに許可するhtmlタグ一覧を定義し、 そのhtmlタグはサニタイズしないという方式で実装しました。

設定ファイル(config.general.php)

<?php

return [
  'apply_tags' => [
    'h1', 'h2', 'h3', 'h4', 'h5',
    'b', 'a', 'img', 'u', 'br',
    'table', 'tr', 'th', 'td', 'tbody', 'thead',
  ]
];
?>

ここにサニタイズしないhtmlタグを配列で定義します。 これ以外のタグはサニタイズ対象になります。

App/Utils/TagSanitize.php

<?php

namespace App\Utils;

use App;
use Config;
use Log;

class TagSanitize
{
    /**
     * サニタイズを行う。
     * @param string $content サニタイズを行う文字列
     * @param array $apply_tags サニタイズを行わない(タグ使用を許可する)タグ名一覧
     * @return string サニタイズ後の文字列
     */
    public static function sanitize($content, $apply_tags) {
        Log::debug('TagSanitize sanitize()');
        Log::debug($content);
        Log::debug($apply_tags);
        $content = htmlspecialchars($content);

        if (!is_array($apply_tags) || count($apply_tags) == 0 ) return $content;

        foreach($apply_tags as $tag) {
            if (strpos($tag, '/') === false) {
                $content = preg_replace_callback("/<\/?". $tag . "( .*?>|\/?>)/i",
                    function ($matches) {
                        $target_str = $matches[0];
                        $target_str = str_replace("<", "<", $target_str);
                        $target_str = str_replace(">", ">", $target_str);
                        $target_str = str_replace(""", "\"", $target_str);
                        return $target_str;
                    },
                    $content);
            }
        }
        Log::debug($content);
        return $content;
    }

    /**
     * ページの本文で使用できるタグの一覧を取得する
     * @return array サニタイズしないタグ一覧
     */
    public static function getContentApplyTagList() {
        return Config::get('general.apply_tags');
    }
}

サニタイズ処理を行うクラスです。

使い方

上記の二つのファイルをControllerで使用する場合の例です。

use App\Utils\TagSanitize;

~~省略~~
    public function index(Request $request) {
        $content = "<b>テスト</b><br><div>タグテストです</div>";

        // サニタイズ
        $apply_tags = TagSanitize::getContentApplyTagList();
        $content = TagSanitize::sanitize($content, $apply_tags);
    }

この例だと、 b, brタグはConfigのapply_tags配列にあるのでサニタイズされませんが、divタグはサニタイズされます。

注意点

設定ファイルの更新や、名前空間の更新をしないと正しく読み込めないかもしれないので、 エラーが発生する場合はこちらのコマンドを打ってみてください。

$ php artisan config:cache
$ composer dump-autoload

参考

こちらのサイトを参考にさせていただきました、ありがとうございます。