/* The serialized format of the anti-XSRF token is as follows: * Version: 1 byte integer * SecurityToken: 16 byte binary blob * IsSessionToken: 1 byte Boolean * [if IsSessionToken = true] * +- IsClaimsBased: 1 byte Boolean * | [if IsClaimsBased = true] * | `- ClaimUid: 32 byte binary blob * | [if IsClaimsBased = false] * | `- Username: UTF-8 string with 7-bit integer length prefix * `- AdditionalData: UTF-8 string with 7-bit integer length prefix */ private static AntiForgeryToken DeserializeImpl(BinaryReader reader) { // we can only consume tokens of the same serialized version that we generate byte embeddedVersion = reader.ReadByte(); if (embeddedVersion != TokenVersion) { return null; } AntiForgeryToken deserializedToken = new AntiForgeryToken(); byte[] securityTokenBytes = reader.ReadBytes(AntiForgeryToken.SecurityTokenBitLength / 8); deserializedToken.SecurityToken = new BinaryBlob(AntiForgeryToken.SecurityTokenBitLength, securityTokenBytes); deserializedToken.IsSessionToken = reader.ReadBoolean(); if (!deserializedToken.IsSessionToken) { bool isClaimsBased = reader.ReadBoolean(); if (isClaimsBased) { byte[] claimUidBytes = reader.ReadBytes(AntiForgeryToken.ClaimUidBitLength / 8); deserializedToken.ClaimUid = new BinaryBlob(AntiForgeryToken.ClaimUidBitLength, claimUidBytes); } else { deserializedToken.Username = reader.ReadString(); } deserializedToken.AdditionalData = reader.ReadString(); } // if there's still unconsumed data in the stream, fail if (reader.BaseStream.ReadByte() != -1) { return null; } // success return deserializedToken; }
// [ ENTRY POINT ] // Given the serialized string representations of a cookie & form token, // validates that the pair is OK for this request. public void Validate(HttpContextBase httpContext, string cookieToken, string formToken) { CheckSSLConfig(httpContext); // Extract cookie & form tokens AntiForgeryToken deserializedCookieToken = DeserializeToken(cookieToken); AntiForgeryToken deserializedFormToken = DeserializeToken(formToken); // Validate _validator.ValidateTokens(httpContext, ExtractIdentity(httpContext), deserializedCookieToken, deserializedFormToken); }
// [ ENTRY POINT ] // Given an HttpContext, validates that the anti-XSRF tokens contained // in the cookies & form are OK for this request. public void Validate(HttpContextBase httpContext) { CheckSSLConfig(httpContext); // Extract cookie & form tokens AntiForgeryToken cookieToken = _tokenStore.GetCookieToken(httpContext); AntiForgeryToken formToken = _tokenStore.GetFormToken(httpContext); // Validate _validator.ValidateTokens(httpContext, ExtractIdentity(httpContext), cookieToken, formToken); }
// [ ENTRY POINT ] // Generates a (cookie, form) serialized token pair for the current user. // The caller may specify an existing cookie value if one exists. If the // 'new cookie value' out param is non-null, the caller *must* persist // the new value to cookie storage since the original value was null or // invalid. This method is side-effect free. public void GetTokens(HttpContextBase httpContext, string serializedOldCookieToken, out string serializedNewCookieToken, out string serializedFormToken) { CheckSSLConfig(httpContext); AntiForgeryToken oldCookieToken = DeserializeTokenNoThrow(serializedOldCookieToken); AntiForgeryToken newCookieToken, formToken; GetTokens(httpContext, oldCookieToken, out newCookieToken, out formToken); serializedNewCookieToken = Serialize(newCookieToken); serializedFormToken = Serialize(formToken); }
private void GetTokens(HttpContextBase httpContext, AntiForgeryToken oldCookieToken, out AntiForgeryToken newCookieToken, out AntiForgeryToken formToken) { newCookieToken = null; if (!_validator.IsCookieTokenValid(oldCookieToken)) { // Need to make sure we're always operating with a good cookie token. oldCookieToken = newCookieToken = _validator.GenerateCookieToken(); } #if NET_4_0 Contract.Assert(_validator.IsCookieTokenValid(oldCookieToken)); #endif formToken = _validator.GenerateFormToken(httpContext, ExtractIdentity(httpContext), oldCookieToken); }
/// <summary>Generates a form token.</summary> /// /// <exception cref="InvalidOperationException">Thrown when the requested operation is invalid.</exception> /// /// <param name="httpContext">Context for the HTTP.</param> /// <param name="identity"> The identity.</param> /// <param name="cookieToken">The cookie token.</param> /// /// <returns>The form token.</returns> public AntiForgeryToken GenerateFormToken(HttpContextBase httpContext, IIdentity identity, AntiForgeryToken cookieToken) { #if NET_4_0 Contract.Assert(IsCookieTokenValid(cookieToken)); #endif AntiForgeryToken formToken = new AntiForgeryToken() { SecurityToken = cookieToken.SecurityToken, IsSessionToken = false }; bool requireAuthenticatedUserHeuristicChecks = false; // populate Username and ClaimUid if (identity != null && identity.IsAuthenticated) { if (!_config.SuppressIdentityHeuristicChecks) { // If the user is authenticated and heuristic checks are not suppressed, // then Username, ClaimUid, or AdditionalData must be set. requireAuthenticatedUserHeuristicChecks = true; } formToken.ClaimUid = _claimUidExtractor.ExtractClaimUid(identity); if (formToken.ClaimUid == null) { formToken.Username = identity.Name; } } // populate AdditionalData if (_config.AdditionalDataProvider != null) { formToken.AdditionalData = _config.AdditionalDataProvider.GetAdditionalData(httpContext); } if (requireAuthenticatedUserHeuristicChecks && String.IsNullOrEmpty(formToken.Username) && formToken.ClaimUid == null && String.IsNullOrEmpty(formToken.AdditionalData)) { // Application says user is authenticated, but we have no identifier for the user. throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, MvcResources.TokenValidator_AuthenticatedUserWithoutUsername, identity.GetType())); } return(formToken); }
/// <summary>Saves a cookie token.</summary> /// /// <param name="httpContext">Context for the HTTP.</param> /// <param name="token"> The token.</param> public void SaveCookieToken(HttpContextBase httpContext, AntiForgeryToken token) { string serializedToken = _serializer.Serialize(token); HttpCookie newCookie = new HttpCookie(_config.CookieName, serializedToken) { HttpOnly = true }; // Note: don't use "newCookie.Secure = _config.RequireSSL;" since the default // value of newCookie.Secure is automatically populated from the <httpCookies> // config element. if (_config.RequireSSL) { newCookie.Secure = true; } httpContext.Response.Cookies.Set(newCookie); }
/// <summary>true this object to the given stream.</summary> /// /// <exception>Thrown when a Create Deserialization Failed error condition occurs.</exception> /// /// <param name="serializedToken">The serialized token.</param> /// /// <returns>An AntiForgeryToken.</returns> public AntiForgeryToken Deserialize(string serializedToken) { try { using (MemoryStream stream = new MemoryStream(_cryptoSystem.Unprotect(serializedToken))) { using (BinaryReader reader = new BinaryReader(stream)) { AntiForgeryToken token = DeserializeImpl(reader); if (token != null) { return(token); } } } } catch { // swallow all exceptions - homogenize error if something went wrong } // if we reached this point, something went wrong deserializing throw HttpAntiForgeryException.CreateDeserializationFailedException(); }
/// <summary>Generates a form token.</summary> /// /// <exception cref="InvalidOperationException">Thrown when the requested operation is invalid.</exception> /// /// <param name="httpContext">Context for the HTTP.</param> /// <param name="identity"> The identity.</param> /// <param name="cookieToken">The cookie token.</param> /// /// <returns>The form token.</returns> public AntiForgeryToken GenerateFormToken(HttpContextBase httpContext, IIdentity identity, AntiForgeryToken cookieToken) { #if NET_4_0 Contract.Assert(IsCookieTokenValid(cookieToken)); #endif AntiForgeryToken formToken = new AntiForgeryToken() { SecurityToken = cookieToken.SecurityToken, IsSessionToken = false }; bool requireAuthenticatedUserHeuristicChecks = false; // populate Username and ClaimUid if (identity != null && identity.IsAuthenticated) { if (!_config.SuppressIdentityHeuristicChecks) { // If the user is authenticated and heuristic checks are not suppressed, // then Username, ClaimUid, or AdditionalData must be set. requireAuthenticatedUserHeuristicChecks = true; } formToken.ClaimUid = _claimUidExtractor.ExtractClaimUid(identity); if (formToken.ClaimUid == null) { formToken.Username = identity.Name; } } // populate AdditionalData if (_config.AdditionalDataProvider != null) { formToken.AdditionalData = _config.AdditionalDataProvider.GetAdditionalData(httpContext); } if (requireAuthenticatedUserHeuristicChecks && String.IsNullOrEmpty(formToken.Username) && formToken.ClaimUid == null && String.IsNullOrEmpty(formToken.AdditionalData)) { // Application says user is authenticated, but we have no identifier for the user. throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, MvcResources.TokenValidator_AuthenticatedUserWithoutUsername, identity.GetType())); } return formToken; }
/* The serialized format of the anti-XSRF token is as follows: * Version: 1 byte integer * SecurityToken: 16 byte binary blob * IsSessionToken: 1 byte Boolean * [if IsSessionToken = true] * +- IsClaimsBased: 1 byte Boolean * | [if IsClaimsBased = true] * | `- ClaimUid: 32 byte binary blob * | [if IsClaimsBased = false] * | `- Username: UTF-8 string with 7-bit integer length prefix * `- AdditionalData: UTF-8 string with 7-bit integer length prefix */ private static AntiForgeryToken DeserializeImpl(BinaryReader reader) { // we can only consume tokens of the same serialized version that we generate byte embeddedVersion = reader.ReadByte(); if (embeddedVersion != TokenVersion) { return(null); } AntiForgeryToken deserializedToken = new AntiForgeryToken(); byte[] securityTokenBytes = reader.ReadBytes(AntiForgeryToken.SecurityTokenBitLength / 8); deserializedToken.SecurityToken = new BinaryBlob(AntiForgeryToken.SecurityTokenBitLength, securityTokenBytes); deserializedToken.IsSessionToken = reader.ReadBoolean(); if (!deserializedToken.IsSessionToken) { bool isClaimsBased = reader.ReadBoolean(); if (isClaimsBased) { byte[] claimUidBytes = reader.ReadBytes(AntiForgeryToken.ClaimUidBitLength / 8); deserializedToken.ClaimUid = new BinaryBlob(AntiForgeryToken.ClaimUidBitLength, claimUidBytes); } else { deserializedToken.Username = reader.ReadString(); } deserializedToken.AdditionalData = reader.ReadString(); } // if there's still unconsumed data in the stream, fail if (reader.BaseStream.ReadByte() != -1) { return(null); } // success return(deserializedToken); }
// [ ENTRY POINT ] // Generates an anti-XSRF token pair for the current user. The return // value is the hidden input form element that should be rendered in // the <form>. This method has a side effect: it may set a response // cookie. public TagBuilder GetFormInputElement(HttpContextBase httpContext) { CheckSSLConfig(httpContext); AntiForgeryToken oldCookieToken = GetCookieTokenNoThrow(httpContext); AntiForgeryToken newCookieToken, formToken; GetTokens(httpContext, oldCookieToken, out newCookieToken, out formToken); if (newCookieToken != null) { // If a new cookie was generated, persist it. _tokenStore.SaveCookieToken(httpContext, newCookieToken); } // <input type="hidden" name="__AntiForgeryToken" value="..." /> TagBuilder retVal = new TagBuilder("input"); retVal.Attributes["type"] = "hidden"; retVal.Attributes["name"] = _config.FormFieldName; retVal.Attributes["value"] = _serializer.Serialize(formToken); return(retVal); }
private string Serialize(AntiForgeryToken token) { return (token != null) ? _serializer.Serialize(token) : null; }
/// <summary>Query if 'cookieToken' is cookie token valid.</summary> /// /// <param name="cookieToken">The cookie token.</param> /// /// <returns>true if cookie token valid, false if not.</returns> public bool IsCookieTokenValid(AntiForgeryToken cookieToken) { return (cookieToken != null && cookieToken.IsSessionToken); }
/// <summary>Validates the tokens.</summary> /// /// <exception cref="HttpAntiForgeryException"> Thrown when a Create Cookie Missing error condition occurs.</exception> /// <exception cref="HttpAntiForgeryException"> Thrown when a Create Form Field Missing error condition occurs.</exception> /// <exception cref="HttpAntiForgeryException"> Thrown when a Create Tokens Swapped error condition occurs.</exception> /// <exception cref="HttpAntiForgeryException"> Thrown when a Create Security Token Mismatch error condition occurs.</exception> /// <exception cref="HttpAntiForgeryException"> Thrown when a Create Username Mismatch error condition occurs.</exception> /// <exception cref="HttpAntiForgeryException"> Thrown when a Create Claim UID Mismatch error condition occurs.</exception> /// <exception cref="HttpAntiForgeryException">Thrown when a Create Additional Data Check Failed error condition occurs.</exception> /// /// <param name="httpContext"> Context for the HTTP.</param> /// <param name="identity"> The identity.</param> /// <param name="sessionToken">The session token.</param> /// <param name="fieldToken"> The field token.</param> public void ValidateTokens(HttpContextBase httpContext, IIdentity identity, AntiForgeryToken sessionToken, AntiForgeryToken fieldToken) { // Were the tokens even present at all? if (sessionToken == null) { throw HttpAntiForgeryException.CreateCookieMissingException(_config.CookieName); } if (fieldToken == null) { throw HttpAntiForgeryException.CreateFormFieldMissingException(_config.FormFieldName); } // Do the tokens have the correct format? if (!sessionToken.IsSessionToken || fieldToken.IsSessionToken) { throw HttpAntiForgeryException.CreateTokensSwappedException(_config.CookieName, _config.FormFieldName); } // Are the security tokens embedded in each incoming token identical? if (!Equals(sessionToken.SecurityToken, fieldToken.SecurityToken)) { throw HttpAntiForgeryException.CreateSecurityTokenMismatchException(); } // Is the incoming token meant for the current user? string currentUsername = String.Empty; BinaryBlob currentClaimUid = null; if (identity != null && identity.IsAuthenticated) { currentClaimUid = _claimUidExtractor.ExtractClaimUid(identity); if (currentClaimUid == null) { currentUsername = identity.Name ?? String.Empty; } } // OpenID and other similar authentication schemes use URIs for the username. // These should be treated as case-sensitive. bool useCaseSensitiveUsernameComparison = currentUsername.StartsWith("http://", StringComparison.OrdinalIgnoreCase) || currentUsername.StartsWith("https://", StringComparison.OrdinalIgnoreCase); if (!String.Equals(fieldToken.Username, currentUsername, (useCaseSensitiveUsernameComparison) ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase)) { throw HttpAntiForgeryException.CreateUsernameMismatchException(fieldToken.Username, currentUsername); } if (!Equals(fieldToken.ClaimUid, currentClaimUid)) { throw HttpAntiForgeryException.CreateClaimUidMismatchException(); } // Is the AdditionalData valid? if (_config.AdditionalDataProvider != null && !_config.AdditionalDataProvider.ValidateAdditionalData(httpContext, fieldToken.AdditionalData)) { throw HttpAntiForgeryException.CreateAdditionalDataCheckFailedException(); } }
private string Serialize(AntiForgeryToken token) { return((token != null) ? _serializer.Serialize(token) : null); }
/// <summary>true this object to the given stream.</summary> /// /// <param name="token">The token.</param> /// /// <returns>A string.</returns> public string Serialize(AntiForgeryToken token) { #if NET_4_0 Contract.Assert(token != null); #endif using (MemoryStream stream = new MemoryStream()) { using (BinaryWriter writer = new BinaryWriter(stream)) { writer.Write(TokenVersion); writer.Write(token.SecurityToken.GetData()); writer.Write(token.IsSessionToken); if (!token.IsSessionToken) { if (token.ClaimUid != null) { writer.Write(true /* isClaimsBased */); writer.Write(token.ClaimUid.GetData()); } else { writer.Write(false /* isClaimsBased */); writer.Write(token.Username); } writer.Write(token.AdditionalData); } writer.Flush(); return _cryptoSystem.Protect(stream.ToArray()); } } }
/// <summary>Query if 'cookieToken' is cookie token valid.</summary> /// /// <param name="cookieToken">The cookie token.</param> /// /// <returns>true if cookie token valid, false if not.</returns> public bool IsCookieTokenValid(AntiForgeryToken cookieToken) { return(cookieToken != null && cookieToken.IsSessionToken); }