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.
| Package | Affected versions | Patched versions |
|---|---|---|
Umbraco.CMSNuGet | < 7.4.0 | 7.4.0 |
Patches
118c3345e4766Fixes U4-7459 XSRF protection bypass - ensures tokens are checked for the non-editor api controllers
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- github.com/umbraco/Umbraco-CMS/commit/18c3345e47663a358a042652e697b988d6a380ebnvdPatchWEB
- github.com/advisories/GHSA-5f6p-4hxq-rjxmghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2015-8814ghsaADVISORY
- issues.umbraco.org/issue/U4-7459nvdIssue Tracking
- www.openwall.com/lists/oss-security/2016/02/16/10nvdMailing ListWEB
- web.archive.org/web/20230608152113/https://issues.umbraco.org/issue/U4-7459ghsaWEB
News mentions
0No linked articles in our index yet.