ラボ > PHP:HTML、Javascript絡み、セキュリティ関連

php htmlspecialchars()で「&」はエスケープしたくない

HTMLに出力するとき「&」はエスケープしたくない

作成日:2023-08-18, 更新日:2023-08-18

概要

「<b>A&B</b>」をエスケープしてHTML出力すると「<b>A&amp;B</b>」と出力されるのは困る
「<b>A&B</b>」とHTML出力して欲しい

htmlspecialchars()には特定の文字のみ除外とかは無いので自作する必要がある…

「&」をエスケープしない場合

「&」のみエスケープ処理をしない場合、
・「<」「>」はエスケープ処理するのでscriptタグとして認識されないので問題無し
しかし、「URLエンコード」「クエリ文字列の解釈」にはセキュリティ的に問題が発生する可能性がある

サンプルソース

function custom_escape($str, $charAsIs_ary = array()) {
    if ( !is_array($charAsIs_ary) ) {
        if ( $charAsIs_ary != '' ) {
            $charAsIs_ary = array($charAsIs_ary);
        }
        else {
            $charAsIs_ary = array();
        }
    }

    $flg_replace = false;
    if ( 0 < count($charAsIs_ary) ) {
        $flg_replace = true;
    }

    $pairChar_ary = array();
    $rev_pairChar_ary = array();

    if ( $flg_replace ) {
        $tmp_wordAsIs = '_'; // いったん別の文字に置き換えるための使う基本になる文字
        $wordAsIs = $tmp_wordAsIs; // 置き換えるための文字

        $maxAttempts = 100;
        $currentAttempt = 1;

        while ($currentAttempt <= $maxAttempts) {
            if (strpos($str, $wordAsIs) === false) {
                break;
            }

            $wordAsIs .= $tmp_wordAsIs;
            $currentAttempt++;
        }

        if ($maxAttempts < $currentAttempt) { // 指定回数を超えたらエラーにする
            return array(false, $str);
        }

        foreach ($charAsIs_ary as $k => $row_char) {
            $rev_word = $wordAsIs . $k . $wordAsIs;
            $pairChar_ary[$row_char] = $rev_word;
            $rev_pairChar_ary[$rev_word] = $row_char;
        }

        $str = strtr($str, $pairChar_ary);
    }

    $fix_str = htmlspecialchars($str, ENT_COMPAT | ENT_HTML5);

    if ( $flg_replace ) {
        $fix_str = strtr($fix_str, $rev_pairChar_ary);
    }

    return array(true, $fix_str);
}

// エスケープせずにそのままにしておきたい文字
$charAsIs_ary = array(
    '&',
);

// エスケープ処理したい文字列
$str = '<>&abc"';
list($sts, $esc_str) = custom_escape($str, $charAsIs_ary);

if ($sts) {
    echo $esc_str;
}
else {
    echo 'error';
}

関連項目