0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

TauriAdvent Calendar 2024

Day 8

アプリ開発者のにとっての Tauri v2 のセキュリティ構造を読み解く

Last updated at Posted at 2024-12-07

はじめに

Tauri v2 にバージョンが上がり、モバイル (iOS/Android) 対応が正式にサポートされた。
また、プラグインに関して大幅なサポート変更があり、Tauri のコア部分までアクセスできるものを作成できるようになった。
そして、Tauri v1 の学びから、高度なセキュリティサポートが明確になっていき、こうして Tauri v2 のセキュリティサポートは大幅な変更と対応拡張が行われたという経緯がある。

公式ドキュメント

まずは公式ドキュメントをぺたり。
これを読み解きつつ、特に注意したり、自分が使うときにどう設定すればいいかを考えながらまとめていく。

あくまで、個人の見解とメモとして下部にまとめていくので、実際にセキュリティを気にしつつ開発にとりかかる場合は公式ドキュメントを必ず参照する様。

トラスト境界 (Trust Boundaries) を意識する

トラスト境界 (Trust Boundaries/トラストバウンダリ) は、プログラムデータや実行が「信頼」のレベルを変更する境界、または異なる機能を持つ2つのプリンシパルがデータまたはコマンドを変換する境界のこと。

つまり、ここでの「2つのプリンシパル」は主要な「フロントエンド (System WebView)」と「バックエンド (Application Core)」のそれぞれのプロセスを指す。

これらの境界の通信が監査され、境界が強く守られることによって、例えば、なんらかの IPC 通信をするプログラムをフロントエンドで動かしていた際に、不要なコマンドによる重要なバックエンド処理を不用意に実行させない様に守ることができる。

image.png

注意点として、この話は境界のみを扱うという点がある。
それぞれの境界内での操作に関しては一切制約を受けない。
となると、例えばバックエンド側で悪意のあるプログラムが仕込まれた場合、OS レベルで不要にアクセスできてしまう事になる。

認証を必要とするレベルのデータ情報をどこで持つか
このトラスト境界を意識して一つ浮かんでくるのが、認証情報を必要とする秘匿レベルの情報を、フロントエンドのみで行うのか、バックエンドのみで行うのか、はたまた両方を上手く活用して行うのかということ。

「この境界は、あえて許可するまでは基本的に破られることがない。」とすると、例えばファイルアクセス一つをとっても両方での操作を兼ね合わせて、「意図的に」トラスト境界を通して情報を渡しあことの方が安全ではないか。

例えば、サーバーサイドでしか行わないようなアクセスは、フロントエンドだけで解決するのではなく、同様に、バックエンド側と責任を分けるべきと考えられる。

この考えを元に境界をコントロールする権限を「意図的に」与えていくことが必要になるが、その機能として Tauri は次の項目を提供している。

  • Permissions (権限)
  • Command Scopes (コマンドスコープ)
  • Capablities (能力)
  • Runtime Authority (ランタイム支配権)

つまり、Tauri での開発者がこれらの設定を強制させられるのは、このセキュリティを測らずとも意識し、不要なセキュリティ事故を起こさないようにするため。
めんどくさがらずに対応することで、必然と、ある程度危険な実装を避けられるように出来る。

これら4つのセキュリティ機能に関しては下部で別途まとめる。

今回のこのポストに関しては、理解に紛らわしさを排除するため「アプリ開発」における設定のみに焦点を定める。

しかしながら、実際のところプラグイン開発に関してもほとんど構成は同様なので、とりあえず「アプリ開発」におけるこれらのセキュリティ機能の役割を理解しておけば、あとはドキュメントを読んで補完するだけで対応できるだろう。

Permissions (権限)

Permissions は、「フロントエンド内で呼び出す『コマンド』に対するアクセス権」を説明するものとなっている。
なので、フロントエンド側から、何かしらファイルアクセスを行おうとしたり、Tauri の Window 機能にアクセスしようとするプラグインを実装する場合は、こちらで制御を行う。

端的に行ってしまえば、 「プラグインや Tarui 側自体が提供するもの」 であり、 「ユーザーがそれらの提示された Permission を選んでアプリに設定するもの」 という立ち位置。

Tauri やプラグイン側からすると、 「開発者側は、プラグインとしてこういうアクセス権限レベルの機能を実装したから Permission として設定しておくよ。アプリ側で使う場合はそれに同意・許可して (設定して) から使ってね。」 という事になる。

プラグイン開発者は、この Permissions を複数定義する事ができ、それをアプリ開発者は利用して設定する形になる。

