HTML Purifier - iframe and scripts - php

I'm using HTML Purifier in my project.
My html is something like this. (containing simple html element + script + iframe)
<p>content...<p>
<iframe></iframe>
<script>alert('abc');</script>
<p>content2</p>
With default config, it turned into this
<p>content...</p>
<p></p>
<p>Content2</p>
But if I set the config like this...
$config->set('HTML.Trusted', true);
$config->set('HTML.SafeIframe', true);
I got this
<p>content...</p>
<p>
<iframe></iframe>
<script type="text/javascript"><!--//--><![CDATA[//><!--
alert('abc');
//--><!]]></script>
</p>
<p>content2</p>
Is there anyway to use HTML Purifier to completely remove 'script' tag but preserve 'iframe' tag? Or other alternative to HTML Purifier?
I've tried
$config->set('Filter.YouTube', true);
$config->set('URI.SafeIframeRegexp', '%^https://(www.youtube.com/embed/|player.vimeo.com/video/)%');
But it turned out that the 'script' tag still there.
[edited]
full example.
$config = HTMLPurifier_Config::createDefault();
$html = "<p>content...<p><iframe ...></iframe><script>alert('abc');</script><p>content2</p>";
$config->set(
'HTML.ForbiddenElements',
'script'
);
$purifier = new HTMLPurifier($config);
$clean_html = $purifier->purify($html);
Result
<p>content...</p><p></p><p>content2</p>

