 |
By Martin Streicher
Amazon Simple Storage Service (Amazon S3) is expansive, capable, affordable, and simple to integrate into any application. Here, learn how Flash and Silverlight applications can connect to Amazon S3.
Use the Flash and Silverlight URL Policy File to Grant Access to Data
Stored in Your Amazon S3 Bucket
To bolster availability, hasten transfer, and leverage an economy of scale,
more and more web developers serve valuable application assets
(images, movies, and web pages, among other forms of media and data)
from Amazon S3.
Amazon S3
provides (seemingly) limitless storage capacity and ample network presence
and bandwidth to quickly deliver content to any computer on the globe.
Amazon S3 is also very affordable. To paraphrase the
Amazon S3 Design
Requirements, Amazon S3 eliminates the hassle, cost, limitations, and
risk of self-hosted storage.
Better yet, Amazon S3 is a snap to integrate. Each developer controls his or her
own collection of files—or, in Amazon S3 parlance, his or her own
bucket—and a set of associated, fine-grained access controls. The contents of
a bucket can be accessed and modified programatically through a Simple
Object Access Protocol (SOAP) or Representational State Transfer (REST)
interface, and Hypertext Transfer Protocol (HTTP) is the default download
protocol, providing each datum with a familiar URL. For example, a publicly
available datum stored in Amazon S3 is referred to by a URL in the form
http://pail.s3.amazonaws.com/filename, where
pail is the unique name of the developer's bucket and
filename is the datum.
In general, access to a bucket is sufficient for any server-based application
(written in Perl, Python, Ruby, or Java, say) to store and retrieve any
asset from Amazon S3:
- Public access. To make an asset publicly
available, upload it to a bucket, grant open access through
the bucket's access control list (ACL), and reference its Amazon S3
URL.
- Privileged access. To protect an asset, use an
Amazon S3 ACL to limit entrée to a specific set of
credentialed individuals and groups. Privileged access is
typically realized programmatically.
- Secure access. The Amazon S3 service is
protected by a "wildcard" Secure Sockets Layer (SSL) certificate.
Simply use HTTP over SSL (HTTPS) to secure each transmission.
Client applications—software that runs within the web browser, such as
Flash and Silverlight—can also use Amazon S3, albeit with some additional
configuration.
Thinking Outside the Sandbox
Code downloaded from the Internet may be faulty, capricious, or downright
malicious. Thus, the browser and other client-side engines must be especially
circumspect. To protect the user, a browser-based client application typically
runs in a sandbox, or something of a virtual (and hopefully) impenetrable
vault that separates client code from the underlying operating system, file
system, network resources, and other sandboxes. That said, a client is not
necessarily isolated; it can access resources using the sandbox as proxy, usually
with strict rules. Thus, data the sandbox reads and potentially writes must be equally
guarded.
For example, by default, a Flash application (or SWF file) may only read an asset
served from the exact same host that serves the Flash application itself.
According to this fiat, if the SWF file was downloaded from www.example.com, it
can access other files on www.example.com but no files from www1.example.com
or any other sub-domain in example.com.
However, because sharing files among many hosts in one domain and across domains
is common, desirable, and practical on the modern Internet—the very
essence of Amazon S3—an exception to the rule can be made if you so elect.
If you want to place your Flash application in your Amazon S3 bucket, you have two
options:
- Lump the SWF file and all its associated assets into the same bucket.
- Use a URL policy file to grant inter-system and cross-domain access.
The former solution is terribly simple to realize: Deposit the application and all its
assets in your Amazon S3 bucket. However, if you do not want to serve your
application directly from your bucket, if you want to share your assets with other
applications, or if you need to access data from another developer's bucket or
server, you must grant or be granted express permission to access data from
across disparate domains.
As a representative example, by default and convention, a Flash application served directly
from the server www.example.com cannot access an Extensible Markup Language (XML)
file from the site's Amazon S3 bucket. Further, an SWF file cannot modify the
pixels of an image stored in a bucket unless given express entitlement, according
to the Flash
9 documentation.
Flash and Silverlight applications are given express permission with the URL policy
file. Let's see how it works.
URL Policy Files
If you use Flash or Silverlight, the URL policy file (or cross-domain policy file
when placed in an Amazon S3 bucket) grants license to read data from the
bucket. Each entry in a cross-domain policy file specifies a set of one or more
domains and the corresponding privileges for the set. You can place a
cross-domain policy file at the root of your bucket and within each sub-folder
to fine-tune access, unless the policy file at the root (the master policy file)
forbids such configuration.
Here is a sample master policy file for use with Flash. The file must be named
crossdomain.xml. (Silverlight has its own master policy
file—clientaccesspolicy.xml—but also honors Flash's crossdomain.xml
file if its own policy file is not provided.)
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE cross-domain-policy SYSTEM
"http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
<site-control permitted-cross-domain-policies="master-only" />
<allow-access-from domain="*.example.com" />
<allow-http-request-headers-from domain="*.example.com"
headers="SOAPAction" />
</cross-domain-policy>
Briefly, this policy file dictates that non-master (non-root) policy files are disallowed
(site-control permitted-cross-domain-policies="master-only") and
strictly limits SOAP requests (allow-http-request-headers-from="SOAPAction,Content-Type")
that originate from hosts in the domain example.com
(domain="*.example.com"). In general, an SWF file cannot
send a header to a remote host without such an explicit grant.
Let's create a small Flash application and a similar Silverlight application to
demonstrate the use of a URL policy file. To follow the examples, you must have:
- An Amazon S3 bucket. If you don't have an
Amazon Web Services&0153; (AWS) account yet,
sign-up is quick and
easy.
- Access to your web server. To deploy your Flash
or Silverlight application, data, and one or more cross-domain
policy files, you must have File Transfer Protocol (FTP), Secure
FPT (SFTP), or Secure Shell (SSH) access to your web server.
- A Flash and/or Silverlight software development kit (SDK).
You can download Flash authoring software from the
Adobe Flash
Developer Center; Silverlight tools are available from the
Silverlight home page.
You must already have Microsoft
Visual Studio 2008 with Service Pack 1 to install the Silverlight
tools.
- The Debug Flash Player. The Debug Flash Player logs
each access of a URL policy file and helps debug implementations
and rules. The Adobe Flash Builder integrated development environment
(IDE) installs the Debug Flash Player, and other Flash software may install
it, too. As an alternative, you can download the Debug Flash Player
separately from the Flash
Player Downloads page.
You may also find it helpful to have an HTTP protocol "sniffer," or monitor, such as the
Mozilla Firefox plug-in Live HTTP Headers.
An HTTP protocol sniffer can reveal headers and traffic and make it easier to debug requests.
Connect to Amazon S3 in a Flash
As mentioned earlier, an SWF file cannot read data from a "remote" host without
explicit consent. (You can find the rationale for this mandate and a complete
description of Flash security measures in the white paper,
"Adobe
Flash Player 9 Security.")
Let's create a simple Flash application to download and display portions of an
inventory list. Use a URL policy file to grant cross-domain access:
- Ensure that the Debug Flash Player (the most recent version as of this
writing is 9.0.124.0) is installed on your client machine and within
the browser of your choice.
- Enable policy file logging:
- Go to your home directory and create the file mm.cfg
with the following two lines:
PolicyFileLog=1 # Enables policy file logging
PolicyFileLogAppend=1 # Optional; do not clear log at startup
(The exact location of your home directory depends on
your platform. In Linux and Mac OS X, it's
/home/name or /Users/name.
In Windows Vista, it's C:\Users\name, and on
older versions of Windows, it's C:\Documents and Settings\name.
Your login name is name. Refer to the section,
"Workflows,"
in "Policy
file changes in Flash Player 9 and Flash Player 10" for all
the details.)
- Create a directory to contain the log file. The directory's location
and name again depend on your platform. Use this guide, and
be mindful of the use of upper- and lowercase letters:
- Windows: C:\Documents and Settings\username\Application Data\Macromedia\Flash Player\Logs
- Windows Vista: C:\Users\username\AppData\Roaming\Macromedia\Flash Player\Logs
- Macintosh: /Users/username/Library/Preferences/Macromedia/Flash Player/Logs
- Linux: /home/username/.macromedia/Flash_Player/Logs
In Linux, for instance, you would type:
mkdir -p /home/username/.macromedia/Flash_Player/Logs
- Run any SWF in Debug Flash Player.
Each time an SWF file runs, it should emit one or more messages
to the file policyfiles.txt. At least one line should be
written each time you open an SWF file. It should resemble
this:
OK: Root-level SWF loaded:
http://www.example.com/bin-release/Example.swf
If you see such a line, logging is enabled and functioning properly.
(You can find an index of possible messages in the section,
"Log
Message Reference," in "Policy file changes in Flash Player 9
and Flash Player 10.")
- Create the application.
The following MXML code is minimal yet sufficient for the purposes of this
demonstration. (This code is derived from an example found in
Marco Casario's book,
Flex Solutions:
Essential Techniques for Flex 2 and 3 Developers, and is used with permission.)
<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"
creationComplete="start( event )">
<mx:List id="list"/>
<mx:Button label="Lookup" click="loader.load( request );"/>
<mx:Script>
<![CDATA[
private var loader:URLLoader;
private var request:URLRequest =
new URLRequest( "http://example-media.s3.amazonaws.com/store.xml" );
[Bindable]
private var result:XMLList;
private function start( event:Event ):void {
XML.ignoreWhitespace = true;
loader = new URLLoader( );
loader.addEventListener( Event.COMPLETE, finish );
}
private function finish( event:Event ):void {
result = new XMLList( loader.data );
list.dataProvider = result.item.name;
}
]]>
</mx:Script>
</mx:Application>
When you click Lookup, the code loads a small XML file from
a server and displays the names of all items found. When you
create the application, be sure to change the URL in the
URLRequest() function to point to your Amazon S3
bucket.
- Create a data file named store.xml with the following data:
<store>
<item>
<id>1</id>
<name>Hot Wheels</name>
</item>
<item>
<id>3</id>
<name>Mini-Mates</name>
</item>
</store>
- Copy store.xml to the root of your Amazon bucket, then edit its ACL to
allow global Read access.
(If you use the Firefox browser, the
Amazon
S3 Firefox Organizer add-on is a convenient, helpful tool
for managing the files in your bucket and your files' ACLs.)
- Compile your MXML application, export the release build, copy the release
build to the root of your Amazon S3 bucket, then run the SWF from your
bucket.
(Be aware that the debug version of your code does not
exercise the URL policy file: You must run the release SWF file.)
Because the SWF and the data file are located concordantly, a
policy file is superfluous. Glance at your policy file log: It
should indicate only that the root-level (or primary) SWF file
was loaded successfully.
- Delete the SWF from your bucket, and copy the original, released SWF file to
your own server.
- Run the application from your server, and refer to the policy file log.
It should contain something like this:
OK: Root-level SWF loaded: file:///.../bin-release/Example.swf
OK: Searching for <allow-access-from> in policy files to authorize data
loading from resource at http://example-media.s3.amazonaws.com/store.xml
by requestor from file:///.../bin-release/Example.swf
Warning: Failed to load policy file from
http://example-media.s3.amazonaws.com/crossdomain.xml
Error: Request for resource at http://example-media.s3.amazonaws.com/store.xml
by requestor from file:///.../bin-release/Example.swf is denied due to
lack of policy file permissions.
As expected, Flash prevents access to the data file, because
there was no exact match between the hostname of the
SWF file and the hostname of the data file. You can allay the
error with a cross-domain policy file.
- Place the following code in the root directory of your Amazon S3 bucket,
making sure to change the
domain attribute of the
allow-access-from element to refer to your domain
or sub-domain.
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE cross-domain-policy SYSTEM
"http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
<site-control permitted-cross-domain-policies="master-only" />
<allow-access-from domain="*.example.com" />
</cross-domain-policy>
- Run your application anew from your own server.
The application should work, as reflected by an entry in
the policy file log similar to this:
OK: Policy file accepted:
http://example-media.s3.amazonaws.com/crossdomain.xml
OK: Request for resource at http://example-media.s3.amazonaws.com/store.xml
by requestor from http://www.example.com/example.swf is
permitted due to policy file at
http://example-media.s3.amazonaws.com/crossdomain.xml
Success!
Typically, a Flash cross-domain policy file is placed in the root folder of your bucket.
However, you can also place the policy file in any sub-folder within your bucket
to limit access to a subset of resources. A non-master (non-root) policy file only
grants access to data within its own directory and within its directory's subdirectories.
You can find a complete description of the Flash cross-domain policy file syntax
online
at the Adobe Developer Center.
Calling a Web Service from Silverlight
Like an SWF file, a Silverlight application can access resources across a
domain boundary through a URL policy file. Silverlight's native cross-domain
policy file is named clientaccesspolicy.xml. Silverlight also recognizes
and honors Flash's crossdomain.xml file, with two caveats. If you use
crossdomain.xml with Silverlight version 2, the file must be in the root
directory of your site, and the policy file must be configured to allow access
to the service from any domain or it is not recognized by Silverlight 2. This
crossdomain.xml file works with Silverlight 2:
<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM
"http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
<allow-http-request-headers-from domain="*" />
</cross-domain-policy>
Scanning for policy files, Silverlight looks first for clientaccesspolicy.xml;
if a file of that name does not exist, Silverlight then looks for the alternate
crossdomain.xml. If both files are provided, the crossdomain.xml is ignored.
For example, to call a web service (written in Visual Basic, say) hosted on
vbws.example.com from a Silverlight application hosted on www.example.com,
you must grant access by placing an appropriately permissive clientaccesspolicy.xml
file or an equivalent crossdomain.xml file in the root of the www.example.com Web
site.
Silverlight is extremely cautious about cross-domain access. Even a difference in
port numbers between a client and server (say, when both are running on
your local machine during development as localhost:1111 and localhost:2222,
respectively) requires a specific access policy.
Here is an example clientaccesspolicy.xml file identical in purpose to the sample
Flash URL policy file shown earlier. This policy file allows any machine in the
example.com domain to make a SOAP call to request an asset. (This example
originally appeared in the blog of Silverlight developer
Tim Heuer and is used with permission.)
<?xml version="1.0" encoding="utf-8" ?>
<access-policy>
<cross-domain-access>
<policy>
<allow-from http-request-headers="SOAPAction">
<domain uri="http://*.example.com"/>
</allow-from>
<grant-to>
<resource include-subpaths="false" path="/"/>
</grant-to>
</policy>
</cross-domain-access>
</access-policy>
You can find a complete description of Silverlight's URL policy file format on the
Microsoft
Developer Network (MSDN).
Building a complete web service for use with Silverlight is beyond the scope
of this article. For detailed instructions on coding such a service, see
"How
to Access a Service from Silverlight using Visual Studio." However, there's
no need to build a new web service for the purpose of this demonstration. Every
Amazon S3 bucket provides a SOAP interface of its own! If your bucket is
example-media.s3.amazonaws.com, your SOAP endpoint is
http(s)://example-media.s3.amazonaws.com/soap. Visual Studio's tools can
automatically generate the proxy code for a Microsoft .NET application to call
Amazon's SOAP services. To permit the call, simply place a cross-domain policy
file in the root of your bucket.
Of course, if you have a server with your own web services and you want to
share those services with other sites' Silverlight applications, just create
an appropriate clientaccesspolicy.xml file in the root of your web server.
Stay Vain with a CNAME
If you prefer, you can obscure the use of Amazon S3 from users with a
simple CNAME record. Create a CNAME to serve your application and its assets from the same "virtual"
host.
For instance, given the domain example.com, a bucket named example-media,
and a datum called grammy.mp3, the addition of the CNAME record . . .
media IN CNAME example-media.s3.amazonaws.com.
. . . allows the MPEG-3 file to be referred to as the more eponymous
http://media.example.com/grammy.mp3.
You must have access to your domain name server to create a
CNAME.
And although access through a bucket alias—such as example-media.s3.amazonaws.com—works
with Amazon S3's SSL certificates, using a CNAME does not.
Weaving a Wider Web with Amazon S3
With the invention of Flash and Silverlight, a web application can now run on
any platform yet be as rich and interactive as native, desktop analogs.
Alas, a web application is only as reliable as its underlying infrastructure—server,
network, bandwidth—and its security. Happily, with Amazon S3,
you need not worry about uptime any longer. And with cross-domain policy
files, you can combine your great application, leverage all those "9s" of up time, and
keep your application and users safe.
Additional Resources
Amazon S3 product page
Other
Amazon S3 articles and tutorials
About the Author
Martin Streicher is a freelance Ruby on Rails software developer, a widely published
technology writer, and the former Editor-in-Chief of
Linux Magazine. He collects art
and toys and is an avid reader of the scientific works of Dr. Bunsen Honeydew.
Martin has an advanced degree in Computer Science from Purdue University.
He lives in Raleigh, NC.
|