クロススクリプティング

本エントリーでは、FLASH Playerのセキュリティのうちクロススクリプティングに焦点を当てます。外部リソースを利用する際のポリシーファイルに関しては、fla.la » crossdomain.xmlをご覧下さい。

AdobeのFlash Playerデベロッパーセンターにある以下の記事が参考になります。

より安全なSWF Webアプリケーション

クロススクリプティングとは

fla.la » crossdomain.xmlでも紹介した通り、Flash Playerのセキュリティに関しては、AdobeのActionScript3のドキュメントのFlash Player セキュリティに詳しく掲載されています。

ではクロススクリプティングとはどんな操作を指すのかまとめてみます。

  • Loaderクラスのcontent変数を通じて、ロードしたswfファイルを参照するオブジェクトを取得する
  • ロードしたswfファイル内に定義されたプロパティにアクセスする
  • ロードしたswfファイル内に定義されたメソッドを実行する
  • ロードしたswfファイル内に定義されたクラスを参照する
  • BitmapDataクラスのdrawメソッドを通じて、ロードしたswfファイルのピクセルをBitmapDataクラスのインスタンスへコピーする
  • ロード元swfファイルのDisplayObjectツリーと、ロードしたswfファイルのDisplayObjectツリー間を移動する
  • ロード元swfファイルのDisplayObjectツリーと、ロードしたswfファイルのDisplayObjectツリー間のイベント伝達フロー

フローを確認

外部リソースをロードするswfを main.swf、外部リソースをresource.swfとします。

同一ドメインから外部リソースをロードする

swfのドメインをswf.example.comとすると、フローは以下のようになります。 [caption id="attachment_153" align="aligncenter" width="515" caption="同一ドメインのswfでクロススクリプティング"]同一ドメインのswfでクロススクリプティング[/caption]

  1. http://swf.exapmle.com/main.swfへアクセス
  2. main.swfを受け取る
  3. main.swfからhttp://swf.exapmle.com/resource.swfをロードする
  4. resource.swfを受け取る
  5. resource.swfに定義されている変数、メソッド、クラスにアクセスする

同一ドメイン内のswfファイルを扱う際には、簡単ですね。

別ドメインから外部リソースをロードする

main.swfからresource.swfをロードする2つの方法を紹介します。

  • Loaderを使う
  • URLLoaderを使う
Loaderを使う場合

外部リソースに対してクロススクリプティングを行う際には、外部リソースにおいて、ロード元のswfのドメインからのアクセスの許可が必要です。

resource.swf内では以下の記述が必要です。

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

フローは以下のようになります。 [caption id="attachment_207" align="aligncenter" width="628" caption="別ドメインのswfでクロススクリプティング Loader"]別ドメインのswfでクロススクリプティング Loader[/caption]

  1. http://swf.exapmle.com/main.swfへアクセス
  2. main.swfを受け取る
  3. main.swfからhttp://other.exapmle.net/resource.swfをロードする
  4. resource.swfを受け取る
  5. resource.swfでSecurity.allowDomain("swf.example.com");を確認
  6. resource.swfに定義されている変数、メソッド、クラスにアクセスする
URLLoaderを使う場合

基本的に、外部リソースに対してクロススクリプティングを行う際には、外部リソースにおいて、ロード元のswfのドメインからのアクセスの許可が必要です。

但し、URLLoaderを使い、ロードしたURLLoaderインスタンスのdataプロパティをLoaderインスタンスのloadBytesメソッドに渡す事で、外部リソースに対してクロススクリプティングが可能です。

これは、LoaderインスタンスのloadBytes()メソッドでは、ロードされた SWF ファイルが常にロード元 SWF ファイルのセキュリティドメインに配置されるためです。

その際、URLLoaderインスタンスのloadメソッドは、データのロードにあたるので、外部リソースのドメインに配置したポリシーファイルにおいてロード元のドメインが許可されている必要があります。

http://other.example.net/crossdomain.xmlの内容の一例は以下のようになります。







フローは以下のようになります。

[caption id="attachment_208" align="aligncenter" width="628" caption="別ドメインのswfでクロススクリプティング URLLoader"]別ドメインのswfでクロススクリプティング URLLoader[/caption]

  1. http://swf.exapmle.com/main.swfへアクセス
  2. main.swfを受け取る
  3. http://other.example.net/crossdomain.xmlへアクセス
  4. crossdomain.xmlを受け取る
  5. main.swfからhttp://other.exapmle.net/resource.swfをロードする
  6. resource.swfを受け取る
  7. resource.swfに定義されている変数、メソッド、クラスにアクセスする

ロードのサンプル

ここでサンプルとして、以下のようなpublicメソッドを持つ簡易アバターを作成しました。

簡易アバターのAPI
API機能
frontアバターが前を向きます
backアバターが後を向きます
leftアバターが左を向きます
rightアバターが右を向きます
playアバターがアニメーションします

簡易アバターのURLは以下となります。 avatar1.swfとavatar2.swfはまったく同じ機能をもったswfですが、下記の記述はavatar2.swfにしかありません。

Security.allowDomain("fla.la");

つまり、avatar2.swfはfla.laドメインに配置されたswfからパブリックメソッドを実行出来ます。

では、LoaderとURLLoaderでこれらの簡易アバターをロードしたのち、クロススクリプティングで操作するコードを紹介します。

Loaderを使う際のサンプルコード

以下をドキュメントクラスとするswfをfla.laドメインへ配置するとします。

package
{
    import flash.display.Loader;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.net.URLLoader;

    /**
     * 外部リソースをLoaderでロードしてからメソッドにアクセス
     * @author naoto koshikawa
     */
    public class CrossScript1 extends Sprite
    {
        //----------------------------------------------------------------------
        //  methods
        //----------------------------------------------------------------------
        //------------------------------
        //  public methods
        //------------------------------
        /**
         * constructor
         */
        public function CrossScript1()
        {
            var avatar:*;

            var loader:Loader = new Loader();
            loader.contentLoaderInfo.addEventListener(
                Event.COMPLETE,
                function(event:Event):void
                {
                    avatar = loader.content; // avatar1.swfだとここでエラー
                    addChild(avatar);
                    avatar.play();
                }
            );
            loader.load(new URLLoader("http://asmple.com/work/avatar2.swf"));
        }
    }
}

32行目の処理は、Loaderクラスのcontent変数を通じて、ロードしたswfファイルを参照するオブジェクトを取得することに該当するので、外部リソースのswfでSecurity.allowDomain()メソッドの処理が必要となります。

                function(event:Event):void
                {
                    avatar = loader.content; // avatar1.swfだとここでエラー
                    addChild(avatar);
                    avatar.play();
                }

URLLoaderを使う

以下をドキュメントクラスとするswfをfla.laドメインへ配置するとします。URLLoaderを使う場合はavatar1.swfでもavatar2.swfでもロードした後に、アニメーションを開始することが可能です。 但し、http://asmple.com/crossdomain.xmlにて以下の記述が必要となります。







package
{
    import flash.display.Loader;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.net.URLLoader;
    import flash.net.URLLoaderDataFormat;
    import flash.net.URLRequest;

    /**
     * 外部リソースをURLLoaderでロードしてからメソッドにアクセス
     * @author naoto koshikawa
     */
    public class CrossScript2 extends Sprite
    {
        //----------------------------------------------------------------------
        //  methods
        //----------------------------------------------------------------------
        //------------------------------
        //  public methods
        //------------------------------
        /**
         * constructor
         */
        public function CrossScript2()
        {
            var avatar:*;

            var loader:Loader = new Loader();
            loader.contentLoaderInfo.addEventListener(
                Event.COMPLETE,
                function(event:Event):void
                {
                    avatar = loader.content;
                    addChild(avatar);
                    avatar.play();
                }
            );

            var urlLoader:URLLoader = new URLLoader();
            urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
            urlLoader.addEventListener(
                Event.COMPLETE,
                function(event:Event):void
                {
                    loader.loadBytes(urlLoader.data);
                }
            );
            urlLoader.load(new URLRequest("http://asmple.com/work/avatar1.swf"));
        }
    }
}

動くサンプル

別ドメインの外部リソースのメッソドにアクセスする例を紹介します。

Loaderを用いた実例

から、先ほども紹介した

をロードします。先の例に照らし合わせると、cross_script3.swfがmain.swfでavatar1.swf, avatar2.swfがresource.swfとなります。

以上を踏まえて、以下のサンプルを触ってみて下さい。ロードするswfを選択してloadボタンをクリックするとswfがロードされます。ロードされたswfの周りに配置されたup, right, down, leftの各ボタンはswfに定義されたメソッドを実行します。

[swfobj src="http://fla.la/swf/cross_script3.swf" width="256" height="160"]

実行すると分かるように、avatar1.swfをロードした後に、up, right, down, leftの各ボタンをクリックするとSecurityErrorが発生しますが、avatar2.swfではロードされたswfがアニメーションを開始します。

URLLoaderを用いた実例

まったく同じサンプルですが、LoaderではなくURLLoaderを用いて外部リソースをロードしています。

から

をロードします。先の例に照らし合わせると、cross_script4.swfがmain.swfでavatar1.swf, avatar2.swfがresource.swfとなります。

[swfobj src="http://fla.la/swf/cross_script4.swf" width="256" height="160"]

こちらの例では、avatar1.swfでもアバターの操作が出来る事と、avatar1.swf, avatar2.swfをロードする際に

をリクエストしていることが確認出来ると思います。

firefoxをお使いの方は、Live HTTP Headersfirebugを使うと簡単に確認出来ます。

[caption id="attachment_206" align="aligncenter" width="390" caption="crossdomain.xmlを取得する様子をfirebugで確認"]crossdomain.xmlを取得する様子をfirebugで確認[/caption]

crossdomain.xmlのロードはキャッシュするので、ロードの確認の際にはブラウザのキャッシュをクリアしてからお試し下さい。

おまけ(wonderflの例)

wonderflに投稿したswfは同一ドメイン swf.wonderfl.net に存在するため、crossdomain.xml も allowDomain も 使わずに他の投稿をロードして使うことが出来ます。

ただし、以下のようにブログパーツとして貼り付ける場合はwonderfl.netドメインのswfファイルを利用するようなので、リソース側のswfに以下の記述が必要です。(このエントリーを書きながら気づきました)

Security.allowDomain("wonderfl.net");
ロードするswf

本エントリーの例では外部リソース resource.swfにあたります。

ロード元swf

本エントリーの例では外部リソース main.swfにあたります。

他の投稿から使うライブラリ的なswfを投稿してみんなに使ってもらうことも出来そうですね。ちなみに、

ちなみに、wonderflに投稿したswfのurlは以下のように取れます。

var swfURL:String = loaderInfo.url;

wonderflにも投稿してあります。

まとめ

別ドメインの外部リソースを扱う際には、Loaderを使うのか、URLLoaderを使うのかによって、クロススクリプティングをする際にチェックするポイントが異なります。

外部リソースのswfを扱うことで柔軟なflashアプリケーションを作成していきたいと思います。