SharePoint Internals – Hristo Pavlov’s Blog

21 May, 2008

What you need to know about AllowUnsafeUpdates (Part 2)

In short here is how to deal with AllowUnsafeUpdates:

1) Don’t update SharePoint objects from your code behind on GET requests as if you do so your code will be exploitable via a cross-site scripting. If you understand the consequences of doing this and still want to do it then see the first part of this article about how to use the AllowUnsafeUpdates property.

2) If your code is processing a POST request then make sure you call SPUtility.ValidateFormDigest() before you do anything else. This will ensure that the post request is validated (that it is not a cross-site scripting attack) and after that you will not have to worry about AllowUnsafeUpdates, because its default value will be “true” after the form digest is validated. To find out more about this read below.

So you have probably noticed from part one of the article that the internal method that sets the AllowUnsafeUpdates property had quite an interesting name: SetIgnoreCanary(). Canary is something that refers to a method of protecting from stack overflow attacks. The terminology is a reference to the historic practice of using canaries in coal mines, since they would be affected by toxic gases earlier than the miners, thus providing a biological warning system. In SharePoint the request canary is a unique pseudo-random value that protects you from cross-site scripting attacks. If you have ever examined the HTML source of your SharePoint pages you have probably noticed the __REQUESTDIGEST  hidden field.  This is what is referred to as the canary or the Form Digest and is used to verify that the request is genuine.

<input

    type=”hidden”

    name=”__REQUESTDIGEST”

    id=”__REQUESTDIGEST”

    value=”0x5DC31993EF285644A7C48F………..BFA2E6FB719CD7E9DB0922A329E97,19 May 2008 23:37:22 -0000″ />

As you see this is nothing more than a hidden field set by the server and verified back by the server when the page is submitted. As documented by Microsoft: The purpose of form digest validation is to help prevent security attacks where a user is tricked into posting data unknowingly to a server.

The place where the Form Digest value is set is the WebPartPage.FormOnLoad() method:

private void FormOnLoad(object sender, EventArgs e)

{

    if (HttpContext.Current != null)

    {

        SPWebcontextWeb = SPControl.GetContextWeb(HttpContext.Current);

        if(contextWeb != null)

        {

            SPWebPartManager.RegisterOWSScript(this, contextWeb);

            if (this.Page.Items[“FormDigestRegistered”] == null)

            {

                stringbstrUrl = SPGlobal.GetVTIRequestUrl(this.Context.Request, null).ToString();

                SPStringCallback pFormCallback = new SPStringCallback();

                contextWeb.Request.RenderFormDigest(bstrUrl, pFormCallback);

                base.ClientScript.RegisterHiddenField(“__REQUESTDIGEST”, SPHttpUtility.NoEncode(pFormCallback.StringResult));

                FormDigest.RegisterDigestUpdateClientScriptBlockIfNeeded(this);

                this.Page.Items[“FormDigestRegistered”] = true;

            }

        }

    }

}

 

The actual value of the __REQUESTDIGEST  field is generated by the COM objects in the OWSSVR.dll. After that another method is called: FormDigest.RegisterDigestUpdateClientScriptBlockIfNeeded()

public static void RegisterDigestUpdateClientScriptBlockIfNeeded(Page page)

{

    doubletotalMilliseconds;

 

    if (SPContext.Current.Site.WebApplication.FormDigestSettings.Enabled)

    {

        totalMilliseconds = SPContext.Current.Site.WebApplication.FormDigestSettings.Timeout.TotalMilliseconds;

        if(totalMilliseconds > 2147483647.0)

        {

            totalMilliseconds = 2147483647.0;

        }

    }

    else

    {

        return;

    }

 

    int num2 = Convert.ToInt32((double)(totalMilliseconds * 0.8));

    if(!page.ClientScript.IsOnSubmitStatementRegistered(typeof(FormDigest), “SPFormDigestUpdaterSubmitHandler”))

    {

      page.ClientScript.RegisterOnSubmitStatement(

            typeof(FormDigest),

            “SPFormDigestUpdaterSubmitHandler”,

            “UpdateFormDigest(‘” + SPEncode.ScriptEncode(SPContext.Current.Web.ServerRelativeUrl) + “‘, “+ num2.ToString(CultureInfo.InvariantCulture) + “);”);

 

        ScriptLink.Register(page, “init.js”, true, false);

    }

} 

