About AllowUnsafeUpdates…

Posted: January 4, 2016 in SharePoint

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 read below 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 the second part of this article.

The Microsoft idea behind introducing the AllowUnsafeUpdates property is to protect YOU from cross-site scripting attacks. The way this works is that if your application is running in an HTTPContext (i.e. it’s a web part for instance) and the request is a GET request then SharePoint will refuse to do any changes unless the value of AllowUnsafeUpdates is set to true and by default it will be false for GET requests. If you try to do any updates to lists, webs or any SharePoint objects that require an SPSite to be created first, and if you don’t set AllowUnsafeUpdates to true you will get this exception:

System.Exception: Microsoft.SharePoint.SPException: The security validation for this page is invalid. Click Back in your Web browser, refresh the page, and try your operation again. —> System.Runtime.InteropServices.COMException (0x8102006D): The security validation for this page is invalid. Click Back in your Web browser, refresh the page, and try your operation again.

It is important to understand that if you are writing a class library for example, your code will behave differently when called from a web application and when called from a rich client. Actually if the HTTPContext.Current is null then AllowSafeUpdates will be always true. This is the case in rich clients where no cross-scripting is possible as there are simply no web requests.

Usually when you create your own SPSite or SPWeb objects, i.e. when you are not getting them from the SPContext (such as SPContext.Web), and when you try to update anything such as web or list properties, list items metadata etc, you may get the exception listed above. This is a clear indication that AllowUnsafeUpdates of the SPWeb is false and this is preventing you from doing the update. This problem is resolved easily by setting the AllowUnsafeUpdates of the parent web object to true. Still sometimes even after you have done this you may still be getting the same error. This is typically caused by one of the the following reasons:

A) You have set the AllowUnsafeUpdate to true for the wrong SPWeb

You have to be careful because sometimes the ParentWeb of an object is not the same instance of the web you have retrieved the object from. For example when you go initialWeb.Lists[listId] you would expect that the returned list’s ParentWeb instance is the same as you initialWeb. However this is not the case. So if somewhere later in your code you go list.ParentWeb.UpdateSomething() this will not work because you have never set the AllowUnsafeUpdates property of list.ParentWeb. You have set it for your initialWeb but even that this is the same web as the list’s parent web both are different instances. Usually you see the error and then you go and investigate in Reflector whether this is the same instance or not.

Alternatively you could use another more generic and clever way to deal with almost any similar situation described in the following post:

http://community.bamboosolutions.com/blogs/bambooteamblog/archive/2008/05/15/when-allowunsafeupdates-doesn-t-work.aspx

The author suggests that you can set the HttpContext.Current to null before you do your updates and then reassign its initial preserved value when done. This will work great but remember to set the HTTPContext to null as early as possible. In the post above probably SharePoint uses the site.RootWeb to do the updates to the site scoped features and the RootWeb’s AllowUnsafeUpdates hasn’t been set to true explicitly.

  1. B) The AllowUnsafeUpdates gets reset to false sometimes after you have set it to true

If we have a look at how the property is managed it turns out that it is stored in the request object associated with every SPWeb (which is actually a COM object)

[SharePointPermission(SecurityAction.Demand, UnsafeSaveOnGet = true)]
private void SetAllowUnsafeUpdates(bool allowUnsafeUpdates)
{
this.Request.SetIgnoreCanary(allowUnsafeUpdates);
}

This actually means that every time the request is reset, the property will be also reset to its default value. The m_Request member is modified when a new web is created, when the web is disposed or when the SPWeb.Invalidate() method is called.

internal void Invalidate()
{
if (this.m_Request != null)
{
if (this.m_RequestOwnedByThisWeb)
{
SPRequestManager.Release(this.m_Request);
}
this.m_Request = null;
}
this.m_bInited = false;
this.m_bPublicPropertiesInited = false;
this.m_Url = null;
}

So any operation that calls SPWeb.Invalidate() will reset AllowUnsafeUpdate to its default value. And for code running under HTTPContext, i.e. web applications, this default value for a GET request will be false. I’ve looked up for you all legitimate cases for which Invalidate() is being called by the SharePoint object model. These cases are:

1) When the Name or the ServerRelativeUrl properties of the SPWeb are changed and then Update() is called. In this case the AllowUnsafeUpdate is reset because with the change of these properties the URL of the web will change and logically the request object will change as it will now point to a different URL.

2) When any object that implements ISecurable (those are SPWeb, SPList and SPListItem) breaks or reverts their role definition inheritance. This means every time you callSPRoleDefinitionCollection.BreakInheritance(), BreakRoleInheritance(), ResetRoleInheritance() or set the value of HasUniquePerm the AllowUnsafeUpdatesproperty of the parent web will reset to its default value and you may need to set it back to true in order to do further updates to the same objects.

3) In many cases when an exception is caught by the SharePoint object model when you try to retrieve any sort of data the AllowUnsafeUpdates of the parent web will be reset to false as a precaution to protect against potential exploits. In those cases however the objects will be in unknown state anyway after the request has been reset and the exception is re-thrown so they are of no practical interest.

And finally it is also good to mention that you may get another related exception when trying to update your SharePoint objects and that is:

System.Exception: Microsoft.SharePoint.SPException: Cannot complete this action.Please try again. —> System.Runtime.InteropServices.COMException (0x80004005): Cannot complete this action.

This usually happens when some updates have been made to an object (usually SPSite, SPWeb or SPList) that may be clashing with your changes and SharePoint refuses to do the update. To recover from this situation you simply need to create fresh copies of the SPSite and the SPWeb objects and do the updates on the objects retrieved from the fresh copies. And of course don’t forget to set the AllowUnsafeUpdates to true for the freshly created SPWeb if required.

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 id=”__REQUESTDIGEST” name=”__REQUESTDIGEST” type=”hidden” value=”0x5DC31993EF285644A7C48FBFA2E6FB719CD7E9DB0922A329E97,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 theForm 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: TheGetUpdatedFormDigest 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 FormDigestcontrol 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 Digesthas 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.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s