I'm working on a web framework and am trying to build XSS prevention into it. I have set it up so it will escape incoming data for storage in the database, but sometimes you want to save html that the user generates. I am trying to make a custom tag that will prevent any javascript from executing, here is my first hack at it:
<html>
<head>
<script type="text/javascript" src="/js/jquery.min.js"></script>
</head>
<body>
<preventjs>
<div id="user-content-area">
<!-- evil user content -->
<p onclick="alert('evil stuff');">I'm not evil, promise.</p>
<p onmouseover="alert('evil stuff');">Neither am I.</p>
<!-- end user content -->
</div>
</preventjs>
<script type="text/javascript">
// <preventjs> tags are supposed to prevent any javascript events
// but this does not unbined DOM events
$("preventjs").find("*").unbind();
</script>
</body>
</html>
I tried using jQuery to unbind everything, but it doesn't unbind events in the DOM, which is exactly what I'm trying to do. Is it possible to unbind all events for a DOM element?
You're problem is that you are doing this on the wrong end of things -- you should be filtering all user input of potentially hostile content when you receive it.
The first rule of thumb when doing this is "always whitelist, never blacklist". Rather than allowing any and all attributes in your user-generated HTML, simply keep a list of allowed attributes and strip away all others when you receive the HTML (possibly on the client side -- definitely on the server side.)
Oh, and HTML is not a regular language. You'll want to use an HTML parser, not a regular expression for this task.
.unbind will only unbind events attached using jQuery. You can get rid of inline event handler code by setting them to null, e.g.:
$("preventjs *").removeAttr("onclick").removeAttr("onmouseover");
Demo.
EDIT: Here's an evil solution, you can remove all attributes starting with "on":
$("preventjs *").each(function() {
var attribs = this.attributes;
var that = this;
$.each(attribs, function(i, attrib) {
if(attrib.name.indexOf("on") === 0) {
$(that).removeAttr(attrib.name);
}
});
});
Demo.
The problem is that you've inline handlers. unbind cannot remove inline handlers.
<p onclick="alert('evil stuff'...
^^^^
To remove inline handlers, use removeAttr
$("preventjs").find("*").removeAttr('onclick');
$("preventjs").find("*").removeAttr('onmouseover');
You can unbind the events individually:
$('p').each(function(){ this.onclick = this.onmouseover = undefined; });
If you want to unbind other events like mouseout you have to add them to that list:
$('p').each(function(){ this.onclick =
this.onmouseover =
this.onmouseout = undefined; });
Of course you'll want to use a selector other than $('p'), I just didn't want to put your other one because preventjs is not an HTML tag
Related
Does Javascript provide an event listener similar to onload that will be invoked if an element is added to the DOM via innerHTML?
I know <body onload="alert('hi');">...</body>. But it does only work in the beginning when the whole site is loaded.. Is it somehow possible to add an element via innerHTML = ... that triggers an event listener instantly that it contains?
It should look like this and it has to be an inline-HTML listener like this:
elementxy.innerHTML = '<b onload="dosth()">....</b>'
This is a really bad hack, but you can create an invisible <img> tag with an onerror attribute and a src that results in an error:
const dosth = () => {
console.log('dosth');
};
div.innerHTML = '<b>b content<img style="display: none;" onerror="this.remove(); dosth()" src="badsrc"></b>';
<div id="div"></div>
This is one reason why inserting untrustworthy HTML is a bad idea - even with innerHTML, it allows for arbitrary code execution.
Regardless, it's best to avoid inline handlers entirely.
I'm using a third-party commenting plugin, and I would like to change the content of some of the buttons. This is straightforward for buttons with id's known ahead of time, but it also has buttons that don't appear until a 'Reply' button is clicked. To be clear, these elements are not present when the page is loaded. They are inserted into the DOM following some event. For those elements, I only know a prefix of the id.
My first thought was to use .on, and to delegate to the children of the reply container, but the load event does not bubble, so this doesn't work:
<script>
$("#container").on("load", 'a[id|="reply-button"]', function(event) { $(this).html("different text"); } );
</script>
<div id="container">
<a id="reply-button-42das56ds6d78a">some text</a>
</div>
What's the next best thing?
"I know they will appear when the 'Reply' button is clicked. When that happens, new elements are inserted into the DOM, and I know what the prefix of the id of those elements will be."
You could use something like the DOMSubtreeModified event to tell when elements are added, but that isn't supported by all browsers. (In fact it has been deprecated.)
Or you could attach a click handler to the 'Reply' button:
$(document).ready(function() {
// initialise plugin here, then:
$("some selector for the reply button(s)").click(function(e) {
// setTimeout(function() {
$('a[id|="reply-button"]').html("different text");
// }, 10);
});
});
jQuery ensures that multiple event handlers will run in the order they are bound, but of course this only applies to handlers added with jQuery. So if the third-party commenting plugin you are using also uses jQuery then just be sure it is initialised first and your own reply click handler should run afterwards and at that time it will be able to access the elements added by the plugin.
If the plugin doesn't use jQuery you can't be sure your click handler will run last so instead uncomment the setTimeout code I've shown above - it will wait a few milliseconds to give the plugin events time to run and then update the text.
Use the selector $('#^=id')
id being the prefix
e.g all ids starting test123
$('#^=test123')
this would work for things like
test1234
test12345
test123fgjfdgj
This might help: http://oscarotero.com/jquery/
And use jquery event listeners for the page load..
e.g. $(document).ready(function(){});
If they are loaded when a button is clicked then do..
$('#buttonid').click(function() {//handle click});
You're looking for DOM Mutation Events. This spec allows you to be notified when DOM nodes are inserted, changed, etc. Browser support is not really there, though... well, IE is behind (IE >= 9 has support). It's also a major performance hog. See this MDN document. For these reasons, I don't think a lot of folks here would suggest using them. Here's some code, though:
document.addEventListener("DOMNodeInserted", function(e) {
if ($(e.target).is('selector matching new elements')) {
//do what you want with e.target, which is the newly-inserted element
}
}, false);
There is a performance-boosting hack involving listening for CSS animation events instead: here. Only problem is that IE9 does not support CSS animations. I think we're all waiting for the day when we can use these events in a cross-browser and performant way, though.
I am used to using inline events in my websites for example
<head>
<script>
function func() {
alert("test");
}
</script>
</head>
<body>
<div id="mydiv" onclick="func()"></div>
</body>
I noticed that sites these days do not use inline events at all. I know you can set events pragmatically like:
document.getElementById('mydiv').onclick = function(){ func()}
Is this how it should be done? Do I have to put the above line at the end of every page?
How are the keypress,click, and blur events attached to the input fields on this site for example: https://twitter.com/signup
Thanks.
Yes, that is one valid way to add an event to an object, but it prevents more than one function from being bound at a time.
I would recommend looking into jQuery (or similar javascript library, like mootools), which is how the vast majority of sites bind their javascript events these days.
For example, in jQuery you generally bind clicks like this:
$("#mydiv").click(function(event) {
// click handling code
});
The cleanest way to add event listeners is to use the built in methods :
var el = document.getElementById('mydiv');
if (el.addEventListener) {
el.addEventListener('click', modifyText, false);
} else if (el.attachEvent) {
el.attachEvent('onclick', modifyText);
}
function modifyText() {
}
This promotes cleaner more readable and reusable code. (Note attachEvent is for pre IE9)
You can either place this code at the end of the <body> tag (or anywhere within the <body> tag but after the DOM element is added) or within a window onload function - which executes after the DOM has completed loading :
window.onload = function() {
// here
};
The prefered method is to use addEventListener, often in combination with a library (such as YUI or jQuery) that smooths over variations between browsers (e.g. old-IE not supporting addEventListener).
Do I have to put the above line at the end of every page?
Usually you would put JavaScript in a file of its own, and then src it into each page that needed it.
The main idea here is that the DOM element that you want to register the handler on should be loaded. so either you do the event binding after the element html, or you could do it in the window.onload event handler, which could be defined before the actual tag definition, like this:
window.onload = function(){
// your event binding
}
But my advice would be to include javascript at the end of the document as much as possible.
Is it possible to get anywhere a pure Javascript function for event handler with similar functionality as jQuery's live() ? I need to have the ability to attach events to objects not yet created but both jquery-livequery as well as jquery-events sources are not useful due to dependencies on jQuery core.
Event delegation is quite simple. Take this example:
Markup:
<div id="container">
<p>Test</p>
<p>Test</p>
<p>Test</p>
</div>
<button id="add">Add new paragraph</button>
Script:
document.getElementById("container").onclick = function(e) {
// e.target is the target of the event or "source element"
alert(e.target.innerHTML);
};
// dynamically adds new paragraph on button click
document.getElementById("add").onclick = function() {
var p = document.createElement("p");
p.innerHTML = "a new paragraph";
document.getElementById("container").appendChild(p);
};
Since the event handler is attached to the parent, it will work for any future elements inserted.
You can try it here.
Useful reference:
http://www.quirksmode.org/js/events_properties.html#target
http://www.sitepoint.com/javascript-event-delegation-is-easier-than-you-think/
Yes, it's called event delegation and has been around longer than jQuery and "live".
"live" works by listening for events on the body or document, then when when an event occurs looks at the event.target to see if it's selector matches one of those stored in a cache. It is quite inefficient, but works OK for some.
A more efficient approach is to add elements you want listeners on to an array, then listen for bubbling events on the lowest common ancestor of the elements you want to delegate events for. The body element is the fallback, but it's the least efficient. When the listener gets an event it's waiting for, check if the event.target is one of the elements in the array and if so, call the related function with the element as this.
You can also just store the element id as a property of an object so looking it up is faster if you have lots of elements, or you can register events based on class.
There are a few limitations and foibles (some events bubble in some browsers but not others, and some don't bubble at all), and it can be very inefficient, so use with care.
I know little of Jquery and your functions.
You are looking how works with events in javascript?
You can make this:
element.addEventListener('onclick',
function() {
//do something
});
or
element.onclick =
function() {
//do something
});
the element var is an reference of dom document.
check https://developer.mozilla.org/en/DOM for more details.
JQuery is pure JavaScript and OpenSource, so just have a look into the sources, then copy what you need and adapt it.
in Javascript: Is there a way of checking if a mouseover event for some element has fired?
If yes, how?
T
var mousedOver = [];
function addToMousedOverElements(obj) {
mousedOver[mousedOver.length] = obj;
}
You could create a callback to add the moused over element to a list.
mouseover me!
Or, something similar. This would then allow you to reference each element that has been moused over. You may also want to check to see if the element has been moused over yet before adding it to the list.
Lets suppose you want to track mouseover event on a bunch of elements. Since, mouseover event gets bubbled up in JS, attach a onmouseover handler to a node that is a parent node to these elements.
Consider the following html:
`<div id="parent">
<div id='div1'>Track mouseover on me</div>
<div id='div2'> Track mouse over on me too.</div>
</div>
So for such an HTML, you can attach the handler to the div called 'parent' like
document.getElementById('parent').onmouseover = function(e){
e = e|| window.event;
if(e.target.id=='div1')
//handle mouseover for first div;
};
`
and so on. Like this, you can have a generic function handler for a bunch of elements.
Dealing with raw Javascript can be troublesome, particularly cross-browser. You are best off using a library like jQuery to handle this:
jQuery onMouseOver
Although such libraries do tend to increase the size of your webpages you can still get good performance by using a CDN such as:
Google's AJAX CDN
Microsoft's AJAX CDN
for example for a div:
<div onmousemove="alert('doSomething')">waiting for mouse over...</div>
you can replace alert by any javascript function.
more informations here: Javascript - Mouse Events.
This does the job:
<head>
<script language="JavaScript">
function myFunction(){
alert('Mouse over!!!')
}
</script>
</head>
<body>
<div id="myDiv" onmouseover="myFunction()">
Mouse over here...
</div>
</body>