技術記事

改ざん検出:HotXLSのAgile dataIntegrity HMAC

HotXLSは、Agile暗号化されたXLSXパッケージに準拠したdataIntegrityブロックを書き込み、開くときにそれを検証します。HMAC-SHA-512は、8バイトのStreamSizeプレフィックスを含むEncryptedPackageストリーム全体をカバーし、どのセグメントも復号される前に暗号文に対してチェックされるため、誤ったパスワードや改変されたパッケージは、意味不明なデータへ復号されるのではなく検出されます

整合性を伴わない暗号化は、答えの半分にすぎません。そしてOffice系のファイル形式では、外から見ると暗号化がいかにも徹底しているように見えるため、その隙間が見落とされやすいのです。各層が何を保証しているのかを理解しておくことが、セキュリティレビューを短く済ませる鍵になります

暗号化されたワークブックが実際に保証しているのは何か

[MS-OFFCRYPTO]で定義されているAgile暗号化は、反復SHA-512パスワードハッシュから導出した鍵によるCBCモードのAESを通じて、機密性を提供します。機密性こそが、この構成が保証するすべてです。CBCは認証付きモードではありません。これから復号しようとしている暗号文が、実際に書き込まれた暗号文と同一かどうかについては、何も語らないのです

実際上の帰結は具体的です。暗号化されたパッケージの中のビットを反転させても、CBCはそれを何食わぬ顔で別の平文へと復号してしまいます。壊れたdeflateストリームが生き残ることはめったにないため、大抵は下流のどこかでZIPパースエラーが発生しますが、「大抵は」という言葉がこの一文の中で相当な役割を担っており、下流のパーサーエラーは、ファイルが改変されたことを知るにはひどい場所です。dataIntegrity要素は、復号する前に、正確なバイト列に対するMACによって、この問いに直接答えるために存在しています

チェックはどの順序で実行されるのか

興味深いのはその順序です。HotXLSは、まずパスワードから中間鍵を導出し、ブロックキーから導出したIVを使ってdataIntegrity属性に含まれる暗号化済みHMACキーとHMAC値を復号し、格納されている暗号化パッケージに対してHMAC-SHA-512を計算し、それらを比較します。セグメントの復号が始まるのは、それが終わってからです

平文ではなく暗号文に対してMACをチェックするのは、暗号化してからMACを付与するという標準的な流儀であり、これがチェックを意味あるものにしています。改ざんされたパッケージは、攻撃者が制御するバイト列を復号・展開の経路に一切通すことなく拒否されます。オープン処理における2つの比較、パスワード照合ハッシュとHMAC値の比較は、いずれも最初に不一致のバイトが見つかった時点で早期リターンするのではなく、ダイジェスト全体にわたってXORとORで差分を蓄積していくため、どちらもタイミングによってバイト位置を漏らすことはありません

var
  Book: TXLSXWorkbook;
begin
  Book := TXLSXWorkbook.Create;
  try
    // 平文、Standard暗号化、Agile暗号化のいずれのファイルにも対応
    if Book.OpenEncrypted('incoming.xlsx', PasswordFromVault) = 1 then
      ProcessWorkbook(Book)
    else
      // パスワードが誤っているか、dataIntegrityのHMACが一致しなかったパッケージ
      Quarantine('incoming.xlsx');
  except
    on E: Exception do
      Quarantine('incoming.xlsx: ' + E.Message);
  end;
  Book.Free;
end;

書き込み側では、自分のコードは何も変える必要がありません。SaveAsEncryptedがこのブロックを自動的に生成し、ソルト、照合子の入力、HMACキーはすべてCryptGenRandomから得られます。この呼び出しが失敗した場合、HotXLSはより弱いソースへフォールバックするのではなく例外を発生させます。フェイルクローズなCSPRNGは、過剰な警戒ではありません。予測可能な乱数源へ静かに降格すれば、暗号化されているように見え、あらゆる機能テストに合格するのに、実際には無価値なファイルが出来上がってしまいます

ブロックのないファイルがそれでも開けるのはなぜか

なぜなら、実際に流通しているAgile暗号化ワークブックの多くは、dataIntegrityを丸ごと省略する生成側によって書かれており、それらを拒否すれば、守れる以上に多くの正当な業務を壊してしまうからです。HotXLSは、暗号化済みHMACキーと暗号化済みHMAC値という2つの属性が両方とも存在し、かつ整形式である場合にのみ、整合性情報が存在すると見なします。それ以外の場合は検証がスキップされ、ファイルは従来どおり開きます

これは互換性のための判断であり、自分の脅威モデルの中で明示的に名指ししておくべきセキュリティ上の帰結を伴います。ブロックの不在は、攻撃者がそれを取り除いた場合と区別できません。なぜなら、これらの属性自体は、それが運ぶはずのMACの外側に位置しているからです。パイプラインの両端を自分でコントロールできるなら、ブロックの欠落をアプリケーションレベルのポリシー違反として扱ってください。世界中からファイルを受け付けているなら、このチェックをあるがままに扱ってください。存在すれば価値あるシグナルであり、存在しなければ何のシグナルでもないということです

「変更のためのパスワード」は境界ではなく取り決めにすぎない

クラシックなXLSワークブックは、暗号化としばしば混同される別の仕組み、書き込み予約、つまりExcelの「変更のためのパスワード」プロンプトをサポートしています。HotXLSはこれをSetModifyPasswordを通じて公開しており、パスワード、読み取り専用推奨フラグ、予約ユーザー名を引数に取り、状態はIsWriteReservedを通じて報告されます。空のパスワードを渡すと予約は解除されます

実際に書き込まれるのは、読み取り専用推奨フラグ、レガシーな16ビットパスワードハッシュ、BIFF8 Unicode文字列としてのユーザー名を含む、WRITEPROTとFILESHARINGのレコードペアです。この16ビットハッシュはチェックサムであって暗号学的ダイジェストではなく、ドキュメントの内容はまったく暗号化されていません。他の任意のツールでこのファイルを開いた者は、すべてを読むことができます。この機能が実際に果たしている役割は調整です。誰かがこのファイルを自分が編集すべきものだと考えていることを次の人に伝える、XLSXシート保護と許可オプションで扱っているシートレベルの制御と同じ範疇のものです

var
  Book: IXLSWorkbook;   // インターフェース参照カウント管理:Freeしないこと
begin
  Book := TXLSWorkbook.Create;
  if Book.Open('shared-model.xls') = 1 then
  begin
    // 読み取り専用を推奨、レポーティングサービスによる予約
    Book.SetModifyPassword('edit-me', True, 'Reporting Service');

    if Book.IsWriteReserved then
      Book.SaveAs('shared-model.xls', xlExcel97);
  end;
end;

それぞれの層を、それぞれが得意とする用途で使い分けてください。本当の機密性は、対象読者以外の誰も持っていないパスワードを使ったSaveAsEncryptedから得られ、これはAES保護されたXLSX出力で説明されているAES-256の出力を生成します。書き込み予約は、そのワークブックが共同編集の成果物であり、誰かが上書き保存する前にExcelに確認を求めさせたい場合に、上乗せするものです

信頼できない取り込み経路で確認すべきこと

整合性検証が保護するのは、暗号化されたペイロードであって、それを取り囲むコンテナではありません。XLSXファイルはZIPアーカイブであり、暗号化ロジックが動く前にアーカイブ構造がパースされるため、コンテナレベルの検証はチェーンの最初に位置すべきものです。具体的な失敗モードは信頼できないXLSXに対するZIP EOCD検証で扱っています。それより後の段階では、整合性の失敗と誤ったパスワードを同じ運用上の出来事として扱ってください。こちら側から見れば、設計上この2つは区別できず、どちらも、そのファイルが送信者の意図どおりのものであると信頼できないことを意味するからです

そもそもdataIntegrityブロックを持っていたファイルがどれかをログに残してください。数千件のドキュメントにわたって見れば、その統計は送信者側のツールについて有用な情報を教えてくれ、ファイル単位のチェックを、対処可能なフリート単位の観測へと変えてくれます

HotXLSは、Excelのインストール不要で、DelphiとC++BuilderからXLS、XLSX、ODSを読み書きし、[MS-OFFCRYPTO]のStandardおよびAgile暗号化経路をPascalで実装しています。暗号化、保護、ワークブックの各APIはHotXLS Delphiスプレッドシートコンポーネントページで説明されています