web-dev-qa-db-ja.com

Angular Asp.Net WebApiに対して、サーバーにCSRFを実装

ASP.NET WebAPIバックエンドにアクセスしているAngular.jsでWebサイトを実装しています。

Angular.jsには、csrf防止対策に役立つ組み込み機能がいくつかあります。各HTTPリクエストで、「XSRF-TOKEN」というCookieを探し、「X-XSRF-TOKEN」というヘッダーとして送信します。

これは、ユーザーを認証した後にXSRF-TOKEN Cookieを設定できるWebサーバーに依存し、着信要求のX-XSRF-TOKENヘッダーを確認します。

Angular documentation 状態:

これを利用するには、サーバーは最初のHTTP GETリクエストでXSRF-TOKENと呼ばれるJavaScriptで読み取り可能なセッションCookieにトークンを設定する必要があります。後続の非GET要求では、サーバーはCookieがX-XSRF-TOKEN HTTPヘッダーと一致することを確認できるため、ドメインで実行されているJavaScriptのみがトークンを読み取れることを確認してください。トークンはユーザーごとに一意であり、サーバーによって検証可能である必要があります(JavaScriptが独自のトークンを作成しないようにするため)。トークンは、セキュリティを強化するために、塩を含むサイトの認証Cookieのダイジェストにすることをお勧めします。

ASP.NET WebAPIのこの良い例を見つけることができなかったので、さまざまなソースからの助けを借りて自分自身をロールバックしました。私の質問は-誰でもコードの問題を見ることはできますか?

最初に、単純なヘルパークラスを定義しました。

public class CsrfTokenHelper
{
    const string ConstantSalt = "<ARandomString>";

    public string GenerateCsrfTokenFromAuthToken(string authToken)
    {
        return GenerateCookieFriendlyHash(authToken);
    }

    public bool DoesCsrfTokenMatchAuthToken(string csrfToken, string authToken) 
    {
        return csrfToken == GenerateCookieFriendlyHash(authToken);
    }

    private static string GenerateCookieFriendlyHash(string authToken)
    {
        using (var sha = SHA256.Create())
        {
            var computedHash = sha.ComputeHash(Encoding.Unicode.GetBytes(authToken + ConstantSalt));
            var cookieFriendlyHash = HttpServerUtility.UrlTokenEncode(computedHash);
            return cookieFriendlyHash;
        }
    }
}

次に、承認コントローラーに次のメソッドがあり、FormsAuthentication.SetAuthCookie()を呼び出した後に呼び出します。

    // http://www.asp.net/web-api/overview/security/preventing-cross-site-request-forgery-(csrf)-attacks
    // http://docs.angularjs.org/api/ng.$http
    private void SetCsrfCookie()
    {
        var authCookie = HttpContext.Current.Response.Cookies.Get(".ASPXAUTH");
        Debug.Assert(authCookie != null, "authCookie != null");
        var csrfToken = new CsrfTokenHelper().GenerateCsrfTokenFromAuthToken(authCookie.Value);
        var csrfCookie = new HttpCookie("XSRF-TOKEN", csrfToken) {HttpOnly = false};
        HttpContext.Current.Response.Cookies.Add(csrfCookie);
    }

次に、コントローラーにcsrfヘッダーをチェックさせるカスタム属性を追加します。

public class CheckCsrfHeaderAttribute : AuthorizeAttribute
{
    //  http://stackoverflow.com/questions/11725988/problems-implementing-validatingantiforgerytoken-attribute-for-web-api-with-mvc
    protected override bool IsAuthorized(HttpActionContext context)
    {
        // get auth token from cookie
        var authCookie = HttpContext.Current.Request.Cookies[".ASPXAUTH"];
        if (authCookie == null) return false;
        var authToken = authCookie.Value;

        // get csrf token from header
        var csrfToken = context.Request.Headers.GetValues("X-XSRF-TOKEN").FirstOrDefault();
        if (String.IsNullOrEmpty(csrfToken)) return false;

        // Verify that csrf token was generated from auth token
        // Since the csrf token should have gone out as a cookie, only our site should have been able to get it (via javascript) and return it in a header. 
        // This proves that our site made the request.
        return new CsrfTokenHelper().DoesCsrfTokenMatchAuthToken(csrfToken, authToken);
    }
}

最後に、ユーザーがログアウトするときにCsrfトークンをクリアします。

