web-dev-qa-db-ja.com

認証されたXMLHttpRequestの後、ブラウザが認証ヘッダーを再利用しないのはなぜですか?

私はAngularを使用してシングルページアプリを開発しています。バックエンドは、基本認証を必要とするRESTサービスを公開します。index.htmlまたはスクリプトのいずれかを取得するために認証は必要ありません。

私のビューの1つに<img>があり、srcがREST認証を必要とするAPIのURLです。<img>はブラウザによって処理され、それが行うGETリクエストの認証ヘッダーを設定する機会がありません。

私はこれを行うことでこれを修正しようとしました:

  1. ソースでimgsrcを空のままにします
  2. 「ドキュメント準備完了」で、認証を発生させるために、Authorizationヘッダーを使用してXMLHttpRequestをサービス(/api/login)に作成します。
  3. その呼び出しが完了したら、img src属性を設定します。それまでに、ブラウザーは後続のリクエストにAuthorizationヘッダーを含めることを知っていると思います...

...しかし、そうではありません。画像のリクエストはヘッダーなしで送信されます。資格情報を入力すると、ページ上の他のすべての画像が正しくなります。 (Angularのng-srcも試しましたが、同じ結果になりました)

2つの質問があります。

  1. ブラウザ(IE10)がXMLHttpRequestの成功後にすべてのリクエストにヘッダーを含めなかったのはなぜですか?
  2. この問題を回避するにはどうすればよいですか?

@ bergiはリクエストの詳細を尋ねました。ここにあります。

/ api/loginへのリクエスト

GET https://myserver/dev30281_WebServices/api/login HTTP/1.1
Accept: */*
Authorization: Basic <header here>
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.2; WOW64; Trident/6.0; .NET4.0E; .NET4.0C; .NET CLR 3.5.30729; .NET CLR 2.0.50727; .NET CLR 3.0.30729)
Connection: Keep-Alive

応答(/ api/login)

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Length: 4
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Fri, 20 Dec 2013 14:44:52 GMT

/ user/picture/2218へのリクエスト:

GET https://myserver/dev30281_WebServices/api/user/picture/2218 HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.2; WOW64; Trident/6.0; .NET4.0E; .NET4.0C; .NET CLR 3.5.30729; .NET CLR 2.0.50727; .NET CLR 3.0.30729)
Connection: Keep-Alive

そして、Webブラウザーは資格情報の入力を求めます。それらを入力すると、次の応答が返されます。

HTTP/1.1 200 OK
Cache-Control: public, max-age=60
Content-Length: 3119
Content-Type: image/png
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Fri, 20 Dec 2013 14:50:17 GMT
39
Sylvain

基本的な考え方

JavaScriptを介して画像を読み込み、サイトに表示します。利点は、認証資格情報がHTMLへの道を見つけられないことです。 JavaScript側では抵抗します。

ステップ1:JSを介して画像データをロードする

基本的なAJAX機能( 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) {
    // ...
  }
};
_

ステップ2:データをフォーマットする

さて、どのように画像データを表示できますか? HTMLを使用する場合、通常はURIを画像要素のsrc属性に割り当てます。ここでは、 'normal' http(s)://派生物の代わりに data URIs を使用するという事実を除いて、同じ原則を適用できます。

_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>_フィールドにペイントすることで構成される別のオプションもあります。この方法では、ユーザーは、画像を表示するときに長いデータURIが表示される_<img>_およびデータURIとは対照的に、画像(キャンバスが配置されている領域)を右クリックできません。プロパティパネル。

24
ComFreek

Googleドライブアップローダーはangular js。を使用して作成されます。作成者は同様の問題に直面しました。アイコンは別のドメインでホストされ、_img src=_としてCSPに違反しています。あなたは、XHRを使用してアイコン画像を取得し、なんとかしてそれらをimgタグに入れる必要がありました。

解決方法を説明してください 。 XHRを使用して画像を取得した後、HTML5ローカルファイルシステムに書き込みます。 _ng-src_ ディレクティブを使用して、URLをimgsrc属性のローカルファイルシステムに配置します。

_$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);
  ...
}
_

理由については、わかりません。画像を取得するためのセッショントークンの作成は問題外だと思いますか? Cookieヘッダーが送信されると思いますか?クロスオリジンのリクエストですか?その場合、 withCredentials プロパティを設定しますか?それはおそらくP3Pのものですか?

8
flup

もう1つのアプローチは、イメージリクエストをプロキシしたサイトバックエンドにエンドポイントを追加することです。したがって、ページは資格情報なしでそれを要求でき、バックエンドが認証を処理します。バックエンドは、頻繁に変更されなかった場合、または更新の頻度がわかっていた場合、イメージをキャッシュすることもできます。これはバックエンドで行うのが非常に簡単で、フロントエンドをシンプルにし、資格情報がブラウザに送信されるのを防ぎます。

問題が認証の場合、リンクには、認証され、現在のブラウザーセッションからのみアクセス可能なユーザー用に生成された単一使用トークンが含まれる場合があります。コンテンツへの安全なアクセスを許可するのは、それが意図されたユーザーに対してのみであり、アクセスが許可されている時間のみです。ただし、これにはバックエンドでの作業も必要になります。

4
SzabV

Access-Control-Allow-Credentials: trueヘッダーを使用してみてください。私はかつてIEの問題に遭遇しました。これは最終的にこのヘッダーの使用に要約されました。また、angular jsコードで$httpProvider.defaults.headers.get = { 'withCredentials' : 'true' }.

2
Aziz Shaikh

私はいつも解析する

Set-Cookie前の(または最初のログイン要求)のヘッダー値を指定し、次の要求でその値を送信します。

このようなもの

最初の要求後の応答:

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

次のリクエスト:

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;

ご覧のとおり、ASP.NET_SessionId = "any value"の値をCookieヘッダーで送信します。サーバーがphpを使用する場合、PHPSESSID = "some value"を解析する必要があります

2
Sergey Pekar

理由として:私はChromeとFirefoxを試しましたが、両方とも基本的な承認を覚えていますのみ資格情報が入力されますブラウザUIから直接、つまり、ブラウザによって作成されたポップアップです。HTTPリクエストは同じですが、資格情報がJavaScriptからのものである場合は記憶されません。これは仕様によるものですが、標準では言及されていません。

0
Franklin Yu