web-dev-qa-db-ja.com

ng2はCookieからcsrfトークンを取得し、ヘッダーとして投稿します

2日間ウェブを検索し、同じ問題に直面している人々のドキュメントとたくさんの未解決の質問を読んだ後、私はまだAngular 2が(x-Origin)Cookieをどのように処理するかを理解していませんそれらにアクセスする方法。

問題:バックエンドは、x-csrf-tokenとJSESSIONIDを含む2つのCookieを送信します。私の仕事は、csrfトークンをメモリ(ng2)に保持し、バックエンドへのすべての投稿でヘッダー(Cookieではない)として(のみ)送り返すことです。

_HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Access-Control-Allow-Origin: http://localhost:4200
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: Access-Control-Allow-Origin,Access-Control-Allow-Credentials
Set-Cookie: x-csrf-token=8555257a-396f-43ac-8587-c6d489e76026; Path=/app
Set-Cookie: JSESSIONID=73E38392C60370E38FBAF80143ECE212; Path=/app/; HttpOnly
Expires: Thu, 12 Apr 2018 07:49:02 GMT
Cache-Control: max-age=31536000
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Wed, 12 Apr 2017 07:49:02 GMT
_

私の部分的な解決策: BaseRequestOptionsを拡張するカスタムRequesstOptionsクラスを作成しました。いくつかのヘッダーを追加し、「withCredentials」をtrueに設定しました。

_export class MyRequestOptions extends BaseRequestOptions {

  headers: Headers = new Headers({
    'Accept': 'application/json',
    'Content-Type': 'application/json',
  });

  withCredentials = true;
}
_

私のHttpServiceで投稿を行い、次のようになります。

_@Injectable()
export class HttpService {

  constructor(
    protected _http: Http,
    protected requestOptions: RequestOptions
  ) {  }

  get(url): Observable<any> {
    return this._http.get(url, this.requestOptions).map( res => res.json() );
  }

  post(url: string, object: any): Observable<any> {
    return this._http.post(url, object, this.requestOptions).map( res => res.json() );
  }
}
_

そして私のapp.moduleで私はそのような魔法をします:

_ providers: [
    { provide: RequestOptions, useClass: DocumentumDefaultRequestOptions },
    { provide: XSRFStrategy, useFactory: xsrfFactory }
  ],
_

私のxsrfFactory

_export function xsrfFactory() {
  return new CookieXSRFStrategy('x-csrf-token', 'x-csrf-token');
}
_

私の部分的な結果:この時点でangularはすべてのリクエストでCookieを送信します(GETおよびPOST区別なし)) x-csrf-token like so:

_POST /app/business-objects/business-objects-type HTTP/1.1
Host: localhost:8040
Connection: keep-alive
Content-Length: 26
Pragma: no-cache
Cache-Control: no-cache
Authorization: Basic ZG1hZG1pbjphZG1pbg==
Origin: http://localhost:4200
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36
Content-Type: application/json
Accept: application/json
Referer: http://localhost:4200/page
Cookie: JSESSIONID=B874C9A170EFC12BEB0EDD4266896F2A; x-csrf-token=0717876e-f402-4a1c-a31a-2d60e48509d3
_

私の10億ドルの質問:

  • X-csrf-tokenにアクセスする方法と場所、およびそれをリクエストに追加するにはどうすればよいですか?
  • CookieXSRFStrategy('x-csrf-token', 'x-csrf-token');は正確に何をしますか。私はブラックボックスの感覚が好きではありません/ドキュメントがそれを説明した方法を理解しています。データのためにアクセスできますか?

HTTPリクエストを送信する前に、CookieXSRFStrategyはXSRF-TOKENという名前のCookieを探し、そのCookieの値を使用してX-XSRF-TOKENという名前のヘッダーを設定します。

  • 私の場合、ヘッダーは設定されません...しかし、なぜですか?

  • 現在、セッションIDとcsrfトークンを使用してCookieをバックエンドに送信していますが、何を送信していますか? CookieXSRFStrategyまたは「withCredentials」フラグ。