HttpContext.Current.Response.Cookies.Remove("XSRF-TOKEN");

誰もがそのアプローチで明らかな(またはそれほど明白ではない)問題を見つけることができますか?

69
dbruning

コードは問題ないようです。唯一のことは、web.apiがasp.net mvcの「トップ」で実行されるため、所有するコードのほとんどを必要としないことです。後者は偽造防止トークンのサポートを組み込みます。

コメントでは、dbrunningとccorrinは、MVC htmlヘルパーを使用している場合にのみ、AntiForgeryトークンでビルドを使用できるという懸念を表明します。それは本当ではない。ヘルパーは、相互に検証できるセッションベースのトークンペアを公開できます。詳細は以下をご覧ください。

更新:

AntiForgeryから使用できる方法は2つあります。

  • _AntiForgery.GetTokens_は2つの出力パラメーターを使用してCookieトークンとフォームトークンを返します

  • AntiForgery.Validate(cookieToken, formToken)は、トークンのペアが有効かどうかを検証します

これら2つのメソッドを完全に再利用し、formTokenをheaderTokenとして使用し、cookieTokenを実際のcookieTokenとして使用できます。次に、属性内の両方でvalidateを呼び出します。

別の解決策は、JWTを使用することです(例: MembershipReboot implementation)

このリンク は、ajaxで組み込みの偽造防止トークンを使用する方法を示しています。

_<script>
    @functions{
        public string TokenHeaderValue()
        {
            string cookieToken, formToken;
            AntiForgery.GetTokens(null, out cookieToken, out formToken);
            return cookieToken + ":" + formToken;                
        }
    }

    $.ajax("api/values", {
        type: "post",
        contentType: "application/json",
        data: {  }, // JSON data goes here
        dataType: "json",
        headers: {
            'RequestVerificationToken': '@TokenHeaderValue()'
        }
    });
</script>


void ValidateRequestHeader(HttpRequestMessage request)
{
    string cookieToken = "";
    string formToken = "";

    IEnumerable<string> tokenHeaders;
    if (request.Headers.TryGetValues("RequestVerificationToken", out tokenHeaders))
    {
        string[] tokens = tokenHeaders.First().Split(':');
        if (tokens.Length == 2)
        {
            cookieToken = tokens[0].Trim();
            formToken = tokens[1].Trim();
        }
    }
    AntiForgery.Validate(cookieToken, formToken);
}
_

この質問もご覧ください AngularJSはXSRF-TOKEN Cookieを見つけることができません

8
vittore

あなたのコードには欠陥があると思います。 CSRFの防止に関する全体的な考え方は、各セッションではなく、各REQUESTで一意のトークンを防止することです。偽造防止トークンがセッション永続値である場合、CSRFを実行する機能は依然として残ります。各リクエストで一意のトークンを提供する必要があります...

0
Kolchy

認証Cookieが有効である限りCSRF攻撃は依然として可能であるため、このソリューションは安全ではありません。攻撃者が別のサイト経由でリクエストを実行すると、auth Cookieとxsrf Cookieの両方がサーバーに送信されるため、ユーザーが「ハード」ログアウトするまで脆弱です。

CRSF攻撃を完全に防ぐには、各リクエストまたはセッションに固有のトークンが必要です。しかし、おそらく最善の解決策は、Cookieベースの認証ではなく、OAuthなどのトークンベースの認証を使用することです。これにより、他のWebサイトがCookieを使用して不要なリクエストを実行できなくなります。これは、トークンがCookieの代わりにhttpヘッダーで使用されるためです。また、httpヘッダーは自動的に送信されません。

  1. ASP.NET Web API 2、Owin、およびIdentityを使用したトークンベースの認証
  2. ASP.NET Web API 2、Owin、およびIdentityを使用したAngularJSトークン認証

これらの優れたブログ投稿には、WebAPIのOAuthの実装方法に関する情報が含まれています。ブログ投稿には、AngularJSとの統合方法に関する優れた情報も含まれています。

別の解決策は、CORSを無効にして、ホワイトリストに登録されたドメインからの着信要求のみを受け入れることです。ただし、モバイルクライアントやデスクトップクライアントなど、Webサイト以外のアプリケーションでは機能しません。 WebサイトがXSS攻撃に対して脆弱になった場合、攻撃者はその半分でリクエストを偽造できます。

0
Dibran