1

After consulting MDN for the referrer-policy and Googling, DuckDucking and StackOverlow-searching, maybe you can help me with this rather simple (yet illusive) issue?

Process Flow

  1. the browser makes a request to the server
  2. based on the HTTP_REFERER header, the server decides a response

but why? (you ask)

This is part of an elaborate set of security checks, in this case deciding if the client has access to a file requested FUBU (for us by us).

This will not work if the referer is missing, but when JavaScript issues a request for a specified worker - the referer (request-header) is indeed missing.

what I've tried - and FAILED

  • setting Referrer-Policy: same-origin for EVERY request
  • setting the appropriate CORS headers Access-Control-Allow-Headers: x-requested-with - in response to EVERY request.

question

How can I find out if a request was made for a JS worker file, or just FORCE the HTTP mechanism to behave like it should?

mika
  • 49
  • 8
  • 3
    You just can't. You cannot trust the client, it might also offer a fake referrer. – Jonas Wilms Oct 19 '19 at 00:21
  • 3
    You shouldn't rely on `HTTP_REFERER`, as ["In short, it cannot really be trusted"](https://www.php.net/manual/en/reserved.variables.server.php) – Nick Oct 19 '19 at 00:21
  • Maybe you are looking for [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) ? – Jonas Wilms Oct 19 '19 at 00:23
  • 1
    First of all, good question! second, how about appending something like `worker.js?from=worker` – argon Oct 19 '19 at 00:23
  • @Nick - I'm sure she knows that, hence why she mentioned it's "part of" some checks I guess. – argon Oct 19 '19 at 00:25
  • 1
    I just had me the craziest idea - how about hijacking the the `Worker` constructor by creating a wrapper for every subsequent instance and append some security key to the end - provided from the server? ... if only I knew HOW to create a Worker wrapper .. I'll need to research this – argon Oct 19 '19 at 00:28
  • Thanks for the info boys. This is not a security question though, @argon seems like a crazy scientist (I like that) – mika Oct 19 '19 at 00:47
  • Using bash anyone can craft and send a custom HTTP request (with whatever headers/values) and server won't see anything strange. To solve this you have to implement an association between the user's login password and the domain/resource they can have access to. So when a request arrives on server just cross-check it. – Vinay Oct 19 '19 at 04:49
  • Indeed @Viney, this is not a security question; the security step mentioned helps in identifying an `interface` - to which the server responds accordingly ... keyword here is **helps** ... *NOT exclusively depending on* – mika Oct 19 '19 at 13:35
  • how odd, a person drops 2 words: `header` and `security` -then everybody jumps up and down about "security issues" ... remove head from uranus, then read again (and repeat if necessary). – mika Oct 19 '19 at 13:39

1 Answers1

1

Thinking out of "the box"

Since there "seems" to be no way to do this in a "good" way, one could always apply a lil creativity to achieve a specific outcome.

Just to recap:

  • the requirement is to have a way to identify from where a request is made
  • this can either be achived manually per Worker invocation, or automatically
  • any security issues seem to be handled elsewhere
  • altering the Worker URL upon invocation could be helpful for automatic handling

Work-able solution

Here is a wrapper that can be used to "hijack" some class invocations or methods:

const hijack = function(driver,victim,jacker)
{
    if(((typeof driver)=='string')&&!victim){return this.plan[driver]}; // recap
    if(victim in this.plan){return}; // only jack once? .. less cruel
    this.plan[victim]={victim:driver[victim],jacker:jacker}; // plan the heist

    let con = {enumerable:false,configurable:false,writable:false,value:function()
    {
        let car=hijack((this.mask||this.name||this.constructor.name)); let m=this.mask;
        let arg=car.jacker.apply(null,arguments); if(!Array.isArray(arg)){arg=[arg]};
        if(!m){return new (Function.prototype.bind.apply(car.victim,[null].concat(arg)))()}
        else{return car.victim.apply(this,arg)};
    }};

    try{con.value.prototype = Object.create(driver[victim].prototype)} // blend in
    catch(oops){Object.defineProperty(driver,'mask',{value:victim});}; // recover
    Object.defineProperty(driver,victim,con);
}.bind({plan:{}});

... nail meets hammer

How it works

  • It takes in 3 arguments:
    1. driver ~ the object that contains the target function/method
    2. victim ~ the name of the function/method that will be intercepted
    3. jacker ~ a callback-function -which is used to relay/change arguments
  • The original method is copied to where it can be used or subsequent calls
  • The callback imposes (deposes) the original and can either relay arguments unchanged between the caller and the callee (exactly like the original), but now you can control how it happens (if at all) and what to relay exactly; either with some simple condition(s) or some elaborate scheme (a.k.a "evil plan")
  • For the sake of simplicity this code (above) only permits 1 interception per victim, but this can be extended for multiple intercepts; either by "chain-relay" (callback array) or "event-dispatcher + event-listener combo(s)".

How to use

Specific to the question:

hijack(window,'Worker',function(arg){return `${arg}?worker=true`});

To address the security concerns in the comments, an api-key could be useful; so if some string was passed to the running instance (browser or server) that is unique to the current session (or client), it could suffice, for example:

hijack(window,'Worker',function(arg){return `${arg}?worker=${window.ApiKey}`});

.. where ApiKey was defined globally as a string, but it can also be the result of a function-call -which gets it from a cookie, or whichever.

Useful tool

This can also be used to enhance security. If you are concerned about XHR requests made from devtools or even worse: eval() -then you can use this hijack to intercept those calls/invocations globally.

For example:

hijack(URL,'createObjectURL',function(arg){console.log(arg); return `whatever`});

If you plan to use this as security tool, then it needs some TLC with a dash of "call-stack back-trace", a "mutation-observer" .. and a pinch of (dark) matter (:

disclaimer
nobody got hurt during this exercise .. the victim turned out okay .. use at your own discretion

argon
  • 449
  • 4
  • 11
  • It works! omg you've saved me. My boss thinks it's "a hack", but also added that it could be useful as it's application could solve a few issues with front-end hacking in general ..... Using a hack to prevent hacks, you learn something new every day. Now, to change the wording hehehe :D Thank you dear stranger. – mika Oct 19 '19 at 14:56
  • @mika - I've posted a "front-end" security Q&A [here](https://stackoverflow.com/questions/58598510/javascript-how-to-prevent-script-injection-attackts/58598511#58598511) - related to this answer. Hope it helps – argon Oct 28 '19 at 21:56
  • Thanks! I've read your article and I'll put in the effort as you have to share what I've learned. – mika Oct 28 '19 at 22:08