You were half on the right track. If you set HTML.SafeIframe to true and URI.SafeIframeRegexp to the URLs you want to accept (%^https://(www.youtube.com/embed/|player.vimeo.com/video/)% works fine), an input example of:
<p>content...<p>
<iframe src="https://www.youtube.com/embed/blep"></iframe>
<script>alert('abc');</script>
<p>content2</p>
...turns into...
<p>content...</p><p>
<iframe src="https://www.youtube.com/embed/blep"></iframe>
</p><p>content2</p>
Explanation: HTML.SafeIframe allows the <iframe> tag, but HTML Purifier still expects a whitelist for the URLs that the iframe can contain, since otherwise an <iframe> opens too much malicious potential. URI.SafeIframeRegexp supplies the whitelist (in the form of a regex that needs to be matched).
See if that works for you!
Code
This is the code that made the transformation I just mentioned:
$dirty = '<p>content...<p>
<iframe src="https://www.youtube.com/embed/blep"></iframe>
<script>alert(\'abc\');</script>
<p>content2</p>';
$config = HTMLPurifier_Config::createDefault();
$config->set('HTML.SafeIframe', true);
$config->set('URI.SafeIframeRegexp', '%^https://(www.youtube.com/embed/|player.vimeo.com/video/)%');
$purifier = new HTMLPurifier($config);
$clean = $purifier->purify($dirty);
Regarding HTML.Trusted
I implore you to never set HTML.Trusted to true if you don't fully trust each and every one of the people submitting the HTML.
Amongst other things, it allows forms in your input HTML to survive the purification unmolested, which (if you're purifying for a website, which I assume you are) makes phishing attacks trivial. It allows your input to use style tags which survive unscathed. There are some things it will still strip (any HTML tag that HTML Purifier doesn't actually know anything about, i.e. most HTML5 tags being some of them, various JavaScript attribute handlers as well), but there are enough attack vectors that you might as well not be purifying if you use this directive. As Ambush Commander once put it:
You shouldn't be using %HTML.Trusted anyway; it really ought to be named %HTML.Unsafe or something.

Consider using a full-fledged HTML parser like Masterminds html5-php. HTML code would then be parsed without undesired alterations like wrapping IFRAME in P, and you would be able to manipulate the resulting DOM tree the way you want, including removing some elements while keeping other ones.
For example, the following code could be used for removing SCRIPT elements from the document:
foreach ($dom->getElementsByTagName('script') as $script) {
$script->parentNode->removeChild($script);
}
And note that code like this:
<script type="text/javascript"><!--//--><![CDATA[//><!--
alert('abc');
//--><!]]></script>`
is obsolete. The modern HTML5 equivalent code is :
<script>alert('abc');</script>
exactly as in your source code before being processed by HTML Purifier.

Related

HtmlPurifier - allow data attribute

I'm trying to allow some data-attribute with htmlPurifier for all my span but no way...
I have this string:
<p>
<span data-time-start="1" data-time-end="5" id="5">
<word class="word">My</word>
<word class="word">Name</word>
</span>
<span data-time-start="6" data-time-end="15" id="88">
<word class="word">Is</word>
<word class="word">Zooboo</word>
</span>
<p>
My htmlpurifier config:
$this->HTMLpurifierConfigInverseTransform = \HTMLPurifier_Config::createDefault();
$this->HTMLpurifierConfigInverseTransform->set('HTML.Allowed', 'span,u,strong,em');
$this->HTMLpurifierConfigInverseTransform->set('HTML.ForbiddenElements', 'word,p');
$this->HTMLpurifierConfigInverseTransform->set('CSS.AllowedProperties', 'font-weight, font-style, text-decoration');
$this->HTMLpurifierConfigInverseTransform->set('AutoFormat.RemoveEmpty', true);
I purify my $value like this:
$purifier = new \HTMLPurifier($this->HTMLpurifierConfigInverseTransform);
var_dump($purifier->purify($value));die;
And get this :
<span>My Name</span><span>Is Zoobo</span>
But how to conserve my data attributes id, data-time-start, data-time-end in my span ?
I need to have this :
<span data-time-start="1" data-time-end="5" id="5">My Name</span data-time-start="6" data-time-end="15" id="88"><span>Is Zoobo</span>
I tried to test with this config:
$this->HTMLpurifierConfigInverseTransform->set('HTML.Allowed', 'span[data-time-start],u,strong,em');
but error message :
User Warning: Attribute 'data-time-start' in element 'span' not
supported (for information on implementing this, see the support
forums)
Thanks for your help !!
EDIT 1
I tried to allow ID in the firdt time with this code line:
$this->HTMLpurifierConfigInverseTransform->set('Attr.EnableID', true);
It doesn't work for me ...
EDIT 2
For data-* attributes, I add this line but nothing happened too...
$def = $this->HTMLpurifierConfigInverseTransform->getHTMLDefinition(true);
$def->addAttribute('sub', 'data-time-start', 'CDATA');
$def->addAttribute('sub', 'data-time-end', 'CDATA');
HTML Purifier is aware of the structure of HTML and uses this knowledge as basis of its white-listing process. If you add a standard attribute to a whitelist, it doesn't allow arbitrary content for that attribute - it understands the attribute and will still reject content that makes no sense.
For example, if you had an attribute somewhere that took numeric values, HTML Purifier would still deny HTML that tried to enter the value 'foo' for that attribute.
If you add custom attributes, just adding it to the whitelist does not teach HTML Purifier how to handle the attributes: What data can it expect in those attributes? What data is malicious?
There's extensive documentation how you can tell HTML Purifier about the structure of your custom attributes here: Customize
There's a code example for the 'target' attribute of the <a>-tag:
$config = HTMLPurifier_Config::createDefault();
$config->set('HTML.DefinitionID', 'enduser-customize.html tutorial');
$config->set('HTML.DefinitionRev', 1);
$config->set('Cache.DefinitionImpl', null); // remove this later!
$def = $config->getHTMLDefinition(true);
$def->addAttribute('a', 'target', 'Enum#_blank,_self,_target,_top');
That would add target as a field that accepts only the values "_blank", "_self", "_target" and "_top". That's a bit stricter than the actual HTML definition, but for most purposes entirely sufficient.
That's the general approach you will need to take for data-time-start and data-time-end. For possible configuration, check out the official HTML Purifier documentation (as linked above). My best guess from your example is that you don't want Enum#... but Number, like this...
$def->addAttribute('span', 'data-time-start', 'Number');
$def->addAttribute('span', 'data-time-end', 'Number');
...but check it out and see what suits your use-case best. (While you're implementing this, don't forget you also need to list the attributes in the whitelist as you're currently doing.)
For id, you should include Attr.EnableID = true as part of your configuration.
I hope that helps!
If anyone else lands here (like I did) for the id attribute not working, and more weirdly not working in all cases.
In version 4.8.0 Attr.ID.HTML5 was added and reflects the usage of relaxed format introduced for HTML5.
For example, numeric values were not allowed, as well as values that start with a number. The following examples are all valid in HTML5, but only the first three are valid for pre-HTML5 (the default behaviour of the purifier):
foo (both)
foo-bar (both)
foo-10 (both)
10 (HTML5 only)
10-foo (HTML5 only)

HTMLPurifier without htmlspecialchars

I am using HTMLPurifier for simple Tinymce WYSIWYG.If I don't use htmlspecialchars,would it be open to XSS Attack?This is what I'm doing
$detail = $purifier->purify($detail);
to purify data for that textarea.If I use htmlspecialchars,it strips all basic tags as well which is not user friendly for an WYSIWYG editor.But the problem is,this allows <script> tag as well.
And if I change conf setting to
$config->set('ExtractStyleBlocks.1', true);
It doesn't allow < and > for <script> tag.Convert < and > for <script> only.But it shows <p>This is paragraph</p> ,<strong>This text is bold</strong> and so on.It shouldn't show <p> and other simple tags to user,but only the text.
How can I get rid of this problem.
Please help.Thanks for your time.
Edit
Here is my HTMLPurifier initialization
$config = HTMLPurifier_Config::createDefault();
//$config->set('ExtractStyleBlocks', true);
$config->set('HTML.ForbiddenElements', array('script','style','applet'));
$purifier = new HTMLPurifier($config);
getting data from database
while(mysqli_stmt_fetch($stmt1)){
$id=htmlspecialchars($id);
$title=htmlspecialchars($title);
$detail = $purifier->purify($detail);
$posts.="<div id='date_news'><div id='news_holder$id' class='news_holder'><h3 id='show_title'>".htmlspecialchars($title)."</h3>".$detail."</div>";
HTML for $detail
At Database
<p><strong>Alu Vazi</strong></p>
<p>I love alu vazi with<script>alert("XSS")</script></p>
User screen
Alu Vazi
I love alu vazi with<script>alert("XSS")</script>
OK, following my comment try adding this to your HTML Purifier config, it should be enabled by default, but worth a shot.
$config = HTMLPurifier_Config::createDefault();
$config->set('HTML.ForbiddenElements', array('script','style','applet'));
$purifier = new HTMLPurifier($config);
Edit
<p>I love alu vazi with<script>alert("XSS")</script></p>
You've already escaped the <script> tag here so HTML Purifier has nothing to parse. It will be output on the page as a result but you have effectively neutralised the XSS attempt.
In your code something is already escaping HTML characters before saving to the database.

configure htmlpurifier with html5 and css3

I am using htmlpurifier. I have some doubts which are as below.
1- My config file contain
$config->set('HTML.Trusted' ,true);
$config->set('CSS.Trusted', true);
But a simple Google is landing me to pages where there are recommendation not to use *.Trusted as "true".
I am not able to understand why should we should not set *.Trusted to true? Can you please explain me. Because if I remove it than I wont get inline css? Even CSS.AllowTricky is not helping.
2- I found that HTML5 and CSS3 selectors are not allowed.
like the code at htmlpurifier/library/HTMLPurifier/Filter/ExtractStyleBlocks.php
// - No Unicode support
// - No escapes support
// - No string support (by proxy no attrib support)
// - element_name is matched against allowed
// elements (some people might find this
// annoying...)
// - Pseudo-elements one of :first-child, :link,
// :visited, :active, :hover, :focus
// handle ruleset
$selectors = array_map('trim', explode(',', $selector));
$new_selectors = array();
foreach ($selectors as $sel) {
//some code to filter css selectors
}
do not contain any code which can allow selectors like '[class*="grid-"]'. Hence all such css is getting removed after purification. Is ther some way to allow all CSS3?
3- Is there some way to allow all HTML 5 tags? for example if we have html like
<section class="mainhead">
<div class="subhead"> </div> </section>
then purifier removes and due to which some css like
.mainhead .subhead { //some css}
wont work.
Setting HTML.Trusted to true enables unsafe elements, such as script and forms. If you, for example, want to allow forms, but don't want to allow scripts, just add them to HTML.ForbiddenElements:
$config->set('HTML.Trusted', true);
$config->set('HTML.ForbiddenElements', ['script']);
CSS3 selectors are not supported - there is a pending Pull Request, but it has been inactive for more than a year (as of Aug 2019). I don't think this will change any soon.
As for HTML5 support - you can use an extension package https://github.com/xemlock/htmlpurifier-html5 (which I'm the author of), which adds spec compliant definitions of HTML5 elements. It's usage is almost the same the bare HTML Purifier - you just have to replace HTMLPurifier_Config with HTMLPurifier_HTML5Config.

How to make a link in a mysql stored variable clickable when rendered on the page

I have a function that enables members on a site to message each other; the message is stored in mysql database.
My question now is this: what is the best way to allow members to include a link in the message so that, when rendered, it is rendered as a click-able link.
I've tried the following:
click here
but when I then tried to render it on the page it came out as:
$message = nl2br($this->escapeHtml(trim($this->theMessage[0]['message'])));
echo $message; // click here
the var_dump Values of $messages is:
string '<a href="testpage.html"> click here</a>'
HTML markup is complicated, because when displaying it to the user and someone has injected unsavory HTML into the markup, then you've got an XSS attack on your hands. Imagine an added onclick interception, etc.. Any data from outside is dangerous.
markup language
This is one of the reasons, why markup languages like BBCode and markdown exist.
You don't want every piece of HTML markup, only clean and safe stuff.
Basically, you want to work with a restricted set of "content".
And one way of allowing data from outside is by using an "intermediate" markup language.
It is intermediate, because it is a custom format, which is later transformed into HTML.
This happens here on Stackoverflow, too:
[link](http://google.com) = link
tell your users: "to insert a link, using a special syntax"
save the content to the database.
the content you store to the database is something like:
The message text. And some markdown [link](http://google.com).
when you fetch the message from database, you process the markdown content:
$messageFromDb = 'The message. [http://google.com](google)';
$parsedown = new Parsedown();
$html = $parsedown->text($messageFromDb);
echo $html; // ready to show
Result: <p>The message. <a href="http://google.com">http://google.com</a></p>
There are libraries out there ready for usage, like
http://parsedown.org/
https://github.com/egil/php-markdown-extra-extended
filter html
Another way is to allow HTML, but only an restricted set. You would have to filter the inserted HTML, to pick only the good content and drop the rest.
PHP Extension Tidy: http://php.net/manual/en/book.tidy.php
Libraries like http://htmlpurifier.org/
DOM based HTML filter
Instead of relying on a filter library, you could also come up with a "little" DOM based HTML filter.
The following example re-creates a clean link from a crappy and bad one.
You should also check the URL attributes to ensure they use known-good schemes like http:, and not troublesome like javascript:.
This allows to whitelist the combination of elements, to control the nesting and the content.
<?php
// content from form
$html = 'Message <img title="The Link" /> Link Text';
$dom = new DOMDocument;
$dom->formatOutput = true;
$dom->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD | LIBXML_NOXMLDECL);
// filter, then rebuild a clean link
foreach ($dom->getElementsByTagName('a') as $node)
{
// extract the values
$title = $node->nodeValue;
$href = $node->getAttribute('href');
// maybe add a href filter?
// to remove links to bad websites
// and to remove href="javascript:"
// oh boy ... simple questions, resulting in lots of work ;)
// create a new "clean" link element
$link = $dom->createElement('a', $title);
$link->setAttribute('href', $href);
// replace old node
$node->parentNode->replaceChild($link, $node);
}
$html = $dom->saveXML();
// drop html, body, get only html fragment
// http://stackoverflow.com/q/11216726/1163786
$html = preg_replace('~<(?:!DOCTYPE|/?(?:html|body|p))[^>]*>\s*~i', '', $dom->saveHTML());
var_dump($html);
Before
Message <img src="injectionHell.png" title="The Link" /> Link Text
After
Message Link Text
To store "HTML in database"
When storing: use addslashes().
When returning text from DB: apply stripslashes(), before rending
A simple way to attain your goal is to save the message including the <a> tags.
You can use an HTML sanitizer so that you accept <a> link tags from your users while removing any potentially dangerous tags.
Then you wouldn't escape the saved text when you output it.
Have a look at HTML purifier.
Alternatively, you could use a Markdown parser to convert plain text to HTML.
your code removes the html tags and replace it with a written form ...
escapeHtml()
what you need is a function that remove all your html tags except what you desire in this case (link tag)
<a>
here is the function you can add it to your code :
function stripme($msg){
$msg = strip_tags($msg,'<a>');
return $msg ;
}
and then call it for your message like this:
$message = nl2br($this->stripme($this->theMessage[0]['message']));

htmlpurifier removes all the formatting done by CKEDitor

I am using CKEditor to let the user post their comments. I thought to use the htmlpurifier to secure my html. But when I tried it, it actually removes all the formatting done by CKEditor.
The CKEditor generated the following html
<div class="originalpost"><span style="color:#B22222;">
<em><u><strong><span style="font-size:250%;">
This is Pakistan</span></strong></u></em></span></div>
After purifying with htmlpurifier the html became like this
<div class=""originalpost""><span><em><u><strong>
<span>This is Pakistan</span></strong></u></em></span></div>
It actually removes all the inline css styles and also class=""originalpost"" is not understand able.
I have used the following way to purify the html with htmlpurifier
require_once("path\HTMLPurifier.auto.php");
$config = HTMLPurifier_Config::createDefault();
$purifier = new HTMLPurifier($config);
$html = "xyzhtml";
$clean_html = $purifier->purify($html);
I want to keep the user formatting, How can I configure htmlpurifier to keep the user formatting also don't change the inline css.
It actually removes all the inline css styles
Inline styles are indeed dangerous - JavaScript can be injected into them using url(), IE's dodgy expression() and browser-specific behavioural extensions.
HTMLPurifier can parse inline styles and filter the dangerous properties and values. You can turn this on by including style in your whitelisted attributes.
$config->set('HTML.AllowedAttributes', '*.style, ...');
style is not included in the default attribute list because parsing styles is a lot of extra complexity (with accompanying chance of bugs) and most applications don't need it.
You can configure the properties that are permitted using %CSS.AllowedProperties if you wish.
I can't reproduce the " problem but certainly ensuring PHP's magic_quotes_gpc option is turned off is an all-round good thing...
I bet that you need to turn off Sybase quotes.

Categories