如何在不发生 XSS 攻击的情况下在 Blade 模板中输出 HTML?

问题描述

如果变量包含以下内容

$sTraddr = "1 elm street<br>Unit 4<br><i>City UnkNown</i>Alabama";

并且我想将该变量包含在 Blade 模板中,我可以这样做:

<td>{{ $sTraddr }}</td>

但这将显示 HTML 标签

然而,the documentation

认情况下,Blade {{ }} 语句会通过 PHPhtmlspecialchars 函数自动发送,以防止 XSS 攻击。如果您不希望您的数据被转义,您可以使用以下语法:

Hello,{!! $name !!}.

在刀片文件中使用 {!! $variable !!} 语法似乎非常危险。还是在这种情况下?

不转义来自数据库的数据是否风险太大?

解决方法

在评论中,我们确定此 HTML 实际上是由您在您的控制器方法中生成的。所以真正的问题是,你为什么要为了颠覆 MVC 范式而大费周章?

假设您有一个 User 模型,其属性为 address_1address_2address_3citystate 和 {{1} },这就是你需要做的。

zip_code

您提供的其余代码正在执行数据验证,绝对不应在数据生命周期的这个阶段进行。您的数据应在进入应用程序时进行验证,无论是用户输入、CSV 导入还是其他任何方式。您不必将城市名称大写,确定您的地址是否超过 3 个字符,或者在显示时想知道是否有州的缩写或全名。

,

用户提供的任何数据,即使存储在中间的数据库中,默认情况下也应被视为不安全。在这种特殊情况下,您面临着存储型 XSS 的风险。

您应该确保您的数据可能是

  1. 转义(htmlspecialcharsstrip_tags 等)和
  2. 已验证(您能否强制执行模式、字符限制(仅限 A-Z 或数字)或长度限制?)
,

好的,就是这样。在 Blade 组件或模板中使用的 {{ }} 将始终将标签呈现为 html 实体。所以关键是,正如上面的 miken32 所说,在返回字符串之前简单地在字符串上使用 strip_tags,然后使用 {!! !!} 将
标记呈现为实际的中断标记而不是 html 实体。

谢谢 miken 和 speckulatius。


好的,我确信 strip_tags 不是一个好主意。所以这里又是一个问题,也许是一个答案。答案之一就是编辑 Blade 模板,这样您就可以将
标签直接放在模板中,而不是冒着将它放在数据库中的风险。但我认为它仍然可以在后端完成。这是我用来在数据表中呈现某人的地址的函数。

public function getFullAddressFhAttribute(){
        $a1 = (!is_null($this->address_1) && strlen(trim($this->address_1))>3) ? ucWordsSpace(trim($this->address_1),'addr') : '';
        $a2 = (!is_null($this->address_2) && strlen(trim($this->address_2))>3) ? ucWordsSpace(trim($this->address_2),'addr') : '';
        $a3 = (!is_null($this->address_3) && strlen(trim($this->address_3))>3) ? ucWordsSpace(trim($this->address_3),'addr') : '';
        $c =  (!is_null($this->city) && strlen(trim($this->city))>2) ? Str::title(trim($this->city)) : '';
        $s = (!is_null($this->state) && strlen(trim($this->state))>1) ? trim($this->state) : '';
        $s = (strlen($s)==2) ? strtoupper($s) : Str::title($s);
        $z = (!is_null($this->zip_code) && strlen(trim($this->zip_code))>4) ? formatzip($this->zip_code) : '' ;

        $address = (strlen($a1)>3) ? $a1 : '';
        $address .= (strlen($address)>3 && strlen($a2)>3) ? "<br>$a2" : '';
        $address .= (strlen($address)>3 && strlen($a3)>3) ? "<br>$a3" : '';
        $address = (strlen($address)>3) ? $address . "<br>" : "<i>Street Addr Unknown</i><br>";
        if(strlen($c.$s)==0)
            $address .= "<i>City,State Unknown</i>";
        else{
            $address .= (strlen($c)>2) ? "$c," : "?,";
            $address .= (strlen($s)>1) ? " $s" : " ?";
        }
        $address .= (strlen($z)>4) ? "  $z" : '';

        $safeElements = ["<br>","<b>","<i>"];
        return strip_tags($address,$safeElements);
    }

我认为上面的函数如果重写为前 7 个变量的 strip_tags 会更安全,就像这样:

 public function getFullAddressFhAttribute(){
        $a1 = (!is_null($this->address_1) && strlen(trim($this->address_1))>3) ? ucWordsSpace(trim($this->address_1),'addr') : '';
        $c =  (!is_null($this->city) && strlen(trim($this->city))>2) ? Str::title(trim($this->city)) : '';
        $s = (!is_null($this->state) && strlen(trim($this->state))>1) ? trim($this->state) : '';
        $s = (strlen($s)==2) ? strtoupper($s) : Str::title($s);
        $z = (!is_null($this->zip_code) && strlen(trim($this->zip_code))>4) ? formatzip($this->zip_code) : '' ;

        $a1 = strip_tags($a1);
        $a2 = strip_tags($a2);
        $a3 = strip_tags($a3);
        $c = strip_tags($c);
        $s = strip_tags($s);
        $z = strip_tags($z);
        
        $address = (strlen($a1)>3) ? $a1 : '';
        $address .= (strlen($address)>3 && strlen($a2)>3) ? "<br>$a2" : '';
        $address .= (strlen($address)>3 && strlen($a3)>3) ? "<br>$a3" : '';
        $address = (strlen($address)>3) ? $address . "<br>" : "<i>Street Addr Unknown</i><br>";
        if(strlen($c.$s)==0)
            $address .= "<i>City,$safeElements);
    }

因此,在将安全标签放到后端之前,先去掉所有标签。