例えば、 "read_file"Autostart プラグイン"autostart:allow-enable""autostart:allow-disable""autostart:allow-is-enabled" などはこちらで定義されたものを使用している。

Breakdawn
Permissions は、開発者・ユーザー間での機能開発・利用の同意書のようなもの。
同意書を Permisions として作成し、その同意書にサイン(設定に書く)ことによってコマンドからのアクセス権を明示的に許可したということになる。

こうした工程を踏まえる事で、アプリ開発者側はフロントエンド側で利用するコマンドに不要なアクセスをされなくて良く、アプリ提供する際のセキュリティとしてアプリユーザー側に一定の安全性を提供できる。
逆にプラグイン開発者側は、あらゆるコマンドの機能をプラグインに押し込めることができ、Permission として利用の判断をアプリ開発者側に委ねることができる。

特に押さえておくべき視点としては、セキュリティに厳格であればあるほど、 「アプリ開発者側が Tauri やプラグイン側がその Permission を設定した範囲がどこにどうアクセスしているものかを把握しておく」 ことが必要になってくる。

ファイル構成

Permission ファイルは、下記の様に、現状は TOML ファイルのみで構成される。

[[permission]]
identifier = "my-identifier"
description = "This describes the impact and more."
commands.allow = [
    "read_file"
]

[[scope.allow]]
my-scope = "$HOME/*"

[[scope.deny]]
my-scope = "$HOME/secret"

このファイルはプラグインを作成する場合は permissions いかに配置される。

tauri-plugin
├── README.md
├── src
│  └── lib.rs
├── build.rs
├── Cargo.toml
├── permissions  <----------------------- コレ
│  └── <identifier>.json/toml
│  └── default.json/toml

この中でも一番特殊なファイルは 「default(.json もしくは .toml ファイル)」 という名前になっており、このデフォルト Permission は、tauri コマンドを通してプラグインをインストールすることによって、アプリケーションのコンフィグである、capabilities の方に追加される。

tauri-app
├── index.html
├── package.json
├── src
├── src-tauri
│   ├── Cargo.toml
│   ├── permissions
│      └── <identifier>.toml
|   ├── capabilities  <------------------ コレ
│      └── <identifier>.json/.toml
│   ├── src
│   ├── tauri.conf.json

アプリケーション開発者は、この capability ファイルに関しては、json5 形式でも記述可能。
これに関しては追加設定等が要るため、別途記事としてまとめる。

Permission のコンフィグリファレンス

Tauri 側で用意されている Core Permission のリファレンス

例えば、主にプラグイン開発側で Tuari のデフォルト権限に制限をかけたりする場合は、こちらを参照して設定していく。

Command Scopes (コマンドスコープ)

Scope は、コマンドに対してより粒度の小さいレベルで振る舞いを定義するためのセキュリティ機能となっている。

Scope のタイプには、allowdeny が存在する。
また、deny スコープの設定は、常に allow 設定よりも優先される
同様の内容が記述されていて、動作してないといった場合は、deny 設定に注目しておくと良い。

具体的な使用例としては、ファイルアクセスに使用する Fs プラグインがあげられる。
Fs プラグインの場合は、「どこの階層にアクセスできるようにするか」をアプリ開発者側に委ね、それを設定させる事によって許可したとみなす。
http プラグインの場合は、どの URL にのみリーチ可能かどうかという設定をさせ、許可させる。

