Flash Player security and application domains summary

12.10.2012 3544 2

Ready ?

Every SWF file that is running in an instance of the Flash Player is inside nested sandboxes.
The first sandbox I named SWF sandbox, then application domains and security domains.
We gonna see these things in the order we have to deal with in a development process.

SWF sandboxes

  • Local with file : the SWF file is located on the local machine and is able access the local machine but not the network
  • Local with network : the SWF file is located on the local machine and is able to access the network but not the local machine
  • Local trusted : the SWF file is located on the local machine and is able to work both on the local machine and on the network
  • Remote : the SWF file is located on the network and is able to access the network but not the local machine
  • Application : the SWF file is embed inside the AIR projector. I will not dig this part in this post, I prefer AIR the music band...

Local with file (by default) or Local with network settings can be specified in main IDEs, this is written at compile time inside the SWF. You allow the SWF to go on the network but not access the local machine or vice-versa. This is the first step in security for the developer.
In order to get your SWF local trusted, that is another step in security and development, you have to put it into a trusted location or "authorize it". This can be done in different ways :

  • The Global Security Settings panel allows you to specify which file or directory you trust, so you authorize to access both the local machine and the network.
  • The Global Flash Player Trust directory where you can set .cfg files that contains the locations of the trusted SWF files or directories. On windows 7 you can find this directory here :
C:\Users\$userName\AppData\Roaming\Macromedia\Flash Player\#Security\FlashPlayerTrust\
  • The mms.cfg file where you can also set locations of the trusted SWF files or directories.

The Local with file and the local trusted sandboxes have full access to the local machine data.
The Local with network, local trusted and the remote SWF's sandboxes can access the network, but this access is restricted by security domains.

Security domains

The picture above, that I love , shows the different use cases where we have to deal with these sandboxes :

Nothing special to do, all is working without any special setting.
You have to set something special or get authorizations.
Exceptions will be thrown in any cases.
The bold curves show the outgoing requests.
The thin curves show the requests type :

  • Text or binary data using URLLoader, sockets or all .loadBytes() methods :
    • This kind of data is yet in a raw "low level" format.
  • Media data (pictures, sounds, movies) using Loader or flash.media.Sound.load() and so on :
    • This kind of data can be used in a common way (addChild(), scale the loader, play() the sound...) or in a "low level" way by accessing and scripting the content data (getPixel(), computeSpectrum()...)
  • SWF content using Loader :
    • This kind of data can be used in a common way (addChild(), rotate the loader) or in a "low level" way by accessing and scripting the content data (getDefinition()...)

The different cases :

  • 1 - Loading text or binary data or loading and scripting media data or SWF content from the same domain.
  • 2 - Loading text or binary data or loading and scripting media data or SWF content from a different domain within the local and network or local trusted sandbox.
  • 3 - Loading SWF content from different domain inside the remote sandbox. In order to script its content you have 2 ways :
    • A cross-domain policy file that trusts the requesting SWF domain and loading the SWF content into the current security domain are needed.
var ctx = new flash.system.LoaderContext();
ctx.loadPolicyFile	= true;
ctx.securityDomain	= flash.system.SecurityDomain.currentDomain;
mySWFLoader.load( "path/to/My.swf", ctx );
OR
flash.system.Security.loadPolicyFile( "remoteDomain.com/crossdomain.xml" );
...
var ctx 		= new flash.system.LoaderContext();
ctx.securityDomain	= flash.system.SecurityDomain.currentDomain;
mySWFLoader.load( "path/to/My.swf", ctx );

OR

    • The requesting SWF domain must be explicitly authorized inside the requested SWF (and vice-versa if needed) :
flash.system.Security.allowDomain( "myDomain.com" );
  • 4 - Loading media content from different domain inside the remote sandbox In order to script its content :
    • A cross-domain policy file that trusts the requesting SWF location is needed.
var ctx 		= new flash.system.LoaderContext();
ctx.loadPolicyFile	= true;
myLoader.load( "path/to/MyPicture.png", ctx );
OR
flash.system.Security.loadPolicyFile( "remoteDomain.com/crossdomain.xml" );
...
var ctx = new flash.system.LoaderContext();
myLoader.load( "path/to/MyPicture.png", ctx );
  • 5 - Loading text or binary data from different domain inside the remote sandbox In all cases :
    • A cross-domain policy file that trusts the requesting SWF location is needed.
