在我的php应用程序中,我有这个textarea框,它接收来自用户的markdown(如stackoverflow' s),然后它显示在网站上。我正在使用Laravel框架并使用parsedown-laravel包 我能做到:
{!! Markdown::parse('__Hello__ Markdown!'); !!}
有效。
{!! Markdown::parse('<h1>Hello</h1> Markdown!'); !!}
它仍然有效。我对此很满意。
现在,如果我这样做:
{!! Markdown::parse('<script>alert("XSS Attack!!!")</script> Markdown!'); !!}
它仍然有效!!!
如何使用Laravel和此软件包阻止我的应用中的脚本标记?
答案 0 :(得分:5)
如果您查看Markdown规范(original syntax by Jon Gruber或CommonMark),您会发现Markdown不应该替换HTML。它的唯一目标是让您更容易阅读您编写的文本。由于Markdown仅涵盖HTML标签的一小部分,因此您仍然可以使用内联HTML代码来创建您想要的内容。事实上,John Gruber说:
对于Markdown语法未涵盖的任何标记,您只需使用HTML本身。没有必要为它添加前缀或分隔它以表明您正在从Markdown切换到HTML;你只需使用标签。
基本上,这就是Markdown的工作方式。显然,如果您正在解析用户的输入,则不应该这样。由于Markdown解析器输出HTML代码,因此您无法使用htmlentities
函数或类似的解决方案。
解决问题的最简单方法是使用像HTML Purifier这样的HTML过滤库。这将从Markdown输出中删除恶意代码,并尝试阻止XSS攻击。基本上,您应首先调用Markdown解析器,并使用该输出调用HTML Purifier库。
答案 1 :(得分:3)
原始的 Parsedown 库有一个转义html的选项:
echo Parsedown::instance()
->setMarkupEscaped(true) # escapes markup (HTML)
->text("<div><strong>*Some text*</strong></div>");
# Output:
# <p><div><strong><em>Some text</em></strong></div></p>
来自Parsedown Tutorial: Get Started
据推测,由于 parsedown-laravel 只是一个包装器,您应该能够访问该选项。
显然这会禁用所有标签而不是特定标签。
Parsedown bugtracker 上的moldcraft上的GitHub用户issue 229 - Disable parsing of specific elements提供了following code,可以为解决方案铺平道路:
moldcraft评论于2月24日•2015-02-24 18:41:31 +0100
对某些人可能有用:我也使用Parsedown进行用户评论,我想用h4替换所有h1,h2,h3以防止SEO警告(例如,页面上只有一个h1),这是我的Symfony2服务
<?php
namespace App\MainBundle\Service;
use Parsedown;
use HTMLPurifier;
use Emojione\Emojione;
use Symfony\Component\DependencyInjection\ContainerInterface;
class Markdown extends Parsedown
{
/**
* @var HTMLPurifier
*/
private $purifier;
public function __construct(ContainerInterface $container)
{
$this->setMarkupEscaped(true);
{
$purifierConfig = array(
'HTML.ForbiddenElements' => array('h1', 'h2', 'h3'),
'HTML.ForbiddenAttributes' => array('style', 'onclick',),
'HTML.TargetBlank' => true,
);
$this->purifier = new HTMLPurifier($purifierConfig);
}
{
Emojione::$imageType = 'svg';
Emojione::$sprites = true;
Emojione::$imagePathSVGSprites = $container->get('templating.helper.assets')->getUrl(
'bundles/appmain/emojione/sprites/emojione.sprites.svg'
);
Emojione::$ascii = true;
}
}
function text($raw)
{
return Emojione::shortnameToImage(
$this->purifier->purify(
parent::text($raw)
)
);
}
private function safeHeader($Block)
{
if ($Block && isset($Block['element'])) {
/**
* Change h1, h2, h3 to h4
*/
if (in_array($Block['element']['name'], array('h1', 'h2', 'h3'))) {
$Block['element']['name'] = 'h4';
}
}
return $Block;
}
protected function blockHeader($Line)
{
return $this->safeHeader(
parent::blockHeader($Line)
);
}
protected function blockSetextHeader($Line, array $Block = null)
{
return $this->safeHeader(
parent::blockSetextHeader($Line, $Block)
);
}
}
答案 2 :(得分:-2)
接受用户输入并将其与应用程序代码无缝集成永远不会是安全的。这是不行的。
如果这只是显示代码,那么您可以使用<textinput>
标记来执行此操作。您可以设置样式,使其看起来不像输入。或者您只需将htmlescape()
这样的函数与<pre>
标记结合使用。