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 @@
+
+