【问题标题】:How do I output HTML in a Blade template without chancing an XSS attack?如何在不更改 XSS 攻击的情况下在 Blade 模板中输出 HTML?
【发布时间】:2021-08-20 22:38:15
【问题描述】:

如果变量包含以下内容:

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

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

<td>{{ $straddr }}</td>

但这会显示 HTML 标签。

但是,the documentation

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

Hello, {!! $name !!}.

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

不逃避来自数据库的数据是否过于冒险?

【问题讨论】:

  • 这取决于用例以及您对存储在数据库中的数据的感受。它非常适合由所见即所得创建的动态内容的博客和页面。如果它仅由登录用户创建(例如仅由员工创建的博客或内容),则无需担心。如果它是由任何人生成的内容,那么肯定是有风险的,而且可能无论如何都不应该有 html 标签。
  • 考虑使用完整 HTML 解析器的白名单驱动,例如 htmlpurifier.org。有一个 Laravel 包:github.com/mewebstudio/Purifier

标签: php laravel laravel-blade


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

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

    假设您有一个 User 模型,其属性为 address_1address_2address_3citystatezip_code,这就是您需要做的。

    <td>
    @if($user->address_1)
        {{ $user->address_1 }}<br/>
        @if($user->address_2) {{ $user->address_2 }}<br/>@endif
        @if($user->address_3) {{ $user->address_3 }}<br/>@endif
    @else
        <i>{{ __("Street address unknown") }}</i><br/>
    @endif
    @if($user->city || $user->state)
        {{ $user->city ?? "?" }}, {{ $user->state ?? "?" }}
    @else
        <i>City and state unknown</i>
    @endif&nbsp;&nbsp;{{ $this->zip_code ?? "" }}
    </td>
    

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

    【讨论】:

    • strip_tags 的文档有一个大大的红色警告,指出 “此函数不应用于尝试防止 XSS 攻击。” &lt;strong onmouseover="alert('foo')"&gt; 仍将执​​行其如果&lt;strong&gt; 是允许的标签,则运行strip_tags 之后的JS。
    • @ceejayoz 好点。可能不得不重新考虑这个答案
    • 同意@ceejayoz,您应该只使用条件语句来执行此操作。你的问题没有提到这个变量是你构造的,我以为是用户输入的。
    • 您的刀片模板应该处理格式。您的控制器不应该关心它返回的数据是如何呈现的,并且根本不应该处理 HTML 标记。我认为这整个问题只是一个大的 XY 问题,真正的问题是你有什么数据,它来自哪里?如果您是添加 HTML 的人,那么您无需担心。
    • @ceejayoz 我已经更新了我的答案以摆脱strip_tags 的建议;感谢您指出它的问题。
    【解决方案3】:

    好的,就是这样。 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') : '';
            $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) : '' ;
    
            $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,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);
        }
    

    所以在你把安全的标签放在后端之前,把所有的标签都去掉。

    【讨论】:

    • 不要使用strip_tags。阅读php.net/strip_tags 上的红框。它不能防止某些形式的 XSS。
    • 我同意。那么如何才能实现目标呢?目标是我有三个地址字段:[address_1]、[address_2]、[address_3],然后是 [city]、[state]、[zip] 字段/列。我从数据库中检索这些,我想在屏幕上显示为屏幕上单独行的地址。如果不使用 strip_tags 刮掉
      标签,你怎么能做到这一点。是在带有 if 条件的刀片模板中执行此操作的唯一答案吗? (当 address_2 和 address_3 为空值时,不要有空行。
    • 如果你有底层地址部分,是的,我会在 Blade 模板本身中做。
    • 绝非易事。
    • 你是说你将这个 HTML 存储在数据库中吗?这根本不是一个好主意。 HTML 是一种显示数据的方法;存储应该是不可知的。
    猜你喜欢
    • 1970-01-01
    • 2018-03-02
    • 2019-10-14
    • 2016-09-09
    • 1970-01-01
    • 1970-01-01
    • 2020-08-28
    • 1970-01-01
    • 2015-05-18
    相关资源
    最近更新 更多