暗号化バックアップの復元実験 — 人工データのみを使用 実測日:2026-09-03 実験の対象 ---------- restic 0.19.1、Python 3.12.10、Windows 11 amd64を使った実験である。 生成した12個のファイルには、UTF-8テキスト、JSON、CSV、XML、ZIP、WAV、 TOMLの7種類の形式を用いた。個人の文書、写真、録音、実際の認証情報は 含まれていない。 一つのローカル環境で行う実験であり、バックアップ製品の比較や、一般的な 安全性の評価ではない。開発中の実行と最終記録の6行は同じ結果になったが、 別々の機器で行った独立した試験ではない。 バックアップのデータと管理情報を置く領域を、リポジトリと呼ぶ。 リポジトリの形式は2、圧縮設定はresticの既定値(auto)とし、キャッシュは 無効にした。各バックアップの成功時に、保存した版を表すスナップショットの 完全なIDを取得する。復元には、その固定IDと人工データの/sourceを使う。 特別な指定値である「latest」は使わない。復元先は、毎回新しく作る実験用 フォルダの中に用意した、新しい空のフォルダである。さらに、上書きをしない --overwrite neverを指定している。 残す予定のファイルと、その内容から計算した比較用の値(ハッシュ値)を 記録した一覧を、マニフェストと呼ぶ。S4では、12個すべての予定一覧を作って から、保存対象の除外を適用した。S5では、必要な現在の版の予定一覧を作って から、古い版を保存した。S6では、意図的に作った不正なJSONと、保存前の 読み取り失敗を一覧へ記録した。不完全なスナップショットや、その中の ファイル一覧だけを「残す予定だったもの」の正解としてはいない。 次の確認を個別に行った。 * restic check:保管領域の通常の構造検査に通るか。 * restic check --read-data:保存データ全体の読み取り検査に通るか。 * restic restore:終了コード0でファイルを取り出せるか。 * 一覧:予定した場所と名前に全ファイルがあり、予定外のものがないか。 * SHA256:復元した全ファイルが、事前の予定一覧のハッシュ値と一致するか。 * parser:形式に沿って内容を読む標準のプログラムで、復元ファイルを読めるか。 実測結果 -------- 条件 check read-data restore 予定ファイル ハッシュ一致 読取成功/実施 S1 0 0 0 12/12 12/12 12/12 S2 12 12 12 0/12 0/12 未実施(0個) S3 0 1 1 11/12 11/12 11/11 S4 0 0 0 11/12 11/12 11/11 S5 0 0 0 12/12 11/12 12/12 S6 0 0 0 12/12 12/12 11/12 終了コード0は、操作が正常に完了したことを示す。今回の12は公開ダミーの パスワードが間違っていたこと、1はデータの読み取りや復元のエラーを示す。 JSONと実行記録には、各操作の結果と診断メッセージを分けて残した。 終了コードが0でも、利用者の復元目的をすべて満たしたとは判断できない。 S1:変更していない正常な人工データを使った。予定したファイル、その内容、 形式の読み取りのすべてが確認を通った。 S2:S1のリポジトリのコピーに対して、意図的に間違った公開ダミーパスワードを 使った。復元されたファイルはない。この誤った解除情報が拒否されることを 確かめた実験であり、 ほかに回復用の鍵が残っているかどうかは試していない。 S3:S1のリポジトリから作った実験専用コピーで、一つのデータ塊(blob)の 暗号文を1バイト反転させた。documents/records.jsonに対応する一つのblobを、 既知の内容ハッシュを使ってresticの索引内から特定した。そのblobの先頭に ある16バイトのnonce(暗号化に使う初期値)の直後を変更している。 保存用ファイルであるpackのサイズと、管理情報(ヘッダー)は変更していない。 通常検査は成功したが、全データの読み取りと復元は失敗した。 復元されたのは11個だった。packとblobのID、変更位置、packのハッシュ値、 欠けたファイルはresults.jsonに記録してある。結果は今回の破損条件に 限られる。別の種類の破損なら、通常検査で検出される場合もある。 S4:すべての予定一覧を記録した後、documents/contacts.csvを保存対象から 外した。resticの全操作は成功したが、独立した予定一覧との照合と、 全ファイルのハッシュ値の照合は11/12にとどまった。 S5:より新しいスナップショットも存在する状態で、古い版を復元した。 documents/status.jsonには、必要なrevision 2の代わりにrevision 1がある。 12個すべてを読み取れたが、必要な現在の版の一覧と一致したのは11個だった。 別に残した古い原本の一覧とは12個すべてが一致した。目的に合わない版を 正しく復元した結果であり、resticが内容を壊したわけではない。 S6:documents/records.jsonは、保存する前から不正なJSONである。原本の 読み取りに失敗した記録もsynthetic-manifests.jsonに含まれる。 復元したすべてのバイト列が原本と一致したが、同じJSONの読み取りは 引き続き失敗した。不正な内容になった原因は、復元操作にはない。 「読取成功/実施」欄の意味 -------------------------- 実際に存在するファイルだけを読み取る。欠けたファイルは一覧の照合で示し、 読み取り成功に数えない。TXTはUTF-8として厳密にデコードし、JSONはjson.loads、 CSVはcsv.reader(strict)と各行の列数の一致、XMLはElementTree.parseで調べる。 ZIPは含まれる全ファイルを読み、内容の誤りを検出するCRCを確認する。 WAVは記録された全フレームを読み、そのバイト数を確認する。 TOMLにはtomllib.loadsを使う。 拡張子だけを確かめたのではなく、実際に形式を解釈して内容を読み取った。 ただし、特定のデスクトップアプリで全機能が表示・動作することや、内容が 実際の利用者の希望を満たすことまで確かめたわけではない。 公開資料 -------- restore-lab.py 最終版の再現プログラム。Pythonの標準機能のみを使用。 results.csv 数値をまとめた結果表。 results.json ファイルごとの結果、スナップショットID、実行ファイルの 入手元、プログラムのハッシュ値、破損条件の詳細。 synthetic-manifests.json 事前に作った予定一覧と、原本の読み取り結果。 command-log.json 実行したコマンド、終了コード、標準出力とエラー出力。 実験場所、実行アカウント名、日時の項目は除去している。 操作の結果は変更していない。 lab-readme.txt この実験方法と結果の読み方。 Windows amd64での再現 -------------------- Python 3.11以降と、下記の公式ZIPが必要になる。記録した実行ではPython 3.12.10を 使った。新しい作業フォルダへrestore-lab.pyを保存し、公式配布ZIPを取得する。 ZIPのSHA256を、公式SHA256SUMSファイルと照合すること。プログラム自身も、 ZIPと展開後の実行ファイルを、下記のハッシュ値に照合する。 プログラムによるダウンロードはなく、resticをシステム全体に導入する必要もない。 配布ページ: https://github.com/restic/restic/releases/tag/v0.19.1 ZIP: https://github.com/restic/restic/releases/download/v0.19.1/restic_0.19.1_windows_amd64.zip 公式チェックサム一覧: https://github.com/restic/restic/releases/download/v0.19.1/SHA256SUMS ZIPのSHA256: da948ad707ed690426473aaba2046cd61f8f90f6f0e7dab6be0d5796531de67d 展開した実行ファイルのSHA256: b0dd1fd21eea5d8fe1325f55f7118213c21f36de8a261e04c0624a5ab9fd7830 実行して得られた版の表示: restic 0.19.1 compiled with go1.26.4 on windows/amd64 ZIPのハッシュ値は、同じ公式HTTPS配布元から取得したチェックサムと照合した。 配布物の署名を別途検証する作業は行っていない。 新しい作業フォルダで、PowerShellから次を実行する。 python .\restore-lab.py --restic-zip .\restic_0.19.1_windows_amd64.zip その作業フォルダ内に、新しいc07-restore-lab-*フォルダが作られ、LAB_ROOTとして 場所が表示される。既存のリポジトリ、原本フォルダ、復元先は指定できない。 別の場所を参照するシンボリックリンクと、ジャンクションを含むWindowsの 再解析ポイントは拒否する。ZIPから読み出すのは、名前を指定した実行ファイルだけだ。 データを意図的に壊す操作は、新しく作ったS3のリポジトリコピー内に限られる。 自動削除はなく、実データの操作も行わない。 PUBLIC-SYNTHETIC-LAB-ONLY...という固定の文字列は、公開しているテスト用ダミーだ。 誰でも読めるため、実際のファイルを守る目的には絶対に使わないこと。 起動されるresticは、既存のRESTIC_*設定を引き継がず、このダミー文字列を使う。 一時ファイルの保存場所も、新しい実験用フォルダ内に設ける。 システム全体の環境変数やVPN設定は変更しない。 結果はLAB_ROOTの中へ保存される。公開用フォルダへの自動コピーは行わない。 再実行するたびに、新しい実験用フォルダができる。無作為に作るリポジトリの鍵、 スナップショットID、packのID、実行時間は毎回異なる。破損対象のファイルは、 既知の内容ハッシュで固定している。プログラムのハッシュ値を開始時と終了時に 確認するため、実行途中にプログラムを書き換えると、その実行は最終結果として 確定されない。 実験の限界 ---------- 通常条件のデータは合計2,330バイトで、意図的に小さくしてある。 大規模なリポジトリ、復元速度、ディスクの故障率、媒体の寿命、別の場所に置いた コピーや接続を切ったコピーが事故後も使えるかどうか、クラウドアカウントの復旧、認証情報の管理、 OS全体の復旧、ファイルの権限、アクセス制御一覧、代替データストリーム、 アプリでの表示、将来のファイル形式への対応は測っていない。 読み取りの成功は、今回の人工ファイルと読み取りプログラムに限った結果である。 個人のバックアップが復元できるという保証にはならない。 関連する公式文書 ---------------- https://restic.readthedocs.io/en/stable/045_working_with_repos.html https://restic.readthedocs.io/en/stable/050_restore.html https://restic.readthedocs.io/en/stable/030_preparing_a_new_repo.html 公開した結果は、版0.19.1での観測である。stableと表示される現行マニュアルは 後から更新される可能性があるため、版の表示、配布URL、チェックサム、実行した コマンド、プログラムを合わせて残している。 教材の追加資料(2026-09-13) -------------------------- 新しい比較用プログラムは、実行記録を公開済みの基準と照合する。 配置方法・表示の意味・限界・図の生成方法はcomparison-readme.txtにまとめた。 この技術補足と単独の比較図は英語で記載している。