Comments (16)
We can do this without problems, simply using the SAFE_FOR_JQUERY
flag is sufficient to produce safe output. So, we could either overwrite elm.html()
and friends or provide an alternative API, prefixed with safe*
.
I'd however like it more if we overwrote stuff and offer the old methods prefixed with unsafe*
(doing it CSP-style). Any thoughts?
So, a possible work-flow for the plugin could be:
- Get included
- Overwrite all jQuery DOMXSS sinks
- Offer the old methods with prefix
unsafe*
- Done
from dompurify.
+1, overriding is fine.
from dompurify.
There's tons of documentation of how we can do it (meaning to override jQuery core functionality). Ben Nadel has published this some years ago. Is that still the best way to do it?
http://www.bennadel.com/blog/1624-ask-ben-overriding-core-jquery-methods.htm
Should we maintain the plugin here or in a separate project? I'd personally opt for a separate project - but might be wrong. @fhemberger what thinks you?
from dompurify.
Naive approach could look like this.
And we'd have to do it for all DOMXSS sinks:
(function(){
// Store a reference to the original remove method.
var unsafehtml = jQuery.fn.html;
// Define overriding method.
jQuery.fn.html = function(){
// Log the fact that we are calling our override.
console.log( "Use DOMPurify for both get and set" );
// Expose the original method.
jQuery.fn.unsafehtml = unsafehtml;
}
})();
from dompurify.
About the interface we should expose with this / methods we technically should overwrite:
jQuery.fn.purify() // same as DOMPurify but using SAFE_FOR_JQUERY flag by default
// write access
jQuery.fn.add( xss1 ) // purify first argument, string or Node
jQuery.fn.append( xss1, xss2, xss3 ) // purify all arguments, each string or Node
jQuery.fn.after( xss1, xss2, xss3 ) // purify all arguments, each string or Node
jQuery.fn.before( xss1, xss2, xss3 ) // purify all arguments, each string or Node
jQuery.fn.html( xss1 ) // purify first argument, string, function or Node
jQuery.fn.prepend( xss1, xss2, xss3 ) // purify all arguments, each string or Node
jQuery.fn.replaceWith( xss1 ) // purify first argument, string or Node
jQuery.fn.wrap( xss1 ) // purify first argument, string or Node
jQuery.fn.wrapAll( xss1 ) // purify first argument, string or Node
Now, I am wondering. Should we also purify any HTML string that is being returned? For instance when using $(elm).html()
? Should we also sanitize nodes that are being returned? Or would that be too invasive?
What worries me a bit is the flexibility of jQuery's APIs. Looking at jQuery.fn.html
for example, this method accepts (at least) three types of arguments that might leads to XSS:
$('body').html(function(){return '<svg onload=alert(1)>'});
$('body').html('<svg onload=alert(2)>');
var img = document.createElement('img');
img.src=123;
img.onerror=function(){alert(3)}
$('body').html(svg)
Then again, jQuery.fn.add
allows to use strings, selectors, selections, nodes and node collections and all of them can lead to XSS. This is horrible to cover and once the API changes slightly we have to change our code too. Which sucks even more.
So, ideally, we'd inject our fine selves at a far more central point to have good coverage. Any thoughts on this?
from dompurify.
I don't think that you have to overwrite too many functions, e.g. jQuery.fn.wrap() =uses=> jQuery.fn.wrapAll() =uses=> jQuery.fn.append()
. I guess the interesting parts will be in manipulation.js.
from dompurify.
True, many calls end up arriving in jQuery.fn.domManip
.
(function(){
var originalMethod = jQuery.fn.domManip;
jQuery.fn.domManip = function(){
console.dir(arguments);
originalMethod.apply( this, arguments );
}
})();
$('body').append('<svg onload=alert(1)>')
Sadly, not all of them. What worries me most is elm.html(fn)
.
from dompurify.
So, I've built a very first, very naive prototype for a quick sanity check, feedback appreciated:
jpurify.js
(function(){
/**
* Define a safe elm.html() method for jQuery
*
* + Replaces elm.html()
* + Exposes original method as elm.unsafeHtml()
*/
jQuery.fn.unsafeHtml = jQuery.fn.html;
jQuery.fn.html = function(){
if(arguments && arguments[0]){
if(typeof arguments[0] === 'string' || arguments[0].nodeName){
arguments[0] = DOMPurify.sanitize(arguments[0]);
}
}
jQuery.fn.unsafeHtml.apply(this, arguments);
};
/**
* Define a safe elm.domManip() method for jQuery
*
* + Replaces elm.domManip()
* + Protects elm.append()
* elm.prepend()
* elm.before()
* elm.after()
* + Exposes original method as elm.unsafeDomManip()
*/
jQuery.fn.unsafeDomManip = jQuery.fn.domManip;
jQuery.fn.domManip = function(){
if(arguments && arguments[0]){
for(var i in arguments[0]){
arguments[0][i] = DOMPurify.sanitize(arguments[0][i]);
}
}
jQuery.fn.unsafeDomManip.apply(this, arguments);
};
/**
* Define a safe el.wrapAll method for jQuery
*
* + Replaces elm.wrapAll()
* + Protects elm.wrapAll()
* elm.wrap()
* elm.wrapInner()
* + Exposes original method as elm.unsafeWrapAll()
*/
jQuery.fn.unsafeWrapAll = jQuery.fn.wrapAll;
jQuery.fn.wrapAll = function(){
if(arguments && arguments[0]){
if(typeof arguments[0] === 'string' || arguments[0].nodeName){
arguments[0] = DOMPurify.sanitize(arguments[0]);
}
}
jQuery.fn.unsafeWrapAll.apply(this, arguments);
};
})();
test.html
<html>
<head>
<script src="jquery.js"></script>
<script src="purify.js"></script>
<script src="jpurify.js"></script>
</head>
<body>
<div id="html"></div>
<div id="append"></div>
<div id="wrap"></div>
<script>
$('#html').html('<h1>XSS<svg/onload=alert(1.0)>');
// no protection yet $('<svg/onload=alert(1.1)>')
// no protection yet $('#html').html(function(){return '<h1>XSS<svg/onload=alert(1.2)>'});
$('#append').append('<svg/onload=alert(2.1)>', '<svg/onload=alert(2.2)>');
$('#append').prepend('<svg/onload=alert(2.3)>', '<svg/onload=alert(2.4)>');
$('#append').before('<svg/onload=alert(2.4)>', '<svg/onload=alert(2.5)>');
$('#append').after('<svg/onload=alert(2.6)>', '<svg/onload=alert(2.7)>');
$('#append').wrap('<svg/onload=alert(4.1)>');
$('#append').wrapInner('<svg/onload=alert(4.2)>');
$('#append').wrapAll('<svg/onload=alert(4.3)></svg>');
</script>
</body>
</html>
from dompurify.
This is fantastic! Pretty excited about this.
One concern with the console.log for unsafe use was that the safe function uses it too. Just something to keep in mind since we don't want to spam the console :( But, warning on console on unsafe use would be really useful too. Not sure what the right thing to do is. On the other hand, a method called unsafe is obviously a red flag and easily caught with a grep.
from dompurify.
Also, another quick comment: a few developers I talked to said they were bigger fans of creating a new function called safe* instead of monkey patching current use to use the safe version directly and creating new unsafe*.
This is a style issue though. I also think your approach is better; but just wanted to put it in your radar and see what you thought.
(sorry, I had looked into this a while back and paging all of it back is taking a bit of time :)
from dompurify.
@devd Thanks for the comments :)
We might add a flag to solve the safe/unsafe question. I'd love to follow the strictness of CSP and dictate safety and unsafe to opt out. Rather than the other way round - security imho needs to be opt-out. But with a flag we can mitigate any conflicts I guess ;)
from dompurify.
Couple more questions:
many of the functions take DOM elements, array of DOM elements, (array of) jQuery objects and strings. I think you are correctly handling the string and DOM element case, but not the array cases or the jQuery objects case.
One additional concern: it is safe to manually create a tree of elements manually and then use append or prepend to insert it into the page. We probably shouldn't sanitize a safely created tree (the inline event handlers might intentionally be there). A trick I have seen done in similar functions is to use the fact that a tree was created manually as a proxy for it being safe. So, the code would essentially do "if string, will sanitize; if tree, don't sanitize". This is another setting that could be behind a configuration flag.
from dompurify.
The code above is just a sanity check, nothing even close to alpha. And yes, later version will of course contain more checks and type handlers. Although we cannot really handle callbacks imho.
As for the tree. I think we should have as few assumptions as possible and sanitize what we can. What if the tree comes from a CORS XHR with response type document? Then your assumption of it being safe would crumble and we had an XSS :)
from dompurify.
thanks! I wasn't sure if you already knew about the array of elements and jquery objects case. I know it definitely surprised me the first time :)
re the tree: definitely agree that default should be safe! I was wondering though if it could be a useful flag to have.
from dompurify.
@devd Yeah, I read the API docs a while ago and was amazed by the amount of types you can throw into these methods. The callback sucks most as we cannot analyze and secure it. Unless we come up with ba smart wrapper function that passes through all arguments and then sanitizes the return value.
from dompurify.
@devd @kozmic @fhemberger I created a private project alongside a working prototype and some first, simple tests: https://github.com/cure53/jPurify
Please let me know if you are interested in having a look. I think we should move the discussion there?
from dompurify.
Related Issues (20)
- Uncertain how to handle 'non-standard' HTML HOT 3
- Need to block external calls, e.g. all HTTP requests HOT 7
- Why does name="name" on an input field get purified? HOT 1
- Exception when passing 0 or "" or null to Dompurify.Sanitize Method #947 HOT 3
- Latest versions of DOMPurify 2.5.x block custom SVG elements when they are set via ADD_TAGS config. HOT 6
- release 3.1.3 assets are the same as 3.1.2 HOT 1
- Number.isNaN is not supported in MSIE HOT 15
- Bower issues : DOMPurify is not defined HOT 5
- HTML and BODY tags are being regardless of `ALLOWED_TAGS` settings HOT 2
- MAX_NESTING_DEPTH remove contents issue HOT 5
- Escape unsafe characters instead of removing them HOT 3
- The MAX_NESTING_DEPTH remove contents issue has not been resolved. HOT 3
- A code comment containing a tag name structure leads to removal of the entire block HOT 2
- Issue secure [email protected] Apache-2.0 + Fair + MPL-2.0 HOT 1
- KEEP_CONTENT remove contents of all ALLOWED_TAGS HOT 2
- <img> xss vulnerability
- MathML Content Markup Removed HOT 2
- Policy creator HOT 2
- name='lang' Attribute Removed During Sanitization HOT 1
- DOM Purify Allows onfocus events HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from dompurify.