replyツイートElectronアプリを作成する手習いの第4回として、前回考えたwebviewによるサンドボックス化を実装します。残りのセキュリティ項目を実装します。はじめに前回は、前々回に作成したセキュリティのチェックリストの仮実装を進めたところ、Javascriptの実行を許さないiframeのサンドボックスでは、ユーザーの操作によるイベントの発火自体がエラーになってしまうことがわかりました。そこで苦肉の策としてwebviewでのサンドボックス化を考えました。今回はそのwebviewサンドボックスと残りのセキュリティ項目をテスト実装します。いつもの本稿の環境は以下です。Windows10:ver.1607Node.js:v6.9.1npm:3.10.8Electron:1.4.10Electronのバージョンが異なる場合、本稿の内容が当てはまらない可能性が多々ありますのでご注意ください。ソースこれまでのようにソースを示します。変化がないソースもそのまま示しますので、適当にスキップしてください。(ディレクトリ構造などは第一回をご参照ください。}});package.jsonのmainに書いている、アプリのエントリーです。次のwindow.htmlを開いています。&lt;/div&gt;&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;ヘッダ部を表示するwebviewタグ(id=”header-frame”)と、コンテンツを表示するwebviewタグ(id=”view”)の2つだけです。後者では、preload属性に”preload.js”を指定しています。preloadに指定したスクリプトは、ページ内の他のスクリプトの実行前にロードされます。webviewタグのnodeIntegrationオプションのある無しに関わらず、nodeのAPIが使えます。Buffer.&quot;);}そのスクリプトですが、evalとBufferを明示的に無効にしているだけです。100%;}window.htmlと次のheader.htmlに共通のCSSです。前回は12行の下に「display:block;」が書いてあり、iframeをブロック要素にしていました。webviewは内部的にdisplay:flexにしている、ということで、それと競合する記述は削除しました。&lt;/header&gt;&lt;/body&gt;&lt;/html&gt;テキストボックスが一つあるだけの、ヘッダ部のHTMLです。前回からの変更はありません。'https://translate.google.com');}windows.htmlのプロセスで実行されるスクリプトです。HTMLのロード終了後に実行される10行にて、変数HeaderFrameにwebviewのエレメントを取得し、以降それに対して以下の操作をしています。preloadのスクリプトにheader.jsを設定。webviewに読み込むソースにheader.htmlを設定。header.htmlロード完了後に実行する関数Initializeを設定。header.jsからメッセージが来たときの処理関数の設定。最後の14行から18行が今回の肝です。webviewのプロセスからメッセージが来たときは、一律にそのエレメントのipc-messageイベントになります。引数のオブジェクトの、メンバchannelにチャネル名が、メンバargsにメッセージの引数が配列で入ります。ここでは「url-input」というチャネルにメッセージがあったときに、その引数をURL文字列として、ビューのwebviewのsrc属性にそのURLを指定しています。(結果、ビューにそのページが表示されます。)また、レンダラーのプロセスからwebviewのプロセスにメッセージを送っているのが28行です。引数がチャネル名、それ以降がメッセージの引数です。チャネル名は受信と同じ「url-input」ですが、違う名前にしたほうがわかりやすかったですね。s_url;});header.htmlのプロセスにて実行されるスクリプトです。preloadで指定していますのでnode.jsのAPIが利用できます。前回のwindow.jsより、テキストボックス(Urlin)に関する操作をこちらに移動しました。3-5行はpreload.jsと同じく、evalとBufferを無効にしています。7,8行にて、プロセス間通信のためのオブジェクトをIpcRendererに取得しています。テキストボックスでキーが押されたときの処理が14-16行です。メソッドを使い、window.jsのプロセスにメッセージでURL文字列を送っています。20-22行では、逆に、window.jsのプロセスからのメッセージを受け取っています。onメソッドの第一引数がチャネル名、第二引数がコールバック関数ですが、そのコールバック関数の第二引数以降がメッセージの引数になります。ここでは全てのメッセージで一つの引数しか使っていませんが、メッセージには複数の引数を指定できます。実行して確認ここまで書いて、ようやく第一回と同じ機能が、割りとセキュアに実装できました。(微妙な表現ですが...。)実行して、同様の動作をすることを確認しました。課題コードの量としては多くはないのですが、Web記事などを参照しながらゆっくりと試作をしてみて、課題が見えてきました。サンドボックス化の範囲まず、ここまで「ヘッダー部を丸ごとサンドボックス化」してきました。目的のものが一般的なWebブラウザであれば、外部リソースを流し込むところは、URLを表示するテキストボックスと、ページのタイトルを表示するタイトルバー或はタグです。(プロセス間通信のような)難しいことをせずに、この2つの部分だけをサンドボックス化すればよいのでしょう。天邪鬼といいますか「ヘッダーそのものが隔離されている」という構造が何か面白いのでこのまま進めようと思います。エスケープ現在のところエスケープの処理は実装していません。タイトルを表示する部分はHTMLエスケープですが、実際のところwebviewのAPIがタイトルをエンコードして返すのか、そうでないのかわかりません。実装するときに調べてみることになります。内部でHTMエスケープをしてくれるjQueryのtext()メソッドを使うだけかもしれません。(この可能性が高い...。)またURLのテキストボックスに入るのはURLだけですので、エスケープというよりはURLとしての検証をするのでしょう。今回書いてみたレンダラーとwebviewのプロセス間通信では、それぞれにおいて書き方が、微妙に異なりました。このあたりは、CORBAのなどリモートプロシージャコールっぽく書けるようにライブラリ化すると見通しがよいかもしれません。今回のサンプルだけでも、レンダラーのHTMLとwebviewが2つと、3つの初期化が走ります。初期化完了の同期をちゃんと考えたほうが良いですね。上記ライブラリ化の仕方によっては、さらに複雑になりそうです。(スタブを動的に登録とか。)ユーザー操作イベント前回、今回とテキストボックスに対するキー入力イベントは、addEventListenerで登録しました。ヘッダー内でのスクリプトの実行を有にしましたので、HTMLのonkeypress属性などに関数コールを書いてよいのかもしれません。前節の問題と関連して、初期化が完全に終わるまでユーザーの操作を有効にしないことまでを考えると、以下の2方式が考えられます。初期化が終わって最後にaddEventListenerでイベントリスナーを登録する。HTMLでは操作部をdisableに書いておき、初期化が終わったらenableにする。(今のところ後者ですかね。id=”view-container”>」ってのはいらなかったですね。Webページですと、ブラウザの幅が大きい時に、ページの幅をそこまで広げないようにbodyの下にコンテナを入れます。(他にも理由があるかも。)しかしアプリですと、ウィンドウのサイズ自体を制限できますし、中身はほぼ「width:100%」です。bodyの下のコンテナは必要ありません。view-containerの方ですが、当初はコンテナの中に複数のwebviewを開きタブブラウザにするイメージでした。今は、レンダラーのウィンドウ自体をタブで切り替えるイメージです。view-containerも必要なし。最初からそう思っていれば、CSSはもう少しだけシンプルでした。まとめ以上、webviewによるサンドボックスとその他のセキュリティ項目の実装でした。今回でようやくフィージビリティスタディが終わった感じです。今回のコードもgithubに上げましたのでご参照ください。セキュリティチェックリストもまとめておきました。次回は、アプリの色の話を一回挟んで、ヘッダー部のデザインをしたいと思います。コメントをキャンセルメールアドレスが公開されることはありません。