本文へスキップ

バリアの突破:サンドボックスによるアプリの強化

·読了時間4分

CVE-2023-4863:WebPにおけるヒープバッファオーバーフローが公表されてから1週間以上が経過し、webp画像をレンダリングするソフトウェアの新しいリリースが相次いでいます。macOS、iOS、Chrome、Firefox、およびさまざまなLinuxディストリビューションがすべて更新を受けました。これは、Citizen Labによる調査に続き、ワシントンD.C.を拠点とする市民団体が使用していたiPhoneが、iMessage内のゼロクリック攻撃を利用した攻撃を受けていたことが判明したことに伴います。

Electronもすぐに対応し、同日に新バージョンをリリースしました。アプリでユーザー提供コンテンツをレンダリングする場合は、Electronのバージョンを更新する必要があります。v27.0.0-beta.2、v26.2.1、v25.8.1、v24.8.3、およびv22.3.24にはすべて、webp画像のレンダリングを担当するライブラリであるlibwebpの修正版が含まれています。

「画像のレンダリング」という無害な操作も潜在的に危険なアクティビティであることを改めて認識した今、Electronには、次の大きな攻撃(それがどのようなものであれ)の影響範囲を制限するプロセスサンドボックスが備わっていることを思い出させておきましょう。

サンドボックスはElectron v1から利用可能で、v20からデフォルトで有効になっていますが、多くのアプリ(特に長期間運用されているアプリ)では、コードのどこかにsandbox: falseがあるか、nodeIntegration: trueがあり、明示的なsandbox設定がない場合、同様にサンドボックスが無効になっている可能性があります。これは理解できます。長年Electronをご利用いただいている場合は、HTML/CSSを実行するコードにrequire("child_process")またはrequire("fs")を自由に使用できた利便性を享受していたことでしょう。

サンドボックスへの移行方法について説明する前に、まず、サンドボックスが必要な理由について説明します。

サンドボックスはすべてのレンダラプロセスを厳密に保護し、内部で何が起こっても、コードは制限された環境内で実行されるようにします。この概念はChromiumよりもはるかに古く、主要なオペレーティングシステムすべてで機能として提供されています。ElectronとChromiumのサンドボックスは、これらのシステム機能の上に構築されています。ユーザー生成コンテンツを表示しない場合でも、レンダラが侵害される可能性を考慮する必要があります。サプライチェーン攻撃のような高度なシナリオから、小さなバグのような単純なシナリオまで、レンダラが意図しない動作をする可能性があります。

サンドボックスは、そのようなシナリオをはるかに安全なものにします。内部のプロセスは、CPUサイクルとメモリを自由に使用できます。それだけですが。プロセスはディスクに書き込んだり、独自のウィンドウを表示したりすることはできません。今回のlibwepのバグの場合、サンドボックスは攻撃者がマルウェアをインストールまたは実行できないようにします。実際、従業員のiPhoneに対する最初のPegasus攻撃では、攻撃は具体的にサンドボックス化されていない画像プロセスをターゲットにして、通常はサンドボックス化されているiMessageの境界を最初に突破することで、電話へのアクセス権を取得しました。この例のようなCVEが発表された場合でも、Electronアプリを安全なバージョンにアップグレードする必要がありますが、その間、攻撃者が実行できる損害の量は大幅に制限されます。

標準的なElectronアプリケーションをsandbox: falseからsandbox: trueに移行することは、容易ではありません。私もそう思います。なぜなら、Electronセキュリティガイドラインの最初のドラフトを自分で書いたにもかかわらず、自分のアプリを移行できていなかったからです。週末にそれが変わりましたので、皆さんにも変更することをお勧めします。

Don’t be scared by the number of line changes, most of it is in package-lock.json

取り組むべきことが2つあります。

  1. preloadスクリプトまたは実際のWebContentsでNode.jsコードを使用している場合は、そのNode.jsとのやり取りをすべてメインプロセス(または、高度な方法であればユーティリティプロセス)に移動する必要があります。レンダラの能力が高まっていることを考えると、コードの大部分は実際にはリファクタリングする必要がない可能性が高いです。

    プロセス間通信に関するドキュメントを参照してください。私の場合、多くのコードを移動し、ipcRenderer.invoke()ipcMain.handle()でラップしましたが、プロセスは簡単で、すぐに完了しました。ここでAPIに注意してください。executeCodeAsRoot(code)というAPIを構築した場合、サンドボックスはユーザーをあまり保護しません。

  2. サンドボックスを有効にすると、preloadスクリプトでのNode.js統合が無効になるため、require("../my-script")を使用できなくなります。つまり、preloadスクリプトは単一ファイルにする必要があります。

    そのためにはいくつかの方法があります。Webpack、esbuild、parcel、rollupのいずれも機能します。私はElectron Forgeの優れたWebpackプラグインを使用しましたが、同様に人気のあるelectron-builderのユーザーはelectron-webpackを使用できます。

全体として、このプロセス全体に約4日間かかりました。これは、Webpackの巨大な能力をどのように制御するかを考えながら頭を悩ませた時間と、他の方法でもコードをリファクタリングする機会を利用した時間も含まれています。