「document.cookie」のような1つのライナーで答えないでください。データはメタデータなしでは役に立たない

7
VikingCode

angular 5.0+の更新

HttpサービスはHttpClientを優先して非推奨になり、CookieXSRFStrategyクラスも非推奨になりました。現在、このミッションはHttpClientXsrfModuleクラスに委任されています。ヘッダーとCookieの名前をカスタマイズする場合は、次のようにこのモジュールをインポートする必要があります。

@NgModule({
  imports: [
    HttpClientModule,
    HttpClientXsrfModule.withConfig({
      cookieName: 'My-Xsrf-Cookie',
      headerName: 'My-Xsrf-Header',
    }),
  ]
})
export class MyModule{}

将来の読者のために:

Ajax応答Cookie

Ajax応答からCookieにアクセスすることはできません。 XHR仕様 をチェックすると、「Set-Cookie」に一致するヘッダーへのアクセスが禁止されていることがわかります。

禁止されている応答ヘッダー名は、次のいずれかにバイト大文字と小文字を区別しない一致であるヘッダー名です。

  • Set-Cookie
  • Set-Cookie2

しかし、私のクッキーはhttpOnlyではありません

良いことですが、httpOnlyは、document.cookieを介してCookieにアクセスできないことを示しているだけです(詳細を参照)。

document.cookie AP​​I

JavascriptでアクセスできるCookieはdocument.cookieのみですが、document.cookieドキュメントとともに送信されたCookie(スクリプトが実行されているページ)を指します。およびいつでも変更されません。 MDNは、現在のドキュメントを参照していることを明確に述べています。

Document.cookie

現在のドキュメントに関連付けられているCookieを取得して設定します。一般的なライブラリについては、この単純なCookieフレームワークを参照してください。

