SafeFrame ver1.1 仕様読解、媒体側の実装例


# まえがき

これは IAB が提唱する "SafeFrame" の仕様書を読み解いたメモです。不完全なメモなのですが、SafeFrame について資料が少ないので、少しでも参考になればと思って書いています。

なお、ある程度ネットワーク広告の基礎的な知識を前提にしています。

# SafeFrameとは？

IAB(※)が提唱するネット広告の制作・配信に関する仕様です。
(※: Interactive Advertising Bureau. オンライン広告の大きな業界団体。)

これまで媒体担当者は広告コンテンツを自身のWebページに展開する際、セキュリティリスクを避けるためiframe内に展開することを選択していました。
これにより媒体は保護されていましたが、広告の機能は制限されていました。例えば、広告コンテンツは媒体の情報にアクセスできず広告効果を判断する情報を得られなかったり、広告コンテンツを拡大できなかったりしました。

SafeFrameは、Webページと広告コンテンツの間に安全な方法で通信を行えるようにします。SafeFrame は、SafeFrame API で媒体と広告コンテンツが通信できるiframeです。媒体の安全性を保ちつつ、SafeFrameが提供する範囲内で広告コンテンツは媒体の情報にアクセスしたり、広告コンテンツを拡大できるようになります。

仕様書はこちらです。2017/10/30現在 version1.1 です。
https://www.iab.com/wp-content/uploads/2014/08/SafeFrames_v1.1_final.pdf

SafeFrame API の実装はIABのgithubか、sourceforge.net から取得できます。
https://github.com/InteractiveAdvertisingBureau/safeframe/releases
https://sourceforge.net/projects/safeframes/?source=typ_redirect

※もし仕様のバージョンが上がったら、最新版は下記からダウンロードできると思います。
https://www.iab.com/guidelines/safeframe/

SafeFrameは媒体とクリエイティブ製作者のどちらも対応する必要があります。

# 用語、概念

- host
    - 媒体のこと。広告枠を提供する側。
- external party
    - 広告配信者、広告制作者。媒体からみて"External"である。
- API
    - SafeFrameが提供する API のこと。"host API" と "External party API" がある。
    - "host API" は媒体側が利用するもの。
        - external partyへの情報の提供、レンダリング開始の指定、状態の取得など。
    - "External party API" はexternal party(広告制作者)が利用するもの。
        - hostの情報の取得、広告の拡縮など。
- セカンダリドメイン
    - ホストWebページとは異なるオリジン、ホスト名、ドメインを持つドメイン。
        - host のドメインとは **別に**、 広告を展開するiframe内のドメインとするもの。
- The SCRIPT Tag
    - `<script type='text/x-safeframe'>` とするタグ。
    - (通常のScriptタグと混じりややこしいので、 "x-safeframeタグ" と勝手に呼ぶことにする。)
- viewable impression(ビューアブルインプレッション)
    - 実際にユーザーが閲覧できる状態にあった広告インプレッションのこと。
    - (詳しくはググってほしい。)

# SafeFrameが実現できること

## hostが安全にexternal partyの広告を表示できる

SafeFrameは、広告コードを「別ドメインのiframe内」に表示します。広告コードはクロスドメイン制約によってiframeの外(host)にアクセスできなくなります。
(SafeFrame特有の機能というわけではなく、これはあくまで iframe とクロスドメイン制約の話です。)

## 広告はSafeFrameのAPIが提供する範囲内で、hostの情報にアクセスできる

上記で広告コードがhostにアクセスできないと書きましたが、hostとexternal partyがSafeFrameに対応していれば、広告コードはSafeFrame APIを使用してhostの情報にアクセスすることができます。
ただし、アクセスできる情報はSafeFrame APIが提供するもののみです。詳しくは仕様書の5章の External Party用APIドキュメントをみてください。主に以下のような機能があります。

- 広告の拡縮
- host側cookieの読み/書き
- Viewable impressionの取得

また、一部のSafeFrame APIは、host側のconfigによって利用の可否を制限することができます(後述します)。

### hostはexternal partyに対して一部の機能を制限できる

SafeFrame APIは External party API を提供しますが、その一部はhost側のconfigで利用の可否を制限することができます。制限できる項目は以下の4つです。

- オーバーレイモードで拡張が許可されるかどうか。default: true
- プッシュモードで拡張が許可されるかどうか。default: false
- hostのCookieの読み取りを許可するかどうか。default: false
- host domainにCookieを書き込むことを許可するかどうか。default: false

(オーバーレイモードとプッシュモードは広告の拡縮に関する設定なようですが、広告の拡縮についてはきちんと読んでいません orz)

External Party API のうち上記のConfigで制限されないものは、常に利用できます。

### external partyが viewable impression を取得できる

クリエイティブ製作者(External party)は External Party API を使って viewable impression を取得できます。External Party API を利用するため、媒体側は基本的に SafeFrame からは viewable imp を取得できません。

viewable imp の加算ロジックはクリエイティブ製作者に委ねられています。SafeFrameの External paty API が提供するのは、viewable imp を測定するのに十分な機能のみで、その加算ロジックまでは提供しません。

(「viewable imp の測定基準は業界団体がまだ検討中なので、測定に用いる機能のみ提供している」と私は理解しています。)

参考: viewable imp の測定基準について
https://markezine.jp/article/detail/20449

参考: ビューアブルインプレッション測定ガイダンスを公開(JIAA)
http://www.jiaa.org/topics/viewable_guidance.html

参考: viewable imp の測定に用いるAPIのドキュメント

- 5.4  sf.ext.geom
- 5.10 sf.ext.inViewPercentage
- 5.11 sf.ext.winHasFocus

### external partyが広告サイズの拡縮ができる(制限アリ)

(広告の拡縮ができるそうですがきちんと読んでないので流します)
媒体側のConfigをOFFにすることで、external partyが広告の拡縮をできないようにすることができます。

### external partyがhostのCookieを読み書きできる(制限アリ)

(Cookieの読み/書きができるそうですがきちんと読んでないので流します)
媒体側のConfigをOFFにすることで、external partyがCookieの読み/書きができないようにすることができます。

# SafeFrameの技術

SafeFrameの仕様書の中に「SafeFrameの機能の実現は postMessage に頼っている」などと書かれていたため、簡単にですが postMessage について調べたメモです。

## 前提

SafeFrameは、広告コードを「セカンダリドメインのiframe内」に展開します。

下記は baitai.com というサイトに書く、広告コードを展開するセカンダリドメインのiframeの例です。

```baitai.com
<iframe id="ad" src="http://secondary-domain.com/hoge.html">
  // 広告コンテンツをここに展開する。
  // secondary-domain.com のiframe なので、
  // クロスドメイン制約によりiframeの外のbaitai.comにはアクセスできない。
</iframe>
```

(iframe の src属性に指定する "セカンダリドメイン" は媒体側が用意します。セカンダリドメインから配信するHTMLファイルも媒体側が用意します。このHTMLファイルも SafeFrame に対応するための仕様があります。これらは後述します。)

## postMessage() によるクロスドメインiframeを越えた通信

これは分かりやすい解説記事をみつけたので、そちらで。

>広告クリエイティブにおける技術紹介③　〜iframeクロスドメイン対応〜（担当：長尾俊）
https://hitokuse.com/blog/?p=158


# 媒体側の実装例

媒体側の実装については、仕様書の2章が詳しく、細かい注意点などが解説されています。

ここでは以下のような条件で実装することを考えてみました。

- SafeFrameには"配信モードA"と"配信モードB"があるが、配信モードBで行う
    - 配信モードAとは、Hostサーバでクリエイティブを取得して、ブラウザへ配信するモードです
    - 配信モードBとは、HostサーバではクリエイティブへのURLのみ取得して、ブラウザからクリエイティブを取得するモードです
- 広告をひとつだけ表示する。1ページに複数の項目を表示する場合、必要なConfigが増える(はず)です。

### セカンダリドメインを用意する

**Host とは Same-origin ではない** オリジンを用意する必要があります。
この "セカンダリドメイン" は、SafeFrameに必要なファイルを配信できなければなりません。配信するファイルのURL規則もあります(後述します)。

### セカンダリドメインにベースHTMLファイルとJavascriptファイルを置く

まず、ベースHTMLファイルとJavascriptファイル(SafeFrame APIの実装)を取得します。
https://sourceforge.net/projects/safeframes/?source=typ_redirect
以下は varsion 1.1.0 の例です。バージョンが上がったらバージョン番号だけ置き換えてください。

`v1.1.0/src/` 以下のファイルがこのようになっているはずです。

```v1.1.0/src/
|--html
| |--r.html
|--js
| |--ext
| | |--ext.js
| |--host
| | |--host.js
| |--lib
| | |--base.js
| | |--boot.js
| | |--proxy.js
```

`src/` 以下を `http://SecondaryHost.com/path-to-SF-resources/1-1-0/` というURLでアクセスできるようにします。
`r.html` なら、`http://SecondaryHost.com/path-to-SF-resources/1-1-0/html/r.html` というURLでアクセスできるようになります。

`path-to-SF-resources/` は複数のディレクトリが許可されています。

(詳しくは「SafeFrameのURI規則」が仕様書の 2.2.4 にあります)

### 媒体ページでSafeFrame APIを読み込み、host.config を記述する

媒体ページで以下のようにして SafeFrame API を読み込み、host.config を記述します。
host.cinfig は host API の全体的な設定です。

```媒体ページ
<script src="http://SecondaryHost.com/path-to-SF-resources/1.1.0/js/lib/base.js"></script>
<script src="http://SecondaryHost.com/path-to-SF-resources/1.1.0/js/host/host.js"></script>
<script src="http://SecondaryHost.com/path-to-SF-resources/1.1.0/js/lib/boot.js"></script>
<script type="text/javascript">
    (function() {
        var conf = new $sf.host.Config({
            renderFile: "http://SecondaryHost.com/path-to-SF-resources/1-1-0/html/r.html"
        });
    })();
</script>
```

renderFile にはセカンダリドメインに置いたHTMLファイルへのパスを記述します。
(※仕様書では、renderFile は `/html/r.html` とだけ書いて、別の項目でパスの指定をしているように読めるのですが、試しに動かしてみた手元の環境ではフルパスでしか動きませんでした。なにか読み間違えているのか、手元の環境がおかしいのかわかりませんでした。)

### "x-safeframeタグ" を記述する

"x-safeframeタグ" の例です。広告掲載位置についてそれぞれ設定するものになります。

```
// 広告を表示する位置を示すdivタグ
<div id="leaderboard">
</div>

// "x-safeframeタグ"
<script type='text/x-safeframe' class='sf_data'>
{
    "id" : "leaderboard_src",
    "src" : "http://ad.delivery.com/path-to-creative/sample.js",// クリエイティブへのURL
    "conf" : // この conf は、sf.host.PosConfig のフィールドに対応している
        {
        "size" : "728x90",
        "z" : "1"// z-index値
        "dest": "leaderboard",// divタグのIDを指定する
        "supports" : {// 全て明示的に OFF にしています
            "exp-ovr" : "0",
            "exp-push" : "0",
            "read-cookie" : "0",
            "write-cookie" : "0"
        }
    }
}
</script>
```

`id` はページ内に複数の広告を表示する場合、分ける必要がある...と思います。
`src` は広告クリエイティブへのURLです。Hostサーバであらかじめ取得しておく必要があります。(これが"配信モードB"の設定になります。クリエイティブへのURLでなく、クリエイティブそのものをHostサーバで取得してしまうのが"配信モードA"であり、configの書き方も異なってきます。)
`conf` は sf.host.PosConfig(仕様書4.5章) のフィールドに対応しており、例として `supports` では設定できる限りすべての機能をOFFにしています。

x-safeframeタグの class属性の値には `sf_data`と書きます。
※仕様書に一箇所だけ 「`iab_sf_data` という値にしろ」と書かれているのですが、たぶんミスだと思います。APIの実装を見ても `sf_data` はありますが `iab_sf_data` はありませんでした。


# 参考

- "[広告] SafeFrameを試してみる" https://qiita.com/daei/items/ef6b7d09f1a9c5d66331