flash.system.Security.loadPolicyFile( "remoteDomain.com/crossdomain.xml" );
...
myLoader.load( "path/to/MyData.xml" );
  • 6 - The local with network sandbox cannot access the local domain file system. See SWF sandboxes to set your SWF's sandbox to trusted.
  • 7 - The local with file sandbox cannot access the network. See SWF sandboxes to set your SWF's sandbox to trusted.

Cross-domain policies

Commonly named crossdomain.xml, this can be a file or data served by a socket server. This is a basic XML that filters requests and allow access for domains (and ports when using with sockets). It looks like :

<cross-domain-policy>
	<allow-access-from domain="myDomain.com"/>
	<!-- all subdomains -->
	<allow-access-from domain="*.myDomain.com"/>
	<!-- all -->
	<allow-access-from domain="*"/>
</cross-domain-policy>

To be operating in a transparent way, when using with LoaderContext, this policy file must be named crossdomain.xml and must be located at the root of the server.

Force loading the policy file

You can force the Flash Player to load this file from a specified location thanks to the loadPolicyFile method :

flash.system.Security.loadPolicyFile( "http://www.myDomain.com/myDir/security.xml" );

Note : Here, the rules that contains the policy file apply to all data inside http://www.myDomain.com/myDir/
Another way to force the Flash Player to load the policy file is to use the LoaderContext class (or SoundLoaderContext and so) when loading a SWF or a media like that :

var ctx = new flash.system.LoaderContext( true );
OR
var ctx			= new flash.system.LoaderContext();
ctx.loadPolicyFile	= true;

Here the Flash Player will start loading the requested file once the crossdomain.xml file located on the root of the target server is loaded and authorize the requesting domain.
We can also get the policy XML through a socket connection at a specified port, otherwise the default port is 843 :

flash.system.Security.loadPolicyFile( "xmlsocket://www.myDomain.com:660" );

Note : The policies won't be loaded after writing this line. A call to a socket connection will launch the loading.

About sockets

The socket policies are quite the same as a "normal" crossdomain.xml file content with an additional attribute :

<cross-domain-policy>
	<!-- all subdomains from port 2000 to 2100 and port 2111 -->
	<allow-access-from domain="*.myDomain.com" to-ports="2000-2100,2111" />
</cross-domain-policy>

The attribute to-ports where you can put a range of available ports and/or singles ports.
It's important to notice that crossdomain policies served on port 1024 or below can authorize all range of ports and these served on port higher than 1024 can only authorize ports higher than 1024.

Working with external SWF files, once you allow another SWF file to access your content data, you have several possibilities to share the classes and objects. Here come application domains.

Application domains

Looking at the previous picture, we are here in the 3rd case :

  • 3 - Loading SWF content from different domain inside the remote sandbox. In order to script its content you have 2 ways :
    • A cross-domain policy file that trusts the requesting SWF domain and loading the SWF content into the current security domain are needed.
var ctx = new flash.system.LoaderContext();
ctx.loadPolicyFile	= true;
ctx.securityDomain	= flash.system.SecurityDomain.currentDomain;
mySWFLoader.load( "path/to/My.swf", ctx );
OR
flash.system.Security.loadPolicyFile( "remoteDomain.com/crossdomain.xml" );
...
var ctx 		= new flash.system.LoaderContext();
ctx.securityDomain	= flash.system.SecurityDomain.currentDomain;
mySWFLoader.load( "path/to/My.swf", ctx );

OR

    • The requesting SWF domain must be explicitly authorized inside the requested SWF (and vice-versa if needed) :
flash.system.Security.allowDomain( "myDomain.com" );

This way you can access properties, methods and classes in the loaded SWF and these features are used by RSL : Runtime Shared Librairies
But what happens when the SWF share the same resource ? There are 3 main behaviours :

All mixed : ApplicationDomain.currentDomain

The application domains are merged and all classes are mixed at the same level. Both the loaded SWF and the loader can use all the classes the same way, and you can in this case use externs in Haxe, in order to get the ghost class signature. Here comes an example :
Main SWF :

