Intro
This topic has been the bane of many questions and answers on StackOverflow -and in many other tech-forums; however, most of them are specific to exact conditions and even worse: "over-all" security in script-injection prevention via dev-tools-console
, or dev-tools-elements
or even address-bar
is said to be "impossible" to protect. This question is to address these issues and serve as current and historical reference as technology improves -or new/better methods are discovered to address browser security issues -specifically related to script-injection
attacks.
Concerns
There are many ways to either extract -or manipulate information "on the fly"; specifically, it's very easy to intercept information gathered from input -to be transmitted to the server - regardless of SSL/TLS.
intercept exampleHave a look here
Regardless of how "crude" it is, one can easily use the principle to fabricate a template to just copy+paste into an eval()
in the browser console to do all kinds of nasty things such as:
console.log()
intercepted information in transit via XHR- manipulate
POST
-data, changing user-references such asUUIDs
- feed the target-server alternative
GET
(& post) request information to either relay (or gain) info by inspecting the JS-code,cookies
andheaders
This kind of attack "seems" trivial to the untrained eye, but when highly dynamic interfaces are in concern, then this quickly becomes a nightmare -waiting to be exploited.
We all know "you can't trust the front-end" and the server should be responsible for security; however - what about the privacy/security of our beloved visitors? Many people create "some quick app" in JavaScript and either do not know (or care) about the back-end security.
Securing the front-end as well as the back-end would prove formidable against an average attacker, and also lighten the server-load (in many cases).
Efforts
Both Google and Facebook have implemented some ways of mitigating these issues, and they work; so it is NOT "impossible", however, they are very specific to their respective platforms and to implement requires the use of entire frameworks plus a lot of work -only to cover the basics.
Regardless of how "ugly" some of these protection mechanisms may appear; the goal is to help (mitigate/prevent) security issues to some degree, making it difficult for an attacker. As everybody knows by now: "you cannot keep a hacker out, you can only discourage their efforts".
Tools & Requirements
The goal is to have a simple set of tools (functions):
- these MUST be in plain (vanilla) javascript
- together they should NOT exceed a few lines of code (at most 200)
- they have to be
immutable
, preventing "re-capture" by an attacker - these MUST NOT clash with any (popular) JS frameworks, such as React, Angular, etc
- does NOT have to be "pretty", but readable at least, "one-liners" welcome
- cross-browser compatible, at least to a good percentile
Runtime Reflection / Introspection
This is a way to address some of these concerns, and I don't claim it's "the best" way (at all), it's an attempt. If one could intercept some "exploitable" functions and methods and see if "the call" (per call) was made from the server that spawned it, or not, then this could prove useful as then we can see if the call came "from thin air" (dev-tools).
If this approach is to be taken, then first we need a function that grabs the
call-stack
and discard that which is not FUBU (for us by us). If the result of this function is empty, hazaa! - we did not make the call and we can proceed accordingly.a word or two
In order to make this as short & simple as possible, the following code examples follow DRYKIS principles, which are:
With that said, pardon my "short-hand", explanation will follow
first we need some constants and our stack-getter
stak()
- short explanationx
is a number then e.g.stack(0)
returns the 1st item in the log, orundefined
a
is either astring
-or anarray
then e.g.stack(undefined, "anonymous")
allows "anonymous" even though it was "omitted" ino
function file line
/
(exactly) so if you test this code, putting in a separate.js
file will yield better results than inindex.html
(typically) -or whichever web-root mechanism is used_fake_
for now, it's in thejack
function belownow we need some tools
bore()
- get/set/rip some value of an object by string referencebake()
- shorthand to harden existing object properties (or define new ones)bake & bore - rundown
These are failry self-explanatory, so, some quick examples should suffice
bore
to get a property:console.log(bore(window,"XMLHttpRequest.prototype.open"))
bore
to set a property:bore(window,"XMLHttpRequest.prototype.open",function(){return "foo"})
bore
to rip (destroy carelessly):bore(window,"XMLHttpRequest.prototype.open",null)
bake
to harden an existing property:bake(XMLHttpRequest.prototype,'open')
bake
to define a new (hard) property:bake(XMLHttpRequest.prototype,'bark',function(){return "woof!"})
intercepting functions and constructions
Now we can use all the above to our advantage as we devise a simple yet effective interceptor, by no means "perfect", but it should suffice; explanation follows:
jack()
- explanationbore
), the second is used as interceptor (function)jack
deposes an existing function, stows it away, then use "interceptor-functions" to replay argumentsundefined
or a value, if no value is returned from any, the original function is not calledfail(":(")
is intentional; an error will be thrown if you don't have that function - only if thejack()
failed.Examples
Let's prevent
eval
from being used in the console -or address-barextensibility
If you want a DRY-er way to interface with
jack
, the following is tested and works well:Now you can intercept in bulk, like this:
A clever attacker may then use the Elements (dev-tool) to modify an attribute of some element, giving it some
onclick
event, then our interceptor won't catch that; however, we can use a mutation-observer and with that spy on "attribute changes". Upon attribute-change (or new-node) we can check if changes were made FUBU (or not) with ourstak()
check:Conclusion
These were but a few ways of dealing with a bad problem; though I hope someone finds this useful, and please feel free to edit this answer, or post more (or alternative/better) ways of improving front-end security.