Why doesn't the browser reuse the authorization headers after an authenticated XMLHttpRequest?

别等时光非礼了梦想. 提交于 2019-11-28 07:10:27

Basic idea

Load the images via JavaScript and display them on the site. The advantage is that the authentication credentials will never find their way into the HTML. They will resist at the JavaScript side.

Step 1: load the image data via JS

That's basic AJAX functionality (see also XMLHttpRequest::open(method, uri, async, user, pw)):

var xhr = new XMLHttpRequest();
xhr.open("GET", "your-server-path-to-image", true, "username", "password");

xhr.onload = function(evt) {
  if (this.status == 200) {
    // ...
  }
};

Step 2: format the data

Now, how can we display the image data? When using HTML, one would normally assign an URI to the src attribute of the image element. We can apply the same principle here except for the fact that we use data URIs instead of 'normal' http(s):// derivates.

xhr.onload = function(evt) {
  if (this.status == 200) {
    var b64 = utf8_to_b64(this.responseText);
    var dataUri = 'data:image/png;base64,' + b64; // Assuming a PNG image

    myImgElement.src = dataUri;
  }
};

// From MDN:
// https://developer.mozilla.org/en-US/docs/Web/API/window.btoa
function utf8_to_b64( str ) {
    return window.btoa(unescape(encodeURIComponent( str )));
}

Canvas

There is also another option which consists in painting the loaded data in a <canvas> field. This way, the user won't be able to right-click the image (the area where the canvas is positioned) as opposed to the <img> and data URIs where the user will see a long data URI when viewing the image properties panel.

flup

The google drive uploader is created using angular js. Its authors faced a similar problem. The icons were hosted on a different domain and putting them as img src= violated the CSP. So, like you, they had to fetch the icon images using XHR and then somehow manage to get them into the img tags.

They describe how they solved it. After fetching the image using XHR, they write it to the HTML5 local file system. They put its URL on the local file system in the img's src attribute using the ng-src directive.

$http.get(doc.icon, {responseType: 'blob'}).success(function(blob) {
  console.log('Fetched icon via XHR');
  blob.name = doc.iconFilename; // Add icon filename to blob.
  writeFile(blob); // Write is async, but that's ok.
  doc.icon = window.URL.createObjectURL(blob);
  ...
}

As for the why, I don't know. I assume that creating a session token for retrieving the images is out of the question? I'd expect that Cookie headers do get sent? Is it a cross-origin request? In that case, do you set the withCredentials property? Is it a P3P thing perhaps?

Another approach would be to add an end point to your sites back end that proxied the image request. So your page could request it without credentials and the back end would take care of the authentication. The back end could also cache the image if it didn't change frequently or you knew the frequency with which it was updated. This is fairly easy to do on the back end, makes your front end simple and prevents credentials being sent to the browser.

If the issue is authentication then the links could contain a single use token generated for the user that is authenticated and only accessible from their current browser session. Giving secure access to the content only for the user it was intended for and only for the time they are authorized to access it. This would also require work in the back end, however.

It seems to me that to solve your problem you should change the design of your app, instead of trying to hack your way around how browsers actually work.

A request to a secure URL will always need authentication, regarding of it being done by the browser with an img tag or in javascript.

If you can perform authorization automatically without user interaction, you can do it on the server side and you don't need to send any user+pass to the client to do this. If that is the case, you could change the code behind https://myserver/dev30281_WebServices/api/user/picture/2218 to perform the authorization and serve the image, without HTTP auth, only if the user is authorized to request it, otherwise return a 403 forbidden response (http://en.wikipedia.org/wiki/HTTP_403).

Another possible solution would be separate the pages that include the secure images from the rest of the app. So you would theoretically have two single-page-apps. The user would be required to login to access the secure part. I'm not sure though if this is possible in your case, since you didn't state all requirements. But it makes more sense that if you want to serve secure resources that require authentication, that the user should be prompted for credentials, just as the browser does.

I always parse

Set-Cookie header value in previous (or first login request) and then send it's value in next requests.

Something like this

Response after first request:

Date:Thu, 26 Dec 2013 16:20:53 GMT
Expires:-1
Pragma:no-cache
Set-Cookie:ASP.NET_SessionId=lb1nbxeyfhl5suii2hfchxpx; domain=.example.com; path=/; secure; HttpOnly
Vary:Accept-Encoding
X-Cdn:Served-By-Akamai
X-Powered-By:ASP.NET

Any next request:

Accept:text/html,application/xhtml+xml
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8,ru;q=0.6
Cache-Control:no-cache
Connection:keep-alive
Cookie:ASP.NET_SessionId=lb1nbxeyfhl5suii2hfchxpx;

As you can see I send ASP.NET_SessionId="any value" value in Cookie header. If server uses php you should parse PHPSESSID="some value"

You need to try using the Access-Control-Allow-Credentials: true header. I once encountered an issue with IE which eventually boiled down to the use of this header. Also set $httpProvider.defaults.headers.get = { 'withCredentials' : 'true' } in the angular js code.

As for the reason: I tried Chrome and Firefox, and both remember basic authorization only if the credential is entered directly from Browser UI, i.e. the pop-up made by browser. It will not remember it if the credential came from JavaScript, although the HTTP request is the same. I guess this is by design, but I don't see it mentioned in standard.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!