class MainBall extends flash.display.Sprite{
    public var size    : Int;
    public function new( size : Int ){
        this.size = size;
        graphics.beginFill( 0xFF0000 );
        graphics.drawCircle( 0, 0, size );
    }
}
extern class LoadedBall extends flash.display.Sprite{
    public var size    : Int;
    public function new( size : Int );
}
class Main{
    static funtion main(){
        var ctx    = new flash.system.LoaderContext( true, flash.system.ApplicationDomain.currentDomain, flash.system.SecurityDomain.currentDomain );
        var loader = new flash.display.Loader();
        loader.contentLoaderInfo.addEventListener( flash.events.Event.complete, cb_complete );
        loader.load( new flash.net.URLRequest( "loaded.swf" ), ctx );
    }
    static function cb_complete( e : Event ){
        var ball = new LoadedBall( 7 );
        Lib.current.addChild( ball );
    }
}

Loaded SWF :

extern class MainBall extends flash.display.Sprite{
    public var size    : Int;
    public function new();
}
class LoadedBall extends flash.display.Sprite{
    public var size    : Int;
    public function new( size : Int ){
        this.size = size;
        graphics.beginFill( 0x0000FF );
        graphics.drawCircle( 0, 0, size );
    }
}
class Loaded{
    static funtion main(){
        var ball = new MainBall();
        Lib.current.addChild( ball );
    }
}

It's important to notice that if the loaded SWF contains a class definition that already exists in the parent SWF (the loader one), this definition will be skipped, ignored. There is no way to get "updated" version of the class definition in a loaded SWF. Take a look at this example :
Main SWF :

class Ball extends flash.display.Sprite{
    public var size    : Int;
    public function new( size : Int ){
        this.size = size;
        graphics.beginFill( 0xFF0000 );
        graphics.drawCircle( 0, 0, size );
    }
}
class Main{
    static funtion main(){
        var ctx = new flash.system.LoaderContext( true, flash.system.ApplicationDomain.currentDomain, SecurityDomain.currentDomain );
        var loader = new flash.display.Loader();
        loader.load( new URLRequest( "loaded.swf" ), ctx );
    }
}

Loaded SWF :

class Ball extends flash.display.Sprite{
    public var size    : Int;
    public function new( size : Int ){
        this.size = size;
        graphics.beginFill( 0x0000FF );
        graphics.drawCircle( 0, 0, size );
    }
}
class Loaded{
    static funtion main(){
        var ball = new Ball();
        Lib.current.addChild( ball );
    }
}

Here, a red ball will be displayed. The loaded Ball class definition is ignored.
Note : In AS2, the last loaded class definition took the place of the previous one. It was easier to "update" an existing class.

All separated : new ApplicationDomain() or new Application( null )

The application domains are totally separated. If a loaded class definition already exists in the loader's SWF domain, both will exist in their own application domain. The classes each other are still accessible but by different way :
Main SWF :

class Main{
    static funtion main(){
        var ctx    = new flash.system.LoaderContext( true, new flash.system.ApplicationDomain(), flash.system.SecurityDomain.currentDomain );
        var loader = new flash.display.Loader();
        loader.contentLoaderInfo.addEventListener( flash.events.Event.complete, cb_complete );
        loader.load( new flash.net.URLRequest( "loaded.swf" ), ctx );
    }
    static function cb_complete( e : Event ){
        var ball = e.currentTarget.applicationDomain.getDefinition( "Ball" );
        Lib.current.addChild( ball );
    }
}

Loaded SWF :

class Ball extends flash.display.Sprite{
    public var size    : Int;
    public function new( size : Int ){
        this.size = size;
        graphics.beginFill( 0x0000FF );
        graphics.drawCircle( 0, 0, size );
    }
}
class Loaded{}

Note : You cannot use Haxe's externs in this case because the 2 class definitions are in separated application domains, in fact they are as different classes. It's also important to notice that it works the same for some Haxe's std types so sharing data in this way can be really painful.

Custom : new ApplicationDomain( loader.contentLoaderInfo.applicationDomain )