ソース: [〜#〜] mdn [〜#〜]

Ajax応答によって設定されたCookieは、現在のドキュメントに属していません。

では、csrf保護を実装するにはどうすればよいですか?

Cookieからヘッダートークンへの保護 がその方法です。 送信するトークンはセッション全体で同じであり、Cookieを送信するワイルドなAjaxリクエストに応じて変更されることは想定されていません

操作の大部分にJavaScriptを使用するWebアプリケーションは、同一生成元ポリシーに依存する反CSRF手法を使用する場合があります。

  • ログイン時に、Webアプリケーションは、ユーザーセッション全体で同じままであるランダムトークンを含むCookieを設定します

    Set-Cookie: Csrf-token=i8XNjC4b8KVok4uw5RftR38Wgp2BFwql; expires=Thu, 23-Jul-2015 10:25:33 GMT; Max-Age=31449600; Path=/
    
  • クライアント側で動作するJavaScriptは、その値を読み取り、トランザクション要求ごとに送信されるカスタムHTTPヘッダーにコピーします。

    X-Csrf-Token: i8XNjC4b8KVok4uw5RftR38Wgp2BFwql
    
  • サーバーはトークンの存在と整合性を検証します

この手法のセキュリティは、同じオリジン内で実行されているJavaScriptのみがCookieの値を読み取ることができるという前提に基づいています。不正なファイルまたは電子メールから実行されているJavaScriptは、それを読み取ってカスタムヘッダーにコピーすることができません。 csrf-token cookieは不正なリクエストとともに自動的に送信されますが、サーバーは引き続き有効なX-Csrf-Tokenヘッダーを期待しています。

出典: ウィキペディア:CSRF

Angular 2+ 2+の場合、このミッションはCookieXSRFStrategyクラスによって実行されます。

元の回答

X-csrf-tokenにアクセスする方法と場所、およびそれをリクエストに追加するにはどうすればよいですか?

CookieXSRFStrategyを使用することは、それをリクエストに追加する方法のようです。 「方法」については、残念ながら、答えは「できない」かもしれません(詳細を参照)。

CookieXSRFStrategy( 'x-csrf-token'、 'x-csrf-token');とはまさにそうです。私はブラックボックスの感覚が好きではありません/ドキュメントがそれを説明した方法を理解しています。

CookieXSRFStrategy

/**
 * `XSRFConfiguration` sets up Cross Site Request Forgery (XSRF) protection for the application
 * using a cookie. See https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)
 * for more information on XSRF.
 *
 * Applications can configure custom cookie and header names by binding an instance of this class
 * with different `cookieName` and `headerName` values. See the main HTTP documentation for more
 * details.
 *
 * @experimental
 */
export class CookieXSRFStrategy implements XSRFStrategy {
  constructor(
      private _cookieName: string = 'XSRF-TOKEN', private _headerName: string = 'X-XSRF-TOKEN') {}

  configureRequest(req: Request): void {
    const xsrfToken = getDOM().getCookie(this._cookieName);
    if (xsrfToken) {
      req.headers.set(this._headerName, xsrfToken);
    }
  }
}

ソース

基本的に、document.cookieからCookieを読み取り、それに応じてRequestヘッダーを変更します。

現在、セッションIDとcsrfトークンを使用してCookieをバックエンドに送信していますが、何を送信していますか? CookieXSRFStrategyまたは「withCredentials」フラグ。

これはwithCredentialsフラグです。このフラグは、XHRが送信されたすべてのCookieを送信する必要があることを示します(以前にAjax応答によって設定されたものも含みますが、ヘッダーではなくCookieとしてこの動作を変更する方法はありません)

私の場合、ヘッダーは設定されません...しかし、なぜですか?

あなたが話しているCookieは、ドキュメント(index.html)ではなく、別のajaxリクエストから送信されます。事実、ajax応答( この回答を参照 )によって設定されたCookieにアクセスすることはできません。これはセキュリティの問題になるためです。ランダムなWebページからwww.stackoverflow.comにアクセスする単純なajaxはスタックオーバーフローCookie。攻撃者はそれを簡単に盗む可能性があります(stackoverflow応答にAccess-Control-Allow-Origin: *ヘッダーが存在する場合)。

一方、document.cookie AP​​Iは、ドキュメントが読み込まれたときに設定されたCookieにのみアクセスでき、他のCookieにはアクセスできません。

したがって、ヘッダーにコピーできる唯一のCookieは、スクリプトが実行されているドキュメント(インデックス)とともに送信されたものであるため、サーバー側でのクライアント/サーバー通信のフローを再考する必要があります。 html)。

これはhttpOnlyCookieではないため、XOriginであってもjsでアクセスできる必要があります

httpOnlyはCookieをdocument.cookie AP​​Iで使用できないようにします、but先ほど申し上げたように、document.cookieで送信されたCookieを指しますAjax応答を介して送信されたものではなく、ドキュメント。応答にSet-Cookieヘッダーを使用して何千ものajax呼び出しを行うことができます。document.cookieは変更なしで同じ文字列のままです。

10億ドルのソリューション

サーバーは、ドキュメントとともにトークンを含むx-csrf-token Cookieを1つだけ送信する必要があり、CookieXSRFStrategyを使用するすべてのリクエストのセッション全体で有効なトークンを使用する必要があります。どうして ?なぜなら それがどのように機能するか

18
n00dl3

AngularにはXSRFのサポートが組み込まれていますここを参照してください: https://angular.io/guide/http#security-xsrf-protection

"HTTPリクエストを実行するとき、インターセプターはCookieからトークンを読み取ります。デフォルトではXSRF-TOKENであり、HTTPヘッダーX-XSRF-TOKENとして設定します"

したがって、サーバーがXSRF-TOKENという名前のCookieを設定すると、自動的に機能します!クライアント側では何もしません。 Cookie /ヘッダーに別の名前を付けたい場合は、次のようにすることもできます。

imports: [
  HttpClientModule,
  HttpClientXsrfModule.withConfig({
    cookieName: 'My-Xsrf-Cookie',
    headerName: 'My-Xsrf-Header',
  }),
]

また、Spring Securityを使用している場合は、angular命名規則をサポートしているため、このサーバー側を構成できます。

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
            .and()
            .authorizeRequests()
            ....    
0
MarkJ