A potentially dangerous Request

Created: 8 May 2020, last update: 12 Sep 2020

A potentially dangerous Request

Default in Sitecore, a dangerous request is not allowed for your application. In the web.config.

<httpRuntime …   requestValidationMode="4.0" requestValidationType="Sitecore.Web.RequestValidators.SitecoreBackendRequestValidator, Sitecore.Kernel" />

requestValidationMode 4 means all HTTP request are validated and settings on the page or controller are ignored. To allow dangers request to your API controller you can set the requestValidationMode to 4.5 in the web.config and set the ValidateInput to false.

Your Sitecore controller look like this:

using Sitecore.Services.Infrastructure.Web.Http;

[RoutePrefix("api/test"), ValidateInput(false)]
public class MandrillController : ServicesApiController
{
	[ValidateInput(false)]
	[HttpPost]
	public System.Web.Http.IHttpActionResult Webhook([FromBody]SearchRequest searchRequest)
	{ 
		return Ok("Done!");
	}
}

This can prevent errors like this:

ERROR Application error.
Exception: System.Web.HttpRequestValidationException
Message: A potentially dangerous Request.Form value was detected from the client (mandrill_events="{"name":"<jan@mirabeau.nl>", ...").
Source: System.Web
at System.Web.HttpRequest.ValidateString(String value, String collectionKey, RequestValidationSource requestCollection)
at System.Web.HttpRequest.ValidateHttpValueCollection(HttpValueCollection collection, RequestValidationSource requestCollection)
at System.Web.HttpRequest.get_Form()
at Sitecore.Web.RequestEventsHandler.OnPreRequestHandlerExecute(HttpContextBase context)
at Sitecore.Nexus.Web.HttpModule.(Object , EventArgs )
at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStepImpl(IExecutionStep step)
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

 

But there is another way, another option is to leave the requestValidationMode on the very secure 4, and change the requestValidationType="Sitecore.Web.RequestValidators.SitecoreBackendRequestValidator, Sitecore.Kernel"  to your own. You can that with this code:

using Sitecore.Diagnostics;
using Sitecore.Web.RequestValidators;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Util;

namespace Sp.Foundation.Api.RequestValidators
{
    //inherent from the SitecoreBackendRequestValidator because we want to keep the Sitecore own save logic
    public class SpSitecoreBackendRequestValidator : SitecoreBackendRequestValidator
    {
        /// Defines a set of well-known, trusted Sitecore URLs.
        internal static readonly string[] SpTrustedUrls = new string[1]
        {
            "/api/test/webhook"
        };

        public SpSitecoreBackendRequestValidator()
          : this(SpSitecoreBackendRequestValidator.SpTrustedUrls)
        {
        }

        protected SpSitecoreBackendRequestValidator(params string[] urlStartPartsToBypass)
        {
            this.SpUrlStartPartsToBypassValidation = urlStartPartsToBypass;
        }

        protected string[] SpUrlStartPartsToBypassValidation { get; private set; }

        /// 
        /// Check if a validation of the incoming form values should be ignored.
        /// 
        ///The request to be checked.
        /// true if  is trusted, and validation should not take place;false otherwise.
        public virtual bool SpShouldIgnoreValidation(string rawUrl)
        {
            Assert.ArgumentNotNull((object)rawUrl, "request");
            return ((IEnumerable)this.SpUrlStartPartsToBypassValidation).Any((Func<string, bool>)(urlToByPass => rawUrl.StartsWith(urlToByPass, StringComparison.InvariantCultureIgnoreCase)));
        }

        /// Validates a string that contains HTTP request data.
        protected override bool IsValidRequestString(HttpContext context, string value, RequestValidationSource requestValidationSource, string collectionKey, out int validationFailureIndex)
        {
            validationFailureIndex = 0;
            if (this.SpShouldIgnoreValidation(this.ExtractRequestUrl(context)))
                return true;
            //not a save own custom url use the original Sitecore logic
            return base.IsValidRequestString(context, value, requestValidationSource, collectionKey, out validationFailureIndex);
        }
    }
}

And in the web.config you need to change the requestValidationType to your custom validator:

<httpRuntime …   requestValidationMode="4.0" requestValidationType=" Sp.Foundation.Api.RequestValidators.SpSitecoreBackendRequestValidator, Sp.Foundation.Api" />