ハードウェア固有ID (UUID) を取得する

machine-uid クレートで OS のマシン固有 ID を取り、ハッシュ化してフロントへ渡す。ライセンス認証での注意点と、自前の UUID を保存する代替案も示す。

システム情報 対象: Tauri 2.x 更新日: 読了目安: 約6分 sys-024
目次
  1. 前提条件
  2. 1. フロントエンドから実装する (TypeScript)
  3. 2. バックエンドから実装する (Rust)
  4. 各 OS で何をもとにした ID か
  5. 代替案: 初回起動時に UUID を生成して保存する
  6. 動作確認
  7. よくあるエラーと対処法
  8. OS ごとの違いと注意点
  9. 関連レシピ

「1 ライセンスにつき 3 台まで」のような端末数制限や、クラッシュレポートの端末単位の集計には、ホスト名より変わりにくい ID が必要です。各 OS はインストール時に生成した固有 ID を持っており、Rust の machine-uid クレートで 1 行で取得できます。ただし出どころは OS ごとに違い、ハードウェアに紐づくのは macOS だけです。何をもとにした ID か、生の値を送ってはいけない理由、初回起動時に自分で UUID を作って保存する代替案も示します。

前提条件

src-tauri で次を実行します。sha2 はハッシュ化用、uuid は代替案用です。

cargo add machine-uid sha2 uuid --features uuid/v4

代替案で Store プラグインを使う場合は npm run tauri add store を実行し、capabilities に store:default を追加します。

1. フロントエンドから実装する (TypeScript)

import { invoke } from '@tauri-apps/api/core';

// OS のマシン ID をアプリ固有のソルトでハッシュ化した 64 文字の hex
const deviceId = await invoke<string>('device_id');
console.log(deviceId); // "3f9a...c21e"

フロントに生の ID を渡す必要はまずありません。サーバーへ送るのもハッシュ後の値にしておくと、他社アプリと突き合わせて同一端末を追跡する材料にならず、プライバシーポリシーにも書きやすくなります。

2. バックエンドから実装する (Rust)

src-tauri/src/lib.rs:

use sha2::{Digest, Sha256};

// アプリ固有のソルト。別アプリと同じ値にならないようにする
const SALT: &str = "com.example.myapp/v1";

#[tauri::command]
fn device_id() -> Result<String, String> {
    // Windows: MachineGuid / macOS: IOPlatformUUID / Linux: /etc/machine-id
    let raw = machine_uid::get().map_err(|e| e.to_string())?;
    let mut h = Sha256::new();
    h.update(SALT.as_bytes());
    h.update(raw.trim().as_bytes());
    // 16 進の文字列にする(sha2 0.10 / 0.11 のどちらでも動く書き方)
    Ok(h.finalize().iter().map(|b| format!("{:02x}", b)).collect())
}

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![device_id])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

machine_uid::get() は Result<String, Box<dyn Error>> を返し、管理者権限は不要です。

各 OS で何をもとにした ID か

OS取得元変わるタイミング
Windowsレジストリ HKLM\SOFTWARE\Microsoft\Cryptography\MachineGuidWindows の再インストール。ハードウェア交換では変わらない
macOSIOPlatformUUID(ioreg -rd1 -c IOPlatformExpertDevice で見える値)ロジックボード交換。OS 再インストールでは変わらない
Linux/var/lib/dbus/machine-id または /etc/machine-idディストリビューションの再インストール。クローンした VM やコンテナでは同じ値になりうる

つまり Windows と Linux では「OS インストール ID」であって、CPU やマザーボードには紐づきません。「ハードウェア ID」と呼んで設計すると、OS 再インストールで「別端末」と判定されライセンス枠を消費する問い合わせが必ず来ます。再アクティベーションの導線を用意してください。

代替案: 初回起動時に UUID を生成して保存する

端末を識別したいだけなら、アプリが自分で発行した UUID を保存する方が扱いやすいことも多いです。ユーザーがアプリデータを消せばリセットでき、説明もしやすくなります。

import { load } from '@tauri-apps/plugin-store';

export async function installId(): Promise<string> {
  const store = await load('device.json', { autoSave: true });
  let id = await store.get<string>('install_id');
  if (!id) {
    id = crypto.randomUUID(); // WebView2 / WebKit どちらでも使える
    await store.set('install_id', id);
    await store.save();
  }
  return id;
}

Rust 側で生成するなら uuid::Uuid::new_v4().to_string() です。マシン ID のハッシュとこの UUID を両方送り、片方が一致すれば同一端末とみなす緩い判定が実運用では現実的です。

動作確認

npm run tauri dev で起動し、コンソールに 64 文字の 16 進文字列が出れば成功です。再起動しても同じ値が返ること、Windows なら PowerShell で (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Cryptography').MachineGuid を表示し、同じソルトでハッシュした値と一致することを確認できます。

よくあるエラーと対処法

  • Linux で machine_uid::get() が Err: /etc/machine-id が無い最小構成のコンテナや、systemd を使わない環境で起きます。systemd-machine-id-setup で生成するか、Store 保存の UUID にフォールバックします。
  • Linux で複数台が同じ ID になる: VM のテンプレートをクローンした環境です。/etc/machine-id を空にして再起動すると再生成されます。「同一 ID の端末が異常に多い」ケースを想定しておきます。
  • store.load not allowed. Permissions associated with this command: store:default, ... という趣旨のエラー: capabilities に store:default が入っていません。
  • crypto.randomUUID is not a function: 安全でないオリジン(http:// の非 localhost で開いた独自 devServer など)で動かしています。Rust 側の uuid で生成して返します。

OS ごとの違いと注意点

  • macOS: IOPlatformUUID は最も「ハードウェア」に近い ID ですが、App Store 審査はデバイスのフィンガープリンティングに敏感なので、用途と保存期間をプライバシーポリシーに明記してください。
  • Linux: /etc/machine-id は systemd の管理下で、ユーザーが自由に書き換えられます。ライセンス保護としての強度は期待できません。
  • プライバシー: 端末 ID は個人を識別しうる情報として扱われます。生の値を送信・保存しない、用途を限定する、削除の手段を用意する、の 3 点を守ってください。

関連レシピ

参考リンク(公式ドキュメント)

Web Ninja

この記事を書いた人

Web Ninja ウェブエンジニア (Web Engineer)

会社員ネットワークエンジニアから独立してかれこれ 25 年以上 Web エンジニアとして活動中。普段は JavaScript と Node.js を自在に操り、時には C++ や Perl といった古流の技も嗜みます。近年は Tauri × Rust という新たな武器を手に、デスクトップアプリ開発の最前線を駆け抜けています。「作りたい」を「作れる」に変えるための、実践的な「技」をお届けします。

お問い合わせ: tauri.ninja@gmail.com

内容の誤り・動かないコードを報告する