コマンド開発者は、この Scope をバイパス(設定を無視)できないように作成していることを確認する事が必要。
また、その Scope 設定が正確なものかは慎重に検証する必要がある。
(例えば、ルートディレクトリ「'/'」を Scope 対象にしていたり、全アクセス Scope 対象「"**"」は選んばせないといった実装にするなど。

Scope のコンフィグリファレンス

Capabilities (能力)

Capabilties は、アプリケーションや Plugin において、フロントエンドシステムである WebView にアクセス権限の細かく有効化、および制限するもの。

つまりは、実際に Permission を使用する際にはここで設定する。
故に、アプリ開発者にとっては、必然的に一番この Capabilities を触る機会が多くなる様になっている。

Capabilities と Permissions の関係性の整理
「Capbabiliteis によって Permissions を設定する」という立ち位置でおさえておくのが分かりやすい。

ファイル構成

Capability の設定ファイルは、現状、JSON/JSON5 か TOML で記述できるようになっており、src-tauri/capabilities ディレクトリの中で管理される。

tauri-app
├── index.html
├── package.json
├── src
├── src-tauri
│   ├── Cargo.toml
│   ├── capabilities  <------------------ コレ
│      └── <identifier>.json/toml
│   ├── src
│   ├── tauri.conf.json

Tauri 側としては、推奨する形は capabilities として個別に管理するようにしておき、それを tauri.conf.json ファイル側でリファレンスする形をとるようにする形態。

以下が設定例。
アプリ開発者は、まずは 「default.json」という定義づけられた名前のデフォルト Capability ファイルを設定する事になる。

src-tauri/capabilities/default.json
{
  "$schema": "./schemas/desktop-schema.json",
  "identifier": "main-capability",
  "description": "Capability for the main window",
  "windows": ["main"],
  "permissions": [
    "core:path:default",
    "core:event:default",
    "core:window:default",
    "core:app:default",
    "core:resources:default",
    "core:menu:default",
    "core:tray:default",
    "core:window:allow-set-title"
  ]
}

そしてこの、Capability を tauri.conf.json 側で参照するように設定する。

src-tauri/tauri.conf.json
{
  "app": {
    "security": {
      "capabilities": ["my-capability", "main-capability"]
    }
  }
}

Capabilities を直接 tauri.conf.json に定義する
推奨する形はリファレンスする形だが、もし、アプリがミニマムな開発携帯であったりする場合などは直接 Capability を設定できるようになっている。

src-tauri/tauri.conf.json
{
  "app": {
    "security": {
      "capabilities": [
        {
          "identifier": "my-capability",
          "description": "My application capability used for all windows",
          "windows": ["*"],
          "permissions": ["fs:default", "allow-home-read-extended"]
        },
        "my-second-capability"
      ]
    }
  }
}

これを分離する基準としては、マルチプラットフォーム対応時などが挙げられるだろう。
mobile 用のコンフィグなどを作る際には、src-tauri/tauri.ios.conf.jsonsrc-tauri/tauri:android.conf.json. など分けていったりするが、これらにそれぞれのセキュリティ設定を埋め込んでいくのは管理が難しくなってしまう。
あくまで、「ミニマムな管理ができる」程度の状況においてこの方法を使用するようにしよう。

Capability をプラットフォームごとに分ける

例えば、デスクトップアプリ版とモバイル版、モバイル版でも iOS 版、Android 版と分けたい状況が出てくると思う。
そういった場合には、Capability を分けて管理できるようになっている。

例えば、デスクトップ版の場合はこう。

src-tauri/capabilities/desktop.json
{
  "$schema": "./schemas/desktop-schema.json",
  "identifier": "desktop-capability",
  "windows": ["main"],
  "platforms": ["linux", "macOS", "windows"],
  "permissions": ["global-shortcut:allow-register"]
}

モバイル版の場合はこう。

src-tauri/capabilities/mobile.json
{
  "$schema": "./schemas/mobile-schema.json",
  "identifier": "mobile-capability",
  "windows": ["main"],
  "platforms": ["iOS", "android"],
  "permissions": [
    "nfc:allow-scan",
    "biometric:allow-authenticate",
    "barcode-scanner:allow-scan"
  ]
}

さらにこれを、プラットフォーム事に分けた tauri.conf.json で指定する事によって、この Capability の切り分けた先を指定、コントロールすることができる。

リモート API アクセス

Tauri は、デフォルトでは、Tauri にバンドルされている API にのみアクセスできるように作成されている。
しかしながら、API によっては、サーバー側で実行するタイプのものを使用する場合も出てくるだろう。そのための権限設定もできる様になっている。

例えば、NFC タグをスキャンして、バーコードスキャナを使用できるようにする設定ファイルが以下。
tauri.app のあらゆるサブドメインへ API アクセス可能にしており、そこから呼び出す設定をしている。

{
  "$schema": "./schemas/remote-schema.json",
  "identifier": "remote-tag-capability",
  "windows": ["main"],
  "remote": {  <--------------------------------コレ
    "urls": ["https://*.tauri.app"]
  },
  "platforms": ["iOS", "android"],
  "permissions": ["nfc:allow-scan", "barcode-scanner:allow-scan"]
}

その他の想定される例
例えば、決済システムをシステム側がプラグインとして利用する場合、こちらを設定すると言ったことも考えられる。

Capability のコンフィグリファレンス

Runtime Authority (ランタイム支配権)

Runtime Authority は Tauri のコアシステムの一つで、Permissions、Capabilties、Scopes をランタイム時に扱うシステムとなっている。

アプリ開発者やプラグイン開発者は直接ここを意識することは少ないが、セキュリティに関してじっくり調査をする際には、こちらを解析していくことになる。

image.png

Content Security Policy (CSP)

Tauri はコンフィグ設定することによって、CSP を制限できる様になっている。
例えば cross-site-scripting (XSS) などのの一般的な Web ベースの脆弱性の影響を軽減、または防止できるようにしている。

ローカルのスクリプトはハッシュ化され、スタイルと外部スクリプトは nonce で暗号化して使用されるように設定されている。

Tauri は、余計な攻撃ベクトルを追加させないよう警告しており、例えば、CDN に直接アクセスするようなコードは書かないように呼び掛けている。
また、同様に、信頼できないファイルも含めないように警告している。

コンフィグ側で設定されない限りは CSP が有効になることはないので注意。

src-tauri/tauri.conf.json
  "csp": {
        "default-src": "'self' customprotocol: asset:",
        "connect-src": "ipc: http://ipc.localhost",
        "font-src": ["https://fonts.gstatic.com"],
        "img-src": "'self' asset: http://asset.localhost blob: data:",
        "style-src": "'unsafe-inline' 'self' https://fonts.googleapis.com"
   }

HTTP Headers

こちらは Tauri v2.1.0 から有効になったもの。

Tauri の WebView に対する HTTP アクセスを行う際は、設定ファイル上で制限できる。

  • Access-Control-Allow-Credentials
  • Access-Control-Allow-Headers
  • Access-Control-Allow-Methods
  • Access-Control-Expose-Headers
  • Access-Control-Max-Age
  • Cross-Origin-Embedder-Policy
  • Cross-Origin-Opener-Policy
  • Cross-Origin-Resource-Policy
  • Permissions-Policy
  • Timing-Allow-Origin
  • X-Content-Type-Options
  • Tauri-Custom-Header

設定例

src-tauri/tauri.conf.json
{
  "app":{
    "security": {
      "headers": {
        "Cross-Origin-Opener-Policy": "same-origin",
        "Cross-Origin-Embedder-Policy": "require-corp",
        "Timing-Allow-Origin": [
          "https://developer.mozilla.org",
          "https://example.com",
        ],
        "X-Content-Type-Options": null, // get アクセスは無視
        "Access-Control-Expose-Headers": "Tauri-Custom-Header",
        "Tauri-Custom-Header": {
          "key1": "'value1' 'value2'",
          "key2": "'value3'"
        }
      },
      // CSP は以下の様に分けて定義される
      "csp": "default-src 'self'; connect-src ipc: http://ipc.localhost",
    }
  }
}

先の CSP 設定項目とは別れていることに注意する。

Application Lifecycle Threas (アプリケーションライフサイクルの脅威)

開発者は、アプリケーションライフサイクルの過程の中でそれぞれ脅威が紛れ込むという事を意識すべきである。

大きく分けて以下の様に分類できる。

  • Upstream (上流工程)
  • Development (開発)
  • Building (ビルド)
  • Distribution (配布)
  • Runtime (ランタイム)

image.png

Upstream Threats (上流工程の脅威)

  • 常にアプリケーションを最新にしておくこと

    • Tuari を使ってリリースしているということは、常に Tauri のライブラリも込みでリリースしているということも意識しておくこと
    • Tauri に脆弱性がある場合は、セキュリティの危険にさらされることになるため、最新を心がけ、セキュリティアップデートを受けることが推奨される
    • また、rustc や nodejs なども最新にするように心がける
  • 依存関係を常に評価しておくこと

    • NPM や Crate.io は数多くの便利パッケージを配布しているが、それらを使用するのは使用者の責任であることを意識しておく
    • もしメンテナンスされていない、期限切れのライブラリを使っている場合は、常に危険レベルがあがった状態で晒されることに注意する
    • なるべく npm auditcargo audit を駆使し、これらに対応することを薦める

Development Threats (開発時の脅威)

  • 開発サーバーに対する注意
    • Tuari の WebView に対するフロントエンドフレームワークは自由であるため、それぞれのフレームワークがどういった開発環境において動いているかに注意しておく
    • これらの開発サーバーとの通信は基本的には暗号化されているはずだが、場合によって悪意あるプログラムが紛れ込む可能性もゼロではない
    • また、使用するライブラリによっては、その開発サーバーを悪用する可能性があることも注意しなければならない

現時点で、Tauri 開発サーバーは相互認証とトランスポート暗号化をサポートしてないため、信頼できないネットワークでは使用しないこと。

  • 堅牢な開発マシンでの開発を心がける
    • 管理者アカウントを使用しない
    • 開発マシンで、プロダクションの秘密情報を扱わない
    • ソースコードのバージョンコントロールに秘密情報が紛れ込まない様に注意する
    • セキュリティハードウェアトークンなどを使用して、侵害されたシステムの影響を軽減する
    • システムを常に最新にする様に心がける
    • インストールしているアプリケーションがメンテナンスされていることに注意する

より詳しい開発システムの堅牢かは awesome-security-hardening を参照する事。

  • ソース管理の認証と承認を確実にする
    • 大勢の開発メンバーで開発している場合、ソースコードのバージョンコントロールやサービスは、利用状況がセキュアであることを常に注意しておく

Building Threats (ビルド時の脅威)

  • CI/CD を使う際には、それらのリモートシステムが完全に信頼できるものであることを確認する必要がある
  • バイナリへのサインは、プラットフォーム事に行う事を薦める
    • 多少コストがかかる方法になってしまうが、出自をコントロールする上では有効であると期待できる

Tauri は、サポートの一環として GitHub Wokrflow のテンプレートを提供している。

  • 悪意あるリリースに署名するため暗号秘密情報が使われることしまうことに注意する
    • ビルドシステム側の秘密情報がハードウェアトークンに適切に保存されている場合は署名キーが漏洩することはできないが、別の悪意あるリリースにそれらが使われてしまう事がある
  • ビルド環境を再現可能にしておく
    • バックドアインジェクションなどに対抗するためには、ビルド環境を再現可能にしておき、ビルドアセットが必ず別の環境のビルドとも一致しているか比較できる様にしておく
    • これらの最初の問題として、「Rust がデフォルトで完全に信頼できる再現可能なビルドを生成していない」というものがある
      • 理論上はこれらをサポートしているが、まだバグがあることに注意
      • この問題に関しては Rust の public bug tracker を参照するようにする
    • 次点の問題として、多くの一般的なフロントエンドバンドらも再現可能な出力を生成しないので、バンドルされたアセットによってはビルドが壊れる可能性がある
    • つまり、現状は、デフォルトでは、再現可能なビルドに完全に依存することができない状況であり、ビルドシステムを完全に信頼しておく必要があることに注意する

Distribution Threats (配布時の脅威)

  • マニフェストサーバー、ビルドサーバー、またはバイナリホスティングサーバーの制御を心がける
    • Tauri 側は、アプリへのホットアップデートの配布を可能な限り簡単かつ安全行えるようにしているが、これらがコントロールされない場合はそういった安全策も台無しになってしまう
    • もし自らでビルドシステムを組む場合は、プロフェッショナルな OPS アーキテクトに相談することを薦める

Runtime Threats (タンタイムの脅威)

  • 基本的に WebView は安全でないと想定する
    • Tauri は、信頼できないアクセスを防ぐため、WebView に対して制限を設けている
    • そのシステムの一環として CSP があり、また、Capability 設定がある
    • これらを上手く活用していくことが大事

協調開示

このポストはアプリ開発時にどういったセキュリティ設定が必要かを扱うが、こちらに関してもあえて、ここで扱う。

Tauri 側は、何かしらセキュリティインシデントを見つけた際には 公の場でそれを公開しないように呼び掛けている
なので、下手に何かしら問題を見つけた際には、公開された GitHub Issue や、はたまた SNS 上にポストするのではなく、Github Vulnerability Disclosure や代替として開発チームへの直メール (上記リンク先を参照) をする様に薦めている。

下手にセキュリティリスクを拡散してはいけない。

まとめ と 最後に

アプリ開発者としてのこれらのセキュリティ事項の設定の参照は、以下の様に辿っていくと開発時にもわかりやすい。

さらにこれを参照したうえで、それぞの設定を熟知し、さらに Application Lifecycle Threas や CSP など周辺のセキュリティを読み解いていくと理解がしやすいだろう。

Tauri v2 になり、プラグインもコア機能まで降られるようになった故、さらにセキュリティ設定が柔軟になったが、ここまでを踏まえると、アプリ開発者はどこを設定していく様に考えればいいのかや、どういったことを意識すれいいのかが徐々に紐解けるだろう。

また、もちろんこれだけではまだまだ十分でないと思われるが、Tauri のセキュリティ事項のそれぞれの部分はセキュリティ意識を付けるうえでも結構いい教科書になっているのではないかと思う。
開発していく中でこれらを意識して日々の開発に就ければと思った。

まだまだ今後も Tauri v2 でもセキュリティ事項はアップデートされる様なので、注視しておきたい。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?