Serving Static Files From Azure Storage: CORS Rules

There’s an special consideration when serving AJAX requested JavaScript, JSON, HTML and the like from Azure storage: Cross Origin Resource Sharing, or CORS.

Modern Web browsers won’t allow a web site to load a non-image file, via AJAX, that’s on a different domain, unless the resource specifically allows it. For example, if you try to use a jQuery $.ajax() request on a JSON file stored in Azure, out-of-the-box you’ll get an error telling you the resource isn’t available.

This protects web users in a number of ways. In addition to making it more difficult to run cross-site scripting attacks, it also makes it a little more difficult for nefarious coders to hijack your data and scripts.

Although, as we all discover eventually, if you put it on the Internet, you should expect it to be public.

CORS rules in Azure Storage

CORS permissions are assigned to the entire account holding the files for which you want to allow client-request access.

(Technically, CORS rules are assigned to the service endpoint that provides access to blobs in a given account; but for simplicity’s sake, that’s the same thing as the account.)

This, clearly, has security implications: You should not put files you don’t want available via client-side request into an Azure Storage account that has CORS permissions set on it.

It also has practical implications: All blobs in a given account share the same CORS rules, so you can’t be granular about the HTTP verbs, request origin sites or cache expiration applied to each blob or container. At least, not without using some sort of proxy, which is a whole different hornet’s nest.

CORS rules are delivered as HTTP header fields. Generally speaking, we need to specify values for at least these header fields to grant access:

  1. AllowedOrigins: This is a List of domains we’re going to allow to access our content. For example, if our site is at www.example.com, and we only want visitors to our site to access JSON files we have in Azure blob storage, we’d set the AllowedOrigins to www.example.com.
  2. AllowedMethods: Determines the HTTP verbs that can access the resource. We at least need to allow GET. If we’re going to allow verbs other than GET, POST and HEAD, we also need to allow OPTIONS, so that the requesting agent can examine the headers for our file and make sure it’s accessible via an AJAX call using that verb.
  3. MaxAgeInSeconds: This sets a cache value for the file, and setting it to the maximum reasonable value can save you a few dollars. More on this shortly.

We can wildcard AllowedOrigins and AllowedMethods with an asterisk. If we wildcard AllowedOrigins, any domain can access our file. If we wildcard AllowedMethods, any verb can be used.

Setting permissions via code

The code to set permissions is pretty straightforward (hat tip to Bill Wilder, whose blog post informed this code).

In this example, I am going to allow GET and POST requests from www.mysite.com (or mysite.com), and I’m going to cache results for one hour:

using Microsoft.WindowsAzure.Storage.Auth;
using Microsoft.WindowsAzure.Storage.Blob;
using Microsoft.WindowsAzure.Storage.Shared.Protocol;

public void SetCorsRules() {
	var rule = new CorsRule 
	{
		AllowedMethods = CorsHttpMethods.Get | CorsHttpMethods.Post,
		AllowedOrigins = new List<string> 
		{
			"www.mysite.com",
			"mysite.com"
		},
		MaxAgeInSeconds = 3600
	};
	
	var account = CloudStorageAccount.Parse("YourStorageConnectionString");
	var client = account.CreateCloudBlobClient();
	
	var properties = client.GetServiceProperties();
	properties.Cors.CorsRules.Clear();
	properties.Cors.CorsRules.Add(rule);
	
	client.SetServiceProperties(properties);
}

This code as a GitHub Gist: https://gist.github.com/dougvdotcom/b3d3a6ccfe35b8befd6884d2213dac00

Setting CORS rules with a GUI client

At this writing, it’s not possible to change CORS rules on an account using CloudBerry Explorer (including Pro), Microsoft Azure Storage Explorer or Storage Explorer 6.

However, commands to set CORS rules are part of the Azure REST API, Powershell and CLI.

CDNs are the better solution

Serving blobs directly from Azure Storage is very efficient, especially if you don’t have a lot of requests and the requests you see tend to be in the same neighborhood of your datacenter.

But now that Microsoft has fixed its content delivery network offerings by partnering with Akamai and EdgeCast, it makes a lot more sense to serve Azure Storage files from there, when possible. That’s the subject for next time.

Featured photo by keijj44 via Pixabay. in the public domain.
Featured photo by keijj44 via Pixabay. in the public domain.

Leave a Reply

  • Check out the Commenting Guidelines before commenting, please!
  • Want to share code? Please put it into a GitHub Gist, CodePen or pastebin and link to that in your comment.
  • Just have a line or two of markup? Wrap them in an appropriate SyntaxHighlighter Evolved shortcode for your programming language, please!