diff --git a/src/Umbraco.Web/Controllers/UmbLoginController.cs b/src/Umbraco.Web/Controllers/UmbLoginController.cs index 2980b4a4c0..2ff80e2668 100644 --- a/src/Umbraco.Web/Controllers/UmbLoginController.cs +++ b/src/Umbraco.Web/Controllers/UmbLoginController.cs @@ -22,6 +22,7 @@ namespace Umbraco.Web.Controllers [HttpPost] [ValidateAntiForgeryToken] + [ValidateUmbracoFormRouteString] public ActionResult HandleLogin([Bind(Prefix = "loginModel")]LoginModel model) { if (ModelState.IsValid == false) diff --git a/src/Umbraco.Web/Controllers/UmbLoginStatusController.cs b/src/Umbraco.Web/Controllers/UmbLoginStatusController.cs index fdc2de8c5f..8f572404fc 100644 --- a/src/Umbraco.Web/Controllers/UmbLoginStatusController.cs +++ b/src/Umbraco.Web/Controllers/UmbLoginStatusController.cs @@ -24,6 +24,7 @@ namespace Umbraco.Web.Controllers [HttpPost] [ValidateAntiForgeryToken] + [ValidateUmbracoFormRouteString] public ActionResult HandleLogout([Bind(Prefix = "logoutModel")]PostRedirectModel model) { if (ModelState.IsValid == false) diff --git a/src/Umbraco.Web/Controllers/UmbProfileController.cs b/src/Umbraco.Web/Controllers/UmbProfileController.cs index b14652ad2e..d9333e8e65 100644 --- a/src/Umbraco.Web/Controllers/UmbProfileController.cs +++ b/src/Umbraco.Web/Controllers/UmbProfileController.cs @@ -23,6 +23,7 @@ namespace Umbraco.Web.Controllers [HttpPost] [ValidateAntiForgeryToken] + [ValidateUmbracoFormRouteString] public ActionResult HandleUpdateProfile([Bind(Prefix = "profileModel")] ProfileModel model) { var provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider(); diff --git a/src/Umbraco.Web/Controllers/UmbRegisterController.cs b/src/Umbraco.Web/Controllers/UmbRegisterController.cs index b0187d6127..4f4173a67d 100644 --- a/src/Umbraco.Web/Controllers/UmbRegisterController.cs +++ b/src/Umbraco.Web/Controllers/UmbRegisterController.cs @@ -24,6 +24,7 @@ namespace Umbraco.Web.Controllers [HttpPost] [ValidateAntiForgeryToken] + [ValidateUmbracoFormRouteString] public ActionResult HandleRegisterMember([Bind(Prefix = "registerModel")]RegisterModel model) { if (ModelState.IsValid == false) diff --git a/src/Umbraco.Web/Mvc/HttpUmbracoFormRouteStringException.cs b/src/Umbraco.Web/Mvc/HttpUmbracoFormRouteStringException.cs new file mode 100644 index 0000000000..d4734e5d24 --- /dev/null +++ b/src/Umbraco.Web/Mvc/HttpUmbracoFormRouteStringException.cs @@ -0,0 +1,23 @@ +using System; +using System.Net; +using System.Web; + +namespace Umbraco.Web.Mvc +{ + /// + /// Exception that occurs when an Umbraco form route string is invalid + /// + /// + [Serializable] + public sealed class HttpUmbracoFormRouteStringException : HttpException + { + /// + /// Initializes a new instance of the class. + /// + /// The error message displayed to the client when the exception is thrown. + public HttpUmbracoFormRouteStringException(string message) + : base(message) + { } + + } +} diff --git a/src/Umbraco.Web/Mvc/ValidateUmbracoFormRouteStringAttribute.cs b/src/Umbraco.Web/Mvc/ValidateUmbracoFormRouteStringAttribute.cs new file mode 100644 index 0000000000..a57ce7e7f7 --- /dev/null +++ b/src/Umbraco.Web/Mvc/ValidateUmbracoFormRouteStringAttribute.cs @@ -0,0 +1,52 @@ +using System; +using System.Net; +using System.Web.Mvc; +using Umbraco.Core; + +namespace Umbraco.Web.Mvc +{ + /// + /// Represents an attribute that is used to prevent an invalid Umbraco form request route string on a request. + /// + /// + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] + public sealed class ValidateUmbracoFormRouteStringAttribute : FilterAttribute, IAuthorizationFilter + { + /// + /// Called when authorization is required. + /// + /// The filter context. + /// filterContext + /// The required request field \"ufprt\" is not present. + /// or + /// The Umbraco form request route string could not be decrypted. + /// or + /// The provided Umbraco form request route string was meant for a different controller and action. + public void OnAuthorization(AuthorizationContext filterContext) + { + if (filterContext == null) + { + throw new ArgumentNullException(nameof(filterContext)); + } + + var ufprt = filterContext.HttpContext.Request["ufprt"]; + if (ufprt.IsNullOrWhiteSpace()) + { + throw new HttpUmbracoFormRouteStringException("The required Umbraco request data is invalid."); + } + + if (!UmbracoHelper.DecryptAndValidateEncryptedRouteString(ufprt, out var additionalDataParts)) + { + throw new HttpUmbracoFormRouteStringException("The required Umbraco request data is invalid."); + } + + if (additionalDataParts[RenderRouteHandler.ReservedAdditionalKeys.Controller] != filterContext.ActionDescriptor.ControllerDescriptor.ControllerName || + additionalDataParts[RenderRouteHandler.ReservedAdditionalKeys.Action] != filterContext.ActionDescriptor.ActionName || + additionalDataParts[RenderRouteHandler.ReservedAdditionalKeys.Area].NullOrWhiteSpaceAsNull() != filterContext.RouteData.DataTokens["area"]?.ToString().NullOrWhiteSpaceAsNull()) + { + throw new HttpUmbracoFormRouteStringException("The required Umbraco request data is invalid."); + } + } + } +} diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 105a40b4a7..39a05ddbc4 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -219,8 +219,10 @@ + +