1

I am working on a chrome extension to modify U2F create request parameters from a web service. Is it possible to replace navigator.credentials.create request or response parameters through a Chrome extension? I couldn't find resources related to this. Any pointers would be helpful.

In particular, when a user registers a U2F for a website, it calls browsers web API navigator.credentials.create which in turn contacts the hardware token and returns the response. I want to modify the request and response from the navigator.credentials.create API, called by the webpage.

Marco Bonelli
  • 63,369
  • 21
  • 118
  • 128
tarun14110
  • 940
  • 5
  • 26
  • 57
  • Are you asking if it's possible to edit the content of a web request sent *by the user* (for example through a web page)? Or maybe you want to modify the behavior of some website? You should add more details to the question, it's not that clear. – Marco Bonelli Feb 10 '20 at 12:21
  • @MarcoBonelli I'm sorry about that. When a user registers a U2F for a website, it calls browsers web API `navigator.credentials.create` which in turn contacts the hardware token and returns the response. I want to modify the request and response from the `navigator.credentials.create` API, called by the webpage. Let me know if it doesn't make any sense. I'll add more details. – tarun14110 Feb 11 '20 at 17:44
  • I doubt you'll get a meaningful response to this. The whole point of U2F/WebAuthN is to make a cryptographically secure challenge/response scheme for strong authentication over the web. Inserting a proxy and changing the request and response will break that cryptographic challenge/response in any context where you're not a MITM, leaving your users dependent upon your extension to authenticate. It might help to explain what you're trying to do with this data. – gcochard Feb 12 '20 at 04:59
  • More importantly, please show what you've already tried. – gcochard Feb 12 '20 at 05:26
  • @tarun14110 I've edited your questiona adding the details from your previous comment. Check out my answer. – Marco Bonelli Feb 12 '20 at 14:06
  • @MarcoBonelli I have asked a follow-up question on this. Can you please help me with that when you get the time? https://stackoverflow.com/questions/61948769/replace-chromes-webapi-function-using-inject-script-through-chrome-extension?noredirect=1#comment109591990_61948769 – tarun14110 May 24 '20 at 00:40
  • 1
    @tarun14110 I answered this months ago. I am currently busy, sorry. – Marco Bonelli May 24 '20 at 01:02

1 Answers1

1

There is no way of achieving what you want through Chrome APIs. Chrome does not offer any kind of API to modify requests or other kind of data "on the fly" like you'd want to do. I can only assume that this is most likely a security measure.

However, since you say that you want to modify the data that is passed and returned by navigaror.credentials.create() on some particular website, you can easily do this with a content script that replaces the function with a custom handler and acts as a proxy, intercepting all calls and potentially modifying data.

Whether this makes any sense or not depends on what specifically you want to do when intercepting these calls. It's important to mention that as @gcochard makes us notice in a comment:

The whole point of U2F/WebAuthN is to make a cryptographically secure challenge/response scheme for strong authentication over the web. Inserting a proxy and changing the request and response will break that cryptographic challenge/response in any context where you're not a MITM, leaving your users dependent upon your extension to authenticate. It might help to explain what you're trying to do with this data.

Either way, you could still log and access the data, so there could be meaningful applications of this strategy.

I will only outline what the content script should do, if you also don't know how to inject a content script in a page, refer to this documentation page. In any case, you want your script to run as soon as possible, so make sure to have "run_at": "document_start".

The content script will do the following:

  1. Create a <script> tag inside the page, loading some code inside it, which will:
  2. Save the create() method of navigator.credentials it in another variable.
  3. Replace the original create() method with a function that "filters" the calls made to it and internally calls the real method.

So here's a simple content script that achieves your goal:

const code = `
    const real_create = navigator.credentials.create.bind(navigator.credentials);

    navigator.credentials.create = function() {
        // Modify the arguments how you want.
        console.log(arguments);

        // Call the real method with the modified arguments.
        let res = real_create.apply(arguments);

        // Modify the return value how you want, then return it.
        console.log(res);
        return res;
    }
`;

const script = document.createElement('script');
script.textContent = code;
(document.documentHead || document.documentElement).appendChild(script);
script.remove();

The above should do exactly what you want, it's only a matter of injecting it into the right page.

NOTE: the code variable is created using a template string literal, delimited by the characters `, if you don't want to use template literals you can use an array of strings and then join it. You can also refer to this answer which lists other ways to inject code into a page from a content script.

Marco Bonelli
  • 63,369
  • 21
  • 118
  • 128
  • Thanks! that is really helpful. But, I am getting an error `Uncaught (in promise) NotSupportedError: Only exactly one of 'password', 'federated', and 'publicKey' credential types are currently supported.` in the line `real_create.apply(arguments);`. I haven't modified the arguments yet. Just trying to get it to work. – tarun14110 Feb 12 '20 at 20:43
  • @tarun14110 first find a call that works with the normal `navigator.credentials.create()`, and *only then* test the wrapper. If you provide invalid arguments to the wrapper that's the same as providing invalid arguments to the real function. Read the doc: https://developer.mozilla.org/en-US/docs/Web/API/CredentialsContainer/create – Marco Bonelli Feb 12 '20 at 20:55
  • So, navigator.credentials.create() is working fine without this wrapper. While using the wrapper on Facebook.com, I am not changing the request parameters. I just pass the parameter object as it is to `navigator.credentials.create()` like you have shown in your answer. I am still getting the same error. Am I missing something? – tarun14110 Feb 12 '20 at 23:57
  • @tarun14110 hmm, difficult to say without looking at the code or some `console.log` of the arguments. Do a `console.log` on the arguments of the real call before it happens (use the Chrome dev tools debugger) and then do the same for the call inside your wrapper. See if they match (if it fails in your wrapper probably they don't match). – Marco Bonelli Feb 13 '20 at 01:41