Parsedown 解析 Markdown 过滤 XSS 时应如何处理 html 转义

用 Parsedown 对 Markdown 进行解析的时候,遇到了一些 XSS 过滤方面的问题。

发现 Parsedown 会对 代码 区域内的 html 代码进行转义,代码区域外的却不进行转义,如以下代码所示

PHP<?php
include 'Parsedown.php';
$test = "```\n<script>alert('test')</script>\n```\n<script>alert('test')</script>";
$Parsedown = new Parsedown();
echo $Parsedown->text($test);

/**
  * 得到结果是:
  * 

<pre><code>&lt;script&gt;alert('test')&lt;/script&gt;</code></pre>


  * <script>alert('test')</script>
  *
  */

这样,<script>alert('test')</script>这句还是被成功执行了


既然如此,那我先自己给它转义一下

PHP<?php
include 'Parsedown.php';
include 'com.func.php';
$test = "```\n<script>alert('test')</script>\n```\n<script>alert('test')</script>";
$test = htmlspecialchars($test, ENT_QUOTES);
$Parsedown = new Parsedown();
echo $Parsedown->text($test);

/**
  * 得到结果是:
  * 

<pre><code>&amp;lt;script&amp;gt;alert(&amp;#039;test&amp;#039;)&amp;lt;/script&amp;gt;</code></pre>


  * 

<p>&lt;script&gt;alert(&#039;test&#039;)&lt;/script&gt;</p>


  */

虽然 XSS 是被过滤掉了,但是代码区域的内容就被转义了两次

然后我发现 SF 的 Markdown 好像是在后端就解析好的,例如这个页面,它的部分源码如下:

html

<p>我现在的代码是这样的<br> &lt;html&gt;<br> &lt;head&gt;<br><br> &lt;title&gt;Untitled Document&lt;/title&gt;</p> <pre><code>&lt;script&gt; function test(){ &lt;?php $conn = mysql_connect("localhost", "username", "123123"); mysql_select_db("username", $conn); mysql_query("INSERT INTO ChargerTogether (Chat) VALUES ('test')"); $result= mysql_query("SELECT *FROM ChargerTogether"); echo "&lt;p&gt; {$result} &lt;/p&gt;&gt;"; ?&gt; } &lt;/script&gt; </code></pre> <p>&lt;/head&gt;<br> &lt;body&gt;<br> &lt;input type="button" onClick="javascript:test();"&gt;</p> <p>&lt;/body&gt;<br> &lt;/html&gt;</p> <p>但这样是错误的<br> 我该怎样做呢</p>

这里的转义就做得很好。


我想到的解决方案是:先转义,再用 Parsedown 进行解析,接着正则匹配出解析后 <code></code> 内的代码,对其进行反转义(一次)。

不知道是不是还有别的更好的转义函数,或者说 有没有其他的能够过滤 XSS 的 Markdown 解析类

P.S. 我在 github 上面找到有个 markdown-js。用这个东西的话可以把经htmlspecialchars(()转义后的字符串直接输出到一个 textaera 里,它能够正确地解析。虽然我可以把那个文本框隐藏起来,但是如果需要输出很多段 Markdown (帖子有很多回复)的话,那可不是一个好的解决方案啊!

阅读 7.1k
2 个回答

刚刚把 Parsedown 源码里所有(共三处)转义用的语句(如下所示)给注释掉,

PHP$text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');

这句出现在了以下三个方法中:
protected function blockCodeComplete($Block)protected function blockFencedCodeComplete($Block)protected function inlineCode($Excerpt)

然后再修改一下 function text($text)

function text($text)
    {
        #添加下面这行
        $text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
        # Code...
    }

这么做虽然说是解决了,但是可能不是最完美的解决方案,还请各位大大继续提供更好的方案~

新手上路,请多包涵

我也遇到这个问题了,后来发现,只要把代码块外面的 <script></script> 写成<script/></script/>即可,这样就会转换成文字了。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