はじめに
- Electronいいわー
- ChromiumとかNode.jsの知見もあるといいらしい *1
- でもChromiumとかようわからんぜ
この際勉強しちまえばいいじゃん!
本エントリはElectronを構成する要素の一つ、Chromiumのアーキテクチャについて初心者が入門してみたものです。
Chromiumドキュメントを読む
Chromiumの仕組みを優しく解説した本やエントリはざっと探した限りはありません。
そういうのが必要な人がふれる領域の話ではないのでしょう。
必然的に英語で書かれたドキュメントを読むことになります。
Chromiumプロジェクトはリンクの樹海のようになっていて迷いそうです。
Developer向けドキュメントは以下のリンクを辿って行くとみつかります。
For Developers > Design Documents - Engineering design docs
これまた多数のドキュメントがありますが、本エントリはそのうちのほんのいくつかをかいつまみながら話を進めます。
Multi-process Architecture
Sandbox
Security Architecture
興味深いドキュメントが他にも多数あるので時間を作って読んでみたいですね。
ElectronはChromiumベースである
このことはElectronドキュメントのQuick Startでもさらっと触れられていますし、ご存知のとおりかと思います。
Since Electron uses Chromium for displaying web pages, Chromium's multi-process architecture is also used.
ここでさらっと説明されているChromiumのマルチプロセス - BrowserプロセスとRendererプロセスからなる - アーキテクチャを、ChromiumベースであるElectronもまた利用しています。
固有名詞を使わずに述べるならば、
- アプリケーション本体を管理するプロセス
- 画面を描画するプロセス
これら2つが明確に役割分担されたChromiumのアーキテクチャを利用しているということです。
これら2つのプロセスはElectronにも、Rendererプロセスはそのままの名前で、そしてBrowserプロセスはMainプロセスに名前を変えて、引き継がれています。
Electronを触り始めた当初、このアーキテクチャはElectron独自のものだと思っていたのですが、それは勘違いだったということですね。
2つのプロセス
ここでChromiumドキュメントにあるアーキテクチャ概略図を見てみましょう。
かなりざっくり説明すると、
- Browserプロセスはひとつだけ。
- Browserプロセスに対してRenderプロセスは複数。
- Browserプロセスとはipcを通じて各Rendererプロセスとコミュニケーションをとっている。
ということです。
2つのプロセスの関係はドキュメント中で次のように説明されています。
In general, each new window or tab opens in a new process. The browser will spawn a new process and instruct it to create a single RenderView.
「新しいウィンドウやタブには新しいプロセスが作られる。Browserプロセスが新しいプロセスを作って、それをRendererプロセスに割り当てる。」
「新しいプロセス」というのはもちろんRendererプロセスのことです。
新しいタブやウィンドウを開く権限=新しいRendererプロセスを作る権限はBrowserプロセスが持っています。Rendererプロセスが自身の権限で新しいタブを開くことはできません。
Electronでも新しいウィンドウを作るAPIはRendererプロセスではなくMainプロセスが持っていますね。
ElectronのRendererプロセスから新しいウィンドウを作りたいとき、Mainプロセスにプロセス間通信でお願いをするように、ChromiumのRendererプロセスも、window.open()なんかが実行されるときには、ipcを経由してBrowserプロセスに新しいウィンドウを開いてくれるようお願いをするわけです。
なんだか回りくどいようですが、役割や権限が2つのプロセスで明確に分断されていることがChromiumアーキテクチャのを理解するうえでのカギとなります。
- Browserプロセスは強い権限を持っている。
- 反対にRendererプロセスは権限が弱い。
以下は各プロセスが担っている代表的な処理です *3。Rendererが画面描画を専任するのに対し、Browserプロセスがブラウザのバックエンドを運用している感じがわかると思います。
Rendering Engine | Browser Kernel |
---|---|
HTML parsing | Cookie database |
CSS parsing | History database |
Image decoding | Password database |
JavaScript interpreter | Window management |
Regular expressions | Location bar |
Document Object Model | Network stack |
Rendering | SSL/TLS |
なぜそんなアーキテクチャになっているのか
なぜChromiumがそのようなアーキテクチャを採用しているのかはドキュメントで次のように説明されています。
We use separate processes for browser tabs to protect the overall application from bugs and glitches in the rendering engine. We also restrict access from each rendering engine process to others and to the rest of the system. In some ways, this brings to web browsing the benefits that memory protection and access control brought to operating systems.
「レンダリングエンジンに起因するバグや欠陥からアプリケーション全体を守るため、ブラウザのタブを担当するプロセスは隔離している。それに加えて各Renderプロセスの持っているレンダリングエンジン同士はもとより、システムのその他の部分にもアクセスできないようにしている。」
Chromiumの中でRendererプロセスはサンドボックス化されており *2、他のプロセスを直接使ったり、システム内のファイルにアクセスしたりできません。つまりBrowserプロセスに比べて弱い権限しか与えられていません。
なぜそうしているのか?そこにセキュリティ上の合理性があるからです。
サンドボックスに脆弱性を押し込める
一口にRendererプロセスといってもその内部では様々なコンポーネントが働いています。代表的なものとして、HTMLパーサーやJavascriptエンジン、そしてDOMなどが挙げられます。ChromeではBlinkやV8に当たるものですね。それらは非常に複雑に出来ており、これまでの傾向から見てもブラウザの脆弱性の元になることが多かったようです。
そういったブラウザの「弱点」とも言えるRendererプロセスをサンドボックスに閉じ込めることは、ブラウザの脆弱性を突く攻撃を受けたときの被害をサンドボックスに閉じ込めるということです。
攻撃者がRendererプロセスに対する攻撃を行っても、Rendererからは他のプロセスやコンピュータのファイルシステムにアクセスする権限がないため、そこで攻撃が食い止められる(はず)ということです。
別の言い方をすれば、攻撃を受けた場合のリスクが高いBrowserプロセスは後方に控え、Rendererプロセスを戦場の最前線に立たせているわけです。
Rendererプロセスのこんな声が聞こえてきそうですね。
「俺はただの鉄砲玉さ。あのお方(Browserプロセス)の元へは辿りつけやしない...。」
まとめ
Electronに入門してみた的なスライドでは、よく「ElectronはChromiumにNode.jsを組み合わせたものだ」的な図が登場します。元は@zcbenzのスライドからだと思います。
Electronを知った当初は「つまりNode.jsが使えてうれしいってことね」と思っていたこの図も、以上のようにChromiumに入門するだけで見え方が変わってきます。
例えば、なぜ一つ一つのRendererがNode.jsを持っているのか。それは各Rendererプロセスはサンドボックス化されていてリソースを共有しないから、と考えられます。
また、プロセスにNode.jsが組まれていることも、本質的には、ChromiumではC++で書かれているプロセス制御のロジックを、Javascriptで書けるようにしている、という意味があるのだとわかります。
低レイヤの部分をほんの少し知ると見える景色が変わってくる気がします。
上のほうではRendererプロセスに対し「鉄砲玉」という呼び方をしてしまいました。しかし別の視点から見れば、Browserプロセスが基幹部分を請け負っているから、Rendererプロセスは画面描画に集中できて、そこに僕の好きなフロントエンドの世界があるわけで...。
これまで何気なく書いていたこんなコードも、アーキテクチャを理解した後だと感慨深く見えてきますね。
var win = new BrowserWindow({ width: 800, height: 600 });
*1 Real World Electron Development より
*2 Chromiumのスタート時オプションによって変更可能なようです:Process Model
*3 ドキュメントのひとつ、Security Architectureより引用。ドキュメントというか、セキュリティの概要についての論文。
その他参考
The Chrome Sandbox Part 1 of 3: Overview