VYPR
High severity8.8NVD Advisory· Published Mar 3, 2017· Updated May 13, 2026

CVE-2015-8814

CVE-2015-8814

Description

Umbraco before 7.4.0 allows remote attackers to bypass anti-forgery security measures and conduct cross-site request forgery (CSRF) attacks as demonstrated by editing user account information in the templates.asmx.cs file.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
Umbraco.CMSNuGet
< 7.4.07.4.0

Patches

1
18c3345e4766

Fixes U4-7459 XSRF protection bypass - ensures tokens are checked for the non-editor api controllers

https://github.com/umbraco/Umbraco-CMSShannonNov 25, 2015via ghsa
9 files changed · +116 16
  • src/Umbraco.Web/Mvc/ValidateMvcAngularAntiForgeryTokenAttribute.cs+61 0 added
    @@ -0,0 +1,61 @@
    +using System.Collections.Generic;
    +using System.Linq;
    +using System.Net;
    +using System.Security.Claims;
    +using System.Web.Mvc;
    +using Umbraco.Web.WebApi.Filters;
    +
    +namespace Umbraco.Web.Mvc
    +{
    +    /// <summary>
    +    /// A filter to check for the csrf token based on Angular's standard approach
    +    /// </summary>
    +    /// <remarks>
    +    /// Code derived from http://ericpanorel.net/2013/07/28/spa-authentication-and-csrf-mvc4-antiforgery-implementation/
    +    /// 
    +    /// If the authentication type is cookie based, then this filter will execute, otherwise it will be disabled
    +    /// </remarks>
    +    public sealed class ValidateMvcAngularAntiForgeryTokenAttribute : ActionFilterAttribute
    +    {
    +        public override void OnActionExecuting(ActionExecutingContext filterContext)
    +        {
    +            var userIdentity = filterContext.HttpContext.User.Identity as ClaimsIdentity;
    +            if (userIdentity != null)
    +            {
    +                //if there is not CookiePath claim, then exist
    +                if (userIdentity.HasClaim(x => x.Type == ClaimTypes.CookiePath) == false)
    +                {
    +                    base.OnActionExecuting(filterContext);
    +                    return;
    +                }
    +            }
    +
    +            string failedReason;
    +            var headers = new List<KeyValuePair<string, List<string>>>();
    +            foreach (var key in filterContext.HttpContext.Request.Headers.AllKeys)
    +            {
    +                if (headers.Any(x => x.Key == key))
    +                {
    +                    var found = headers.First(x => x.Key == key);
    +                    found.Value.Add(filterContext.HttpContext.Request.Headers[key]);
    +                }
    +                else
    +                {
    +                    headers.Add(new KeyValuePair<string, List<string>>(key, new List<string> { filterContext.HttpContext.Request.Headers[key] }));
    +                }
    +            }
    +            var cookie = filterContext.HttpContext.Request.Cookies[AngularAntiForgeryHelper.CsrfValidationCookieName];
    +            if (AngularAntiForgeryHelper.ValidateHeaders(
    +                headers.Select(x => new KeyValuePair<string, IEnumerable<string>>(x.Key, x.Value)).ToArray(),
    +                cookie == null ? "" : cookie.Value,
    +                out failedReason) == false)
    +            {
    +                var result = new HttpStatusCodeResult(HttpStatusCode.ExpectationFailed);
    +                filterContext.Result = result;
    +                return;
    +            }
    +
    +            base.OnActionExecuting(filterContext);
    +        }
    +    }
    +}
    \ No newline at end of file
    
  • src/Umbraco.Web.UI/umbraco_client/Application/Extensions.js+16 0 modified
    @@ -357,4 +357,20 @@
     
    
         });
    
     
    
    +    //This sets the default jquery ajax headers to include our csrf token, we
    
    +    // need to user the beforeSend method because our token changes per user/login so
    
    +    // it cannot be static
    
    +    $.ajaxSetup({
    
    +        beforeSend: function (xhr) {
    
    +
    
    +            function getCookie(name) {
    
    +                var value = "; " + document.cookie;
    
    +                var parts = value.split("; " + name + "=");
    
    +                if (parts.length === 2) return parts.pop().split(";").shift();
    
    +            }
    
    +
    
    +            xhr.setRequestHeader("X-XSRF-TOKEN", getCookie("XSRF-TOKEN"));
    
    +        }
    
    +    });
    
    +
    
     })(jQuery);
    \ No newline at end of file
    
  • src/Umbraco.Web/Umbraco.Web.csproj+1 0 modified
    @@ -306,6 +306,7 @@
         <Compile Include="Models\PublishedContentWithKeyBase.cs" />
    
         <Compile Include="Mvc\IRenderController.cs" />
    
         <Compile Include="Mvc\RenderIndexActionSelectorAttribute.cs" />
    
    +    <Compile Include="Mvc\ValidateMvcAngularAntiForgeryTokenAttribute.cs" />
    
         <Compile Include="PropertyEditors\DatePreValueEditor.cs" />
    
         <Compile Include="RequestLifespanMessagesFactory.cs" />
    
         <Compile Include="Scheduling\LatchedBackgroundTaskBase.cs" />
    
    
  • src/Umbraco.Web/WebApi/Filters/AngularAntiForgeryHelper.cs+30 15 modified
    @@ -1,4 +1,6 @@
     using System;
    +using System.Collections.Generic;
    +using System.Collections.Specialized;
     using System.Linq;
     using System.Net.Http;
     using System.Net.Http.Headers;
    @@ -28,6 +30,8 @@ public static class AngularAntiForgeryHelper
             /// </summary>
             public const string AngularHeadername = "X-XSRF-TOKEN";
     
    +        
    +
             /// <summary>
             /// Returns 2 tokens - one for the cookie value and one that angular should set as the header value
             /// </summary>
    @@ -64,13 +68,10 @@ public static bool ValidateTokens(string cookieToken, string headerToken)
                 return true;
             }
     
    -        /// <summary>
    -        /// Validates the headers/cookies passed in for the request
    -        /// </summary>
    -        /// <param name="requestHeaders"></param>
    -        /// <param name="failedReason"></param>
    -        /// <returns></returns>
    -        public static bool ValidateHeaders(HttpRequestHeaders requestHeaders, out string failedReason)
    +        internal static bool ValidateHeaders(            
    +            KeyValuePair<string, IEnumerable<string>>[] requestHeaders, 
    +            string cookieToken,
    +            out string failedReason)
             {
                 failedReason = "";
     
    @@ -85,26 +86,40 @@ public static bool ValidateHeaders(HttpRequestHeaders requestHeaders, out string
                     .Select(z => z.Value)
                     .SelectMany(z => z)
                     .FirstOrDefault();
    -
    -            var cookieToken = requestHeaders
    -                .GetCookies()
    -                .Select(c => c[CsrfValidationCookieName])
    -                .FirstOrDefault();
    -
    +            
                 // both header and cookie must be there
                 if (cookieToken == null || headerToken == null)
                 {
                     failedReason = "Missing token null";
                     return false;
                 }
     
    -            if (ValidateTokens(cookieToken.Value, headerToken) == false)
    +            if (ValidateTokens(cookieToken, headerToken) == false)
                 {
                     failedReason = "Invalid token";
                     return false;
                 }
    -            
    +
                 return true;
             }
    +
    +        /// <summary>
    +        /// Validates the headers/cookies passed in for the request
    +        /// </summary>
    +        /// <param name="requestHeaders"></param>
    +        /// <param name="failedReason"></param>
    +        /// <returns></returns>
    +        public static bool ValidateHeaders(HttpRequestHeaders requestHeaders, out string failedReason)
    +        {
    +            var cookieToken = requestHeaders
    +                .GetCookies()
    +                .Select(c => c[CsrfValidationCookieName])
    +                .FirstOrDefault();
    +
    +            return ValidateHeaders(
    +                requestHeaders.ToDictionary(x => x.Key, x => x.Value).ToArray(),
    +                cookieToken == null ? null : cookieToken.Value,
    +                out failedReason);
    +        }
         }
     }
    \ No newline at end of file
    
  • src/Umbraco.Web/WebServices/BulkPublishController.cs+1 0 modified
    @@ -15,6 +15,7 @@ namespace Umbraco.Web.WebServices
         /// <summary>
    
         /// A REST controller used for the publish dialog in order to publish bulk items at once
    
         /// </summary>
    
    +    [ValidateMvcAngularAntiForgeryToken]
    
         public class BulkPublishController : UmbracoAuthorizedController
    
         {
    
             /// <summary>
    
    
  • src/Umbraco.Web/WebServices/DomainsApiController.cs+2 0 modified
    @@ -13,13 +13,15 @@
     //using umbraco.cms.businesslogic.language;
    
     using umbraco.BusinessLogic.Actions;
    
     using umbraco.cms.businesslogic.web;
    
    +using Umbraco.Web.WebApi.Filters;
    
     
    
     namespace Umbraco.Web.WebServices
    
     {
    
         /// <summary>
    
         /// A REST controller used for managing domains.
    
         /// </summary>
    
         /// <remarks>Nothing to do with Active Directory.</remarks>
    
    +    [ValidateAngularAntiForgeryToken]
    
         public class DomainsApiController : UmbracoAuthorizedApiController
    
         {
    
             [HttpPost]
    
    
  • src/Umbraco.Web/WebServices/ExamineManagementApiController.cs+2 0 modified
    @@ -13,9 +13,11 @@
     using Umbraco.Core.Logging;
    
     using Umbraco.Web.Search;
    
     using Umbraco.Web.WebApi;
    
    +using Umbraco.Web.WebApi.Filters;
    
     
    
     namespace Umbraco.Web.WebServices
    
     {
    
    +    [ValidateAngularAntiForgeryToken]
    
         public class ExamineManagementApiController : UmbracoAuthorizedApiController
    
         {
    
             /// <summary>
    
    
  • src/Umbraco.Web/WebServices/SaveFileController.cs+1 0 modified
    @@ -26,6 +26,7 @@ namespace Umbraco.Web.WebServices
         /// This isn't fully implemented yet but we should migrate all of the logic in the umbraco.presentation.webservices.codeEditorSave
    
         /// over to this controller.
    
         /// </remarks>
    
    +    [ValidateMvcAngularAntiForgeryToken]
    
         public class SaveFileController : UmbracoAuthorizedController
    
         {
    
             /// <summary>
    
    
  • src/Umbraco.Web/WebServices/XmlDataIntegrityController.cs+2 1 modified
    @@ -4,10 +4,11 @@
     using Umbraco.Core.Models.Rdbms;
     using Umbraco.Core.Persistence;
     using Umbraco.Web.WebApi;
    +using Umbraco.Web.WebApi.Filters;
     
     namespace Umbraco.Web.WebServices
     {
    -
    +    [ValidateAngularAntiForgeryToken]
         public class XmlDataIntegrityController : UmbracoAuthorizedApiController
         {
             [HttpPost]
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.