/* 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;
        }
예제 #2
0
        // [ 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);
        }
예제 #3
0
        // [ 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);
        }
예제 #4
0
        // [ 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);
        }
예제 #5
0
        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);
        }
예제 #6
0
        /// <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);
        }
예제 #7
0
        /// <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);
        }
예제 #8
0
        /// <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);
        }
예제 #9
0
        /// <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();
        }
예제 #10
0
        /// <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;
        }
예제 #11
0
        /* 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);
        }
예제 #12
0
        // [ 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);
        }
예제 #13
0
 private string Serialize(AntiForgeryToken token)
 {
     return (token != null) ? _serializer.Serialize(token) : null;
 }
예제 #14
0
        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);
        }
예제 #15
0
 /// <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);
 }
예제 #16
0
        /// <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();
            }
        }
예제 #17
0
 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());
                }
            }
        }
예제 #19
0
 /// <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);
 }
예제 #20
0
        /// <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();
            }
        }