There is a way to define a custom application domain that inherits definitions from a parent application domain.
For example you can load 2 SWFs A and B each into a new domain but which is a child of the Main SWF ( ApplicationDomain.currentDomain ) :
Main SWF :

class Common {
    static function foo(){
        trace( "foo" );
    }
}
class Main{
    static funtion main(){
        var ctx1    = new flash.system.LoaderContext( true, new flash.system.ApplicationDomain( flash.system.ApplicationDomain.currentDomain ), flash.system.SecurityDomain.currentDomain );
        var ctx2    = new flash.system.LoaderContext( true, new flash.system.ApplicationDomain( flash.system.ApplicationDomain.currentDomain ), flash.system.SecurityDomain.currentDomain );
        var loader1 = new flash.display.Loader();
        var loader2 = new flash.display.Loader();
        loader1.load( new flash.net.URLRequest( "A.swf" ), ctx1 );
        loader2.load( new flash.net.URLRequest( "B.swf" ), ctx2 );
    }
}

A SWF :

extern class Common {
    static function foo() : Void;
}
class Ball extends flash.display.Sprite{
    public var size    : Int;
    public function new( size : Int ){
        this.size = size;
        graphics.beginFill( 0xFF0000 );
        graphics.drawCircle( 0, 0, size );
    }
}
class A{
    static function main(){
        Common.foo();
    }
}

B SWF :

extern class Common {
    static function foo() : Void;
}
class Ball extends flash.display.Sprite{
    public var size    : Int;
    public function new( size : Int ){
        this.size = size;
        graphics.beginFill( 0x0000FF );
        graphics.drawCircle( 0, 0, size );
    }
}
class B{
    static function main(){
        Common.foo();
    }
}

In this example, both, A and B can use the foo method from the Common class that is in their parent application domain. Common class can be used as Haxe's extern in A and B. Both the red and the blue Ball class definitions exist because they are in separated application domains. The 3 SWF files can use the red and the blue ball accessing them as in separated application domains, using applicationDomain.getDefinition(...)

flash.system.Security.allowDomain() (and allowInsecureDomain())

As seen upper, we can cross-script 2 SWF files using a simle loading (without any loaderContext) and Security.allowDomain() like that :
A SWF (on "anotherDomain.com" ):

class A {
    public static function foo(){
        trace( "foo );
    }
    static function main(){
       flash.system.Security.allowDomain( "myDomain.com" );
    }
}

Main SWF (on "myDomain.com" ) :

class Main {
   static function main(){
        var loader = new flash.display.Loader();
        loader.contentLoaderInfo.addEventListener( flash.events.Event.complete, cb_complete );
        loader.load( new flash.net.URLRequest( "http://anotherDomain.com/A.swf" ) );
    }
    static function cb_complete( e : Event ){
        e.currentTarget.applicationDomain.getDefinition( "A" ).foo();
    }
}

In this example, A allows "myDomain.com" to access its resources but A cannot access Main's resource. It must be explicitly defined in Main's SWF with :

flash.system.Security.allowDomain( "anotherDomain.com" );

As for crossdomain policies, you can use subdomains ( *.myDomain.com ) or wildcards too ( "*" )
The main difference between using LoaderContext, SecurityDomain and ApplicationDomain and using Security.allowDomain() in order to cross-script, is that the last way let the loaded SWF in its original security domain. It means that the SWF can still access it's own domain resources. This is not the case when loading the SWF in the current security domain.
allowInsecureDomain() works the same way as allowDomain() but with the HTTPS protocol.

Conclusion :


We have seen almost all the cases where we have to deal with Flash Player's security sandboxes and there is always an appropriate way to solve a "problem". So since now, please avoid the common Security.allowDomain( "*" ) and <allow-access-from="*" /> !

Commentaires

16.07.2014 à 14:18 George

"As for crossdomain policies, you can use subdomains ( *.myDomain.com ) " is misleading to readers .
Check http://goo.gl/er2oVz and http://help.adobe.com/en_US/AS2LCR/Flash_10.0/help.html?content=00000466.html.

16.07.2014 à 16:02 Michal

You're right, I'll remove this sentence, thanks

Laisser un commentaire

http://
×