This article describes how dangerous some userjs tricks are and how to cope with this danger.
Other articles in the series:
LiveConnect
The danger of this technology is obvious. As soon as you allow access from some address to advanced features (for example, work with the clipboard), all scripts from this address can use advanced features for their own purposes (for example, send the contents of the clipboard to the site - suddenly there is a password).
The solution is to allow access to only one address - “http://0.0.0.0/”; open a frame with this address and perform all actions via XDM. The disadvantage is that all calls become asynchronous. But what can't you do for the sake of safety. Yes, and there is some plus in this - the scripts on the page will not hang waiting for some long synchronous call.
')
Now you need to secure XDM.
Xdm
Any script on the page can find the iframe and send it a message. It is enough to know the format of messages. Finding is not a problem if userjs is online. Yes, even if userjs is written by you and for yourself, it is often easy to guess the format.
So, the task is to make the format unstable. For this you need:
- for each user to generate a password;
- add to the beginning of each message;
- use the “BeforeEvent.message” event to be the first to know about the message;
- process only messages with a suitable password;
- use the “Event.preventDefault” function to prevent the script from receiving messages on the page.
Canned functions
But this is still not enough to ensure safety.
Unfortunately, userjs are running in the context of the page. So all modifications of objects by scripts from the page are reflected in userjs.
Suppose password checking in userjs is done as follows:
if (ev.data.substring (0, password.length) == password) {...}
Then the script on the page can execute:
String.prototype.substring = function (start, len) {alert (this); }
and get a password!
It is necessary to save the functions used in critical places of userjs:
// At the very beginning of the script.
var functionCall = Function.prototype.call;
var stringSubstring = String.prototype.substring;
// In the event handler.
stringSubstring.call = functionCall;
if (stringSubstring.call (ev.data, 0, password.length) == password) {...}