There are a couple of interesting pieces of information in the code above. Firstly the Form Digest value generated by the server can expire. By default this happens in 30 min. Secondly the SPWebApplication.FormDigestSettings property can be used to change the form digest settings per web application. Those settings are persisted in the configuration database if you call SPWebApplication.Update(). The information provided in MSDN for the “Enabled” property is however not completely correct. MSDN says that: Enabled gets or sets a value that determines whether security validation is included with all form pages. But my SharePoint code examination and code tests showed that the Form Digest will be always included regardless of the Enabled value. The value of “false” means that when the digest expires (by default in 30 min) the user will not be able to submit the form and will get a security validation timeout exception trying to do so. Further test however showed that setting Enabled to false will indeed disable the security validation and you will not be getting the “The security validation for this page is invalid. Click Back in your Web browser, refresh the page, and try your operation again.” exception even that AllowUnsafeUpdates will have a value of false.

Looking into the code of FormDigest.RegisterDigestUpdateClientScriptBlockIfNeeded() we see that it registers a client script that calls the UpdateFormDigest()  JavaScript function when the form is submitted. This JavaScript function calls the GetUpdatedFormDigest() method of the _vti_bin/sites.asmx WebService and updates the form digest field value on the fly before the form is submitted back to the server. According to the preliminary documentation of the Sites Web Service Protocol released by Microsoft on 4 Apr 2008: The GetUpdatedFormDigest is used to request renewal of an expired security validation, also known as a message digest. The purpose of form digest validation is to help prevent security attacks where a user is tricked into posting data unknowingly to a server.  To generate the new digest the web service simply creates a new instance of the FormDigest control and returns its DigestValue property value. So if the function is not called and the Form Digest is not updated a security timeout exception will occur and users will have to refresh a page before they can submitt it.

So the next question is where is the Form Digest validated? This is actually done in the SPWeb.ValidateFormDigest() method:

public bool ValidateFormDigest()

{

    HttpContext current = HttpContext.Current;

 

    if (current != null)

    {

        if (HttpContext.Current.Items[“FormDigestValidated”] == null)

        {

            if (!this.Request.ValidateFormDigest(this.Url, null))

            {

                return false;

            }

 

            current.Items[“FormDigestValidated”] = true;

 

            return true;

        }

 

        return true;

    }

 

    return true;

}

And as the code above shows the validation is done only once per web request and is then cached in the HTTPContext. Furthermore when a new SPWeb or SPSite object is created they also create an internal SPRequest object from the HTTPRequest. And within the lifetime of a single HTTP request to your web part or a web page, if the Form Digest has been successfully validated once then the AllowUnsafeUpdates property will now have a default value of true for all freshly created objects and the updates will be considered safe by SharePoint which also means you don’t have to set AllowUnsafeUpdates to do your job.

For some reason sometimes SharePoint doesn’t always call the ValidateFormDigest() method and this is why a workaround with setting AllowUnsafeUpdates to true is used. But a much better and safer solution is if you call this method yourself. The best way to do it is to call the SPUtility.ValidateFormDigest() method somewhere at the beginning of your POST request code behind.

And finally if you use in SharePoint you custom built ASPX pages which don’t inherit from a WebPartPage you can insert a FormDigest control in them which will insert the Form Digest  for you automatically and will protect you from cross-site scripting. Just make sure to call ValidateFormDigest() and DON’T touch the AllowUnsafeUpdates.

I hope that my investigation was more useful than confusing to you. Knowing more about how things work is always a key to building better and more secure applications.

Happy coding.

 

16 Comments »

  1. […] 2) If your code is processing a POST request then make sure you call SPUtility.ValidateFormDigest() before you do anything else. This will ensure that the post request is validated (that it is not a cross-site scripting attack) and after that you will not have to worry about AllowUnsafeUpdates, because its default value will be “true” after the form digest is validated. To find out more about this read the second part of this article. […]

    Pingback by What you need to know about AllowUnsafeUpdates (Part 1) « SharePoint Internals - Hristo Pavlov’s Blog — 21 May, 2008 @ 1:22 am

  2. Great Info!

    /Jonas

    Comment by Jonas — 21 May, 2008 @ 12:59 pm

  3. Inspite of doing all these, I’m getting the same error time and again.Plz help

    Comment by meera — 29 May, 2008 @ 10:04 am

  4. […] function that I talked about in my What you need to know about AllowUnsafeUpdates (Part 2) post is: public static bool […]

    Pingback by Don’t Forget SPUtility « SharePoint Internals - Hristo Pavlov’s Blog — 30 May, 2008 @ 5:17 am

  5. Comment by meera:

    > Inspite of doing all these, I’m getting the same error time and
    > again.Plz help

    It turned out that meera was building a regular ASP.NET application not running inside SharePoint, which was using the SharePoint object model. Well in this case you need to manually put a FormDigest control to your pages to be able to use SPUtility.ValidateFormDigest()

    Comment by hristopavlov — 2 June, 2008 @ 12:43 am

  6. […] What you need to know about AllowUnsafeUpdates (Part 1) What you need to know about AllowUnsafeUpdates (Part 2) […]

    Pingback by Points to Share from Venkat Varkala » Code Acceptance checklist for Custom MOSS Solutions — 19 August, 2008 @ 4:45 pm

  7. Excellent info about AllowUnsafeUpdates !!

    Comment by decatec — 14 September, 2008 @ 10:53 pm

  8. Thank you so much for this information. It took me some hours to figure out how to by pass the “Security Validation” exception using the AllowUnsafeUpdates property. I was not quite happy with this solution though, so I used a few more hours searching for more information. Fortunately I came by your post. I am sure you just saved me ten hours of work. I really appriciate that you made your observations available to your visitors.

    In my situation I had an .aspx page embedding a SharePoint WebPart. I got the “Security Validation” exception because the ASPX page didn’t define the FormDigest element. The solution was – as you mentioned – to create an instance of the Microsoft.SharePoint.WebControls.FormDigest WebControl and simply output it using the Render method.

    Comment by Jimmy Thomsen — 10 December, 2008 @ 2:12 pm

  9. Well done! Thanks for sharing your investigation with us.

    Comment by Mike Sharp — 18 December, 2008 @ 3:45 am

  10. […] What you need to know about AllowUnsafeUpdates (Part 2) Published Saturday, March 21, 2009 5:01 PM by liuhuimiao […]

    Pingback by Huimiao's Blog - .NET and SharePoint : SharePoint ??-1 — 21 March, 2009 @ 8:01 am

  11. Thank you so much for your thorough explanation! Interestingly, calling SPUtility.ValidateFormDigest() even works in cases where setting AllowUnsafeUpdates to true does not: When calling the Update() method of a SPFeaturePropertyCollection, I kept getting a “The security validation for this page is invalid” error until I found your blog and tried the ValidateFormDigest() method.

    I’d like to add that it is actually possible to get rid of all “security validation” errors once and for all by simply turning off security validation, an option which can be set at the Central Administration. However, this might not be too wise since it could, in theory, render ALL SharePoint objects vulnerable to certain attacks.

    Comment by Andy — 19 February, 2010 @ 2:12 pm

  12. […] What You Need To Know About AllowUnsafeUpdates part 2 […]

    Pingback by Is AllowUnsafeUpdates=true the right way? « — 24 February, 2010 @ 8:46 am

  13. Wow! great post. Lots of useful information is given here.

    Comment by pranav kulkarni — 5 May, 2010 @ 8:40 am

  14. […] most complete explanation of the underlying security mechanism This entry was posted in Uncategorized and tagged SharePoint 2010. Bookmark the permalink. […]

    Pingback by RunWithElevatedPrivileges() and Error “The security validation for this page is invalid …” | Dieter Bremes's Blog — 21 July, 2010 @ 8:12 pm

  15. Can someone please explain what constitutes a “GET requests” and Post request in SharePoint? Virtually all of my code that updates SharePoint objects occure from a web part in response to a button being pressed. Would that be a post request?

    Comment by David Moss — 2 November, 2010 @ 10:34 am


RSS feed for comments on this post. TrackBack URI

Leave a reply to decatec Cancel reply

Blog at WordPress.com.