If you have a website, can you somehow find out if visitors are modifying your site with javascript userscripts?

- 398,270
- 210
- 566
- 880

- 542
- 2
- 8
- 20
-
What kind of modifications do you want to detect and in which browser? – John Dvorak Feb 07 '13 at 07:05
-
Short answer: Yes. ... Longer answer: Detecting mods in a way that the user cannot thwart is much more difficult. ... In theory, the user can thwart anything the site does (except for going dark). – Brock Adams Feb 07 '13 at 07:58
2 Answers
In short: EEEEEEK! Don't do it! Rather, decide what needs to be guarded, and guard that. Avoid polling (periodical checking) at all costs. Especially, avoid periodical heavy checks of anything.
Not every change is possible to track. Most changes are just extremely hard to track, since there are so many things that could change.
Changes to the DOM (new nodes, removed nodes, changed attributes) can be detected. The other answer suggests checking innerHTML
periodically, but it's better to use mutation observers (supported by Firefox, Chrome) or the older mutation events (DOMSubtreeModified
et al.) (support varies by event) instead.
Changes to standard methods cannot be reliably detected, except by comparing every single method and property manually (eeeek). This includes the need to reference tons of objects including, say, Array.prototype.splice
(and Array
and Array.prototype
as well, of course), and run a heavy script periodically. However, this is not what a userscript typically does.
The state of an input is a property, not an attribute. This means that the document HTML won't change. If the state is changed by a script, the change
event won't fire either. Again, the only solution is to poll every single input manually (eeek).
There is no reliable way to detect if an event handler has been attached. For starters, you would need to guard the onX
attributes (paragraph #2), detect any call to addEventListener
(ek) (without tripping the paragraph #2 check), detect any calls to the respective methods by your library (jQuery.bind
and several others).
One thing that plays in your favor, and possibly the only one: user scripts run on page load (never sooner), so you have plenty of time to prepare your defenses. not even that plays in your favor (thanks Brock Adams for noting and the link)
You can detect a standard method has been called by replacing it with your own (ek). There are many methods that you would need to instrument this way (eek), some by the browser, some by your framework. The fact that IE (and even firefox can be instructed to, thanks @Brock) won't let you touch the prototypes of the DOM classes adds another "e" or two to the "eek". The fact that some methods can only be obtained via a method call (return value, callback arguments) adds another "e" or two, for a total of "eeeek". The idea of crawling across the entirety of window
will be foiled by security exceptions and uncatchable security exceptions. That is, unless you don't use iFrames and you are not within an iFrame.
Even if you detect every method call, DOM can be changed by writing to innerHTML
. Firefox and Chrome support Mutation Observers, so you can use these.
Even if you detect every method call to a pre-existing method and listen to mutations, most properties are reflected by neither, so you need to watch all properties of every object as well. Pray someone does not add a non-enumerable property with a key you would never guess. Incidentally, this will catch DOM mutations as well. In ES6, it will be possible to observe an object's property set. I'm not sure if you can attach a setter to an existing object property in ES5 (while adhering to ES3 syntax). Polling every property is eeeek.
Of course, you should allow your own scripts to do some changes. The work flow would be to set a flag (not accessible from the global scope!) "I'm legit", do your job, and clear the flag - remember to flank all your callbacks as well. The method observers will then check the flag is set. The property watchdogs will have a harder time detecting if a change is valid, but they could be notified from the script of every legit change (manually; again make sure the userscripts cannot see that notification stream). Eeek.
There's an entirely different problem that I didn't realise at first: Userscripts run at page load, but they can create an iFrame as well. It's not entirely inconcievable (but still unlikely now) that a userscript would: 1) detect your script blocker, 2) nuke the page from the orbit (you can't prevent document.body.innerHTML =
, at least not without heavily tampering with document.body
), 3) insert a single iframe with the original URL (prevent double loads server-side?) and 4) have a plenty of time to act on that empty iframe before your protection is even loaded.
Also, see the duplicate found by Brock Adams, which shows several other checks that I didn't think of that should be done.

- 1
- 1

- 26,799
- 13
- 69
- 83
-
Re: `user scripts run on page load (never sooner), so you have plenty of time to prepare your defenses.` ... No, every scriptable browser supports [starting before any of the page loads.](http://developer.chrome.com/extensions/content_scripts.html#run_at) The userscript can override prototypes and hide all kinds of watchers and safeguards. In the case of Firefox and Opera, it's not too hard to selectively block or edit the page's javascript before it ever executes. It's not as easy in Chrome. Additionally, Firefox objects can be frozen, so the page can't override them. – Brock Adams Feb 07 '13 at 08:30
-
-
Re: `Rather, decide what needs to be guarded, and guard that.` +1 for that. Trust nothing from the client. Always verify, sanitize and cross-reference on the server end. In the end, if the data's good (and game rules have not been violated to the harm of others), then it doesn't matter what the client does or how he does it. (I suspect the OP is a scripter worried about countermeasures, however.) – Brock Adams Feb 07 '13 at 08:38
If you don't have script yourself that changes things you cold compare document.body.innerHTML and document.head.innerHTL with what it was.
When you do change DOM in your script you can update the values to compare it with. Use setInterval to compare periodically.

- 37,593
- 24
- 91
- 160
-
1
-
Like what changes would not be detected? The only thing I can think of is resizing the window. Anything changed with JS does something to the DOM's properties/values or adds/delete DOM elements. That will change the innerHTML – HMR Feb 07 '13 at 07:12
-
sorry, you're right. input text value, user defined properties do not show when changed and there are probably more. – HMR Feb 07 '13 at 07:35
-
1Not to mention that any comparison you try to make will have to use code that can be modified by a user script (i.e. the userscript could modify your control, or modify the communication to the server if you compare on the server, etc) – Jim Deville Feb 07 '13 at 07:38