Wowza Media ServerでSWFVerification(っぽいこと)をする方法

2008.12.09 / actionscript

Kitasando.asで話題になったのがSWFVerification。これはストリーミングサーバが特定のswfからのアクセスしか許さない、という機能。FMS3は接続を行うswfをあらかじめ登録しておくことで、接続時にハッシュ値の整合性を確認し、1byteでも異なっている場合は接続を許可しない、という仕様だそうです。(どうやって整合性とってるのかは謎。typesterさんも言及してました。

で、そんな機能ってたしかWowzaにもあったはずだよなぁ。。と思って調べてみるとそれっぽい機能がありました。ただ、FMS3とは少し違う仕様で、「接続を許可するswfの設置サーバのドメインを登録できる」というもの。ハッシュ値照合までしないので、厳密にはVerificationではないものの、セキュリティ的には結構これで事足りる気はします。つまりローカルにswfを落としてそこから接続を試みるような野良swfは接続できない、ということですね。

実際、FMS3のSWFVerificationはデプロイが問題になるみたいです。と、いうのもクライアントのswfを書き出し直すたびにFMS3にデプロイしなおす必要があるので、この作業が煩雑になる問題があるみたいですね。それを考えるとWowzaの接続元ドメインのホワイトリスト制はセキュリティという観点では割といい機能なんじゃないのかな、とも思います。

設定方法

超簡単。Application.xmlの冒頭のConnections/AllowDomainsの箇所に、許可ドメインをカンマで区切って羅列します。こんなかんじ。

  <Connections>
      <AutoAccept>true</AutoAccept>
      <AllowDomains>lab.katsuma.tv</AllowDomains>
  </Connections>
  

こうすると同じswfでもlab.katsuma.tvに設置したもののみがこのアプリケーションに接続できて、他のドメイン(たとえば、blog.katsuma.tv)に設置されたswfは接続できない、というものです。

また、検証コードも書いてみました。手元のWowzaに「live」というアプリケーションを作ってみてください(rtmp://localhost/live に接続できる状態にする)。その後、上でかいたドメイン縛りの記述を付け加えてWowzaを再起動し、Firebugを開いた状態で次のURLにアクセスしてみてください。

前者は

  start init
  ["init", "blog", "rtmp://localhost/live/"]
  start connect
  ["blog", "NetConnection.Connect.Rejected"]
  ["blog", "NetConnection.Connect.Closed"]
  

こんなかんじのログが出て、後者は

  start init
  ["lab", "init", "rtmp://localhost/live/"]
  ["lab", "NetConnection.Connect.Success"]
  

こんなログが出力されるかと思います。どちらも同じswfですが接続元から接続可能かどうかの結果が分かれています。

まとめ

SWFVerificationでWowzaの導入をためらっていた方はこの方法を検討してみる、というのも手かもしれません。デプロイも気にしなくていいから楽ですよ!

ソースコード

あと、いきなりローカルに接続されると怪しく思う人もいると思うのでソース晒しておきます。突貫コードですけど。ちなみにsecondlifeさんのlog関数を利用しています。

RTMPConnectionText.mxml

  <?xml version="1.0" encoding="utf-8"?>
  <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
      paddingTop="0" paddingRight="0" paddingBottom="0" paddingLeft="0"
      creationComplete="init()">
      
      <mx:Script>
          <![CDATA[
              
              private var swfID:String;
              
              private function init() : void 
              {               
  
                  log("start init");
                  var server:String = Application.application.parameters.server || '';
                  this.swfID = Application.application.parameters.id || '';
                  log( this.swfID, "init", server);
              
                  var nc : NetConnection= new NetConnection();
                  nc.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
                  nc.addEventListener(IOErrorEvent.IO_ERROR, ioErrorEventHandler);
                  nc.connect(server);
              }
              
              private function netStatusHandler(evt:NetStatusEvent) : void
              {
                  var code : String = evt.info.code;
                  log(this.swfID, code);
              }
              
              private function ioErrorEventHandler(evt:IOErrorEvent) : void
              {
                  log(this.swfID, evt);
              }
              
          ]]>
      </mx:Script>
      
  </mx:Application>
  

Application.xml

Wowza/conf/live/につっこんで再起動。

  <Root>
      <Application>
          <!-- Uncomment to set application level timeout values
          <ApplicationTimeout>60000</ApplicationTimeout>
          <PingTimeout>12000</PingTimeout>
          <ValidationFrequency>8000</ValidationFrequency>
          <MaximumPendingWriteBytes>0</MaximumPendingWriteBytes>
          <MaximumSetBufferTime>60000</MaximumSetBufferTime>
          -->
          <Connections>
              <AutoAccept>true</AutoAccept>
              <AllowDomains>lab.katsuma.tv</AllowDomains>
          </Connections>
          <!--
              StorageDir path variables
              
              ${com.wowza.wms.AppHome} - Application home directory
              ${com.wowza.wms.ConfigHome} - Configuration home directory
              ${com.wowza.wms.context.VHost} - Virtual host name
              ${com.wowza.wms.context.VHostConfigHome} - Virtual host config directory
  
              ${com.wowza.wms.context.Application} - Application name
              ${com.wowza.wms.context.ApplicationInstance} - Application instance name
              
          -->
          <Streams>
              <StreamType>live-lowlatency</StreamType>
              <StorageDir>${com.wowza.wms.AppHome}/content</StorageDir>
              <Properties>
                  <!-- Properties defined here will override any properties defined in conf/Streams.xml for any streams types loaded by this application -->
                  <!--
                  <Property>
                      <Name></Name>
                      <Value></Value>
                  </Property>
                  -->
              </Properties>
          </Streams>
          <SharedObjects>
              <StorageDir></StorageDir>
          </SharedObjects>
          <Client>
              <IdleFrequency>-1</IdleFrequency>
              <Access>
                  <StreamReadAccess>*</StreamReadAccess>
                  <StreamWriteAccess>*</StreamWriteAccess>
                  <StreamAudioSampleAccess></StreamAudioSampleAccess>
                  <StreamVideoSampleAccess></StreamVideoSampleAccess>
                  <SharedObjectReadAccess>*</SharedObjectReadAccess>
                  <SharedObjectWriteAccess>*</SharedObjectWriteAccess>
              </Access>
          </Client>
          <RTP>
              <!-- RTP/Authentication/Methods defined in Authentication.xml. Default setup includes; none, basic, digest -->
              <Authentication>
                  <Method>digest</Method>
              </Authentication>
              <!-- RTP/AVSyncMethod. Valid values are: senderreport, systemclock, rtptimecode -->
              <AVSyncMethod>senderreport</AVSyncMethod>
              <MaxRTCPWaitTime>12000</MaxRTCPWaitTime>
              <Properties>
                  <!-- Properties defined here will override any properties defined in conf/RTP.xml for any depacketizers loaded by this application -->
                  <!--
                  <Property>
                      <Name></Name>
                      <Value></Value>
                  </Property>
                  -->
              </Properties>
          </RTP>
          <MediaCaster>
              <Properties>
                  <!-- Properties defined here will override any properties defined in conf/MediaCasters.xml for any MediaCasters loaded by this applications -->
                  <!--
                  <Property>
                      <Name></Name>
                      <Value></Value>
                  </Property>
                  -->
              </Properties>
          </MediaCaster>
          <MediaReader>
              <Properties>
                  <!-- Properties defined here will override any properties defined in conf/MediaReaders.xml for any MediaReaders loaded by this applications -->
                  <!--
                  <Property>
                      <Name></Name>
                      <Value></Value>
                  </Property>
                  -->
              </Properties>
          </MediaReader>
          <!-- 
          <Repeater>
              <OriginURL></OriginURL>
          </Repeater> 
          -->
          <Modules>
              <Module>
                  <Name>base</Name>
                  <Description>Base</Description>
                  <Class>com.wowza.wms.module.ModuleCore</Class>
              </Module>
              <Module>
                  <Name>properties</Name>
                  <Description>Properties</Description>
                  <Class>com.wowza.wms.module.ModuleProperties</Class>
              </Module>
              <Module>
                  <Name>logging</Name>
                  <Description>Client Logging</Description>
                  <Class>com.wowza.wms.module.ModuleClientLogging</Class>
              </Module>
              <Module>
                  <Name>flvplayback</Name>
                  <Description>FLVPlayback</Description>
                  <Class>com.wowza.wms.module.ModuleFLVPlayback</Class>
              </Module> 
          </Modules>
          <Properties>
              <!-- Properties defined here will be added to the IApplication.getProperties() and IApplicationInstance.getProperties() collections -->
              <!--
              <Property>
                  <Name></Name>
                  <Value></Value>
              </Property>
              -->
          </Properties>
      </Application>
  </Root>