皆さんこんにちは。特に気の利いたあいさつ文を思いつかなかったid:k-furusawa--gです。
今回は業務上でObjectStorageを扱っていたら、MD5ハッシュの扱いで躓いた話を書かせていただきます。AWSのS3で開発を行ったことがある方にとってはあるあるなのですが、今回はOracle Cloud Infrastructure(以下OCI)で同じ現象で躓いたため、せっかくだからということで記事にさせていただきます。
前置き
まず前置きとして、MD5ハッシュとOCIの動きについての基礎知識を。
MD5ハッシュとは何か
詳しくはwikiを読んでいただくとして、簡単に説明するとMD5とは任意の原文を128bitに変換するハッシュ関数のことで、与えられる原文が変更されるとハッシュの内容も変わるため、変更の検知(改ざん、欠落)に多く利用されます。
ObjectStorageにおいては、アップロードしたファイルの内容よりMD5ハッシュを生成してそれを記録してくれるので、ファイルの変更の検知に利用することができます。
AWSのS3においても、APIリクエスト時にETagヘッダ内にMD5ハッシュ値を載せることが可能です。
MD5ハッシュを確認してみる
実際にどこで確認できるのかご紹介します。
ObjectStorageの任意のオブジェクトの詳細を開くと、項目内に コンテンツのMD5ハッシュ
というものがあります。これがそれに該当します。
マルチパートアップロードするとMD5ハッシュも分割される
ObjectStorageへファイルをアップロードする際、選択筋としてはいろいろあると思いますが、公式のドキュメント曰く、100MiBを超えるような大きなファイルをアップロードする場合はマルチパートアップロードを利用せよとあります。推奨なだけで強制ではありません。
実際にマルチパートアップロードしてみたオブジェクトを参照してみると、以下のようになります。
MD5ハッシュが消えます。代わりに、opc-multipart-md5
というヘッダが追加されます。これはマルチパートアップロードを用いたことでMD5ハッシュも分割されたことを示します。ハイフンから後ろについている数値が分割数です。
この事象は、AWSのETagでも発生します。ヘッダが違うだけで同じルールですね。
マルチパートアップロードされる条件は様々
OCIの公式ドキュメントを読むと分かるのですが、64MiBより大きなファイルをアップロードするとマルチパートアップロードが自動で行われます。
実際、前項のスクリーンショットで示したファイルは公式のコンソールよりアップロードしたもので、コンテンツ長が1038876467。ヘッダからわかる分割数が16です。64MiBで割るとピッタリですね。
他にもCLI利用や外部ツール利用など様々なものが要因として上げられます。まとめると以下のようになります。
- OCI公式コンソールから64MiB以上のファイルをアップロードする
- CLIを用いてデフォルト設定のまま128MiB以上のファイルをアップロードする
- CLIを用いてアップ最大サイズを任意で変更し、そのサイズを超えたファイルをアップロードする
- rcloneといった外部のツールを利用し、分割サイズ設定を超えたファイルをアップロードする
分割サイズは作業する環境によって当然変わってきます。大きいサイズだけ小さく、小さいサイズは大きく設定を変えるなどもあり得ますね。一律していることはほぼないと思います。
本題
以上を踏まえたうえで、発生した問題について。
やりたかったこと:マルチパートアップロードされたオブジェクトの変更検証を行いたい
マルチパートアップロードした場合はMD5ハッシュが分割されるのは先に書いた通りです。これをローカル(またはNAS)のファイルと比較するにはどうするかといえば、MD5ハッシュを復号することはできないので(暗号化しているわけじゃないので)、比較したいファイル側を同じルールで分割してMD5ハッシュを作成すれば、比較可能ですね。
このあたりのかなり詳しい検証を、他社様ではありますが、株式会社コーソル様のブログ記事で行われていますので、以下のリンクからご参照ください。
これを行うためには、分割時のパーツ情報が必要です。前述したように、マルチパートアップロードされる条件は様々であり、環境や状況によって分割サイズが異なるためオブジェクトアップロード時の分割データ長がなければ同じルールで分割できません。
問題1:アップロード済みのオブジェクトはパーツ情報を得ることができない
しかし、OCIには分割された際のパーツ情報を得るための手段が現在(2020/08/20)は存在しません。
公式ドキュメントをよく読めば書いてあります。
完了したアップロードからパートをリストまたは取得することはできません。完了したアップロードからパートを追加または削除することもできません。
アップロード中であればListMultipartUploads APIを用いれば取得可能ですが、アップロード済みの場合は取得できなくなります。
パーツ情報はアップロードが終了した時点で消えてしまうようです。実際、私もアップロード済みのオブジェクトのパーツ情報を得ようとしましたが、どうやっても無理でした。
問題2:分割される条件は様々で予測するのは難しい
マルチパートアップロードされる条件がかなり限定的であるなら、ある程度の対処も考えられますが、実際はそうもいきません。繰り返しになりますが、マルチパートアップロードされる条件は以下になります。
- OCI公式コンソールから64MiB以上のファイルをアップロードする
- CLIを用いてデフォルト設定のまま128MiB以上のファイルをアップロードする
- CLIを用いてアップ最大サイズを任意で変更し、そのサイズを超えたファイルをアップロードする
- rcloneといった外部のツールを利用し、分割サイズ設定を超えたファイルをアップロードする
64MiBもしくは128MiBといった決め打ちで検証するわけにもいかないわけです。
アプリケーションレベルで対応するのは非常に難しい
以上のことから、分割時のデータ長を正確に把握する手段がないため、アプリケーションレベルで完璧に対応するのは難しいです。
特定の環境から特定のサイズでマルチパートアップロードされたものだけを対象として、それ以外のものに関しては未対応として処理するしかないわけですね。
補足:AWSのS3でも同じことが起きる
先にもちらりと書きましたが、AWSでも同じことが起きます。
S3のETagにはMD5ハッシュが載りますが、マルチパートアップロードが行われるとこの内容がOCIと同じく分割されたものとなります。その関係で、自動検証などの機能が使えなくなります。
AWSとしてはPutObject APIを利用すればマルチパートアップロードされることはないので、検証を行いたい場合はそちらを利用するのが一般的なようです。
MD5ハッシュの比較で変更を検証するアプリケーション開発の際は気を付けよう
AWSのS3でも同様のことが起こるので、MD5ハッシュの比較で変更を検証するようなアプリケーション開発をする際は、アップロード時にマルチパートアップロードであった場合の扱いを明確にしておくことが大事です。
例えばローカル(またはNAS)のファイルとObjectStorageのファイルを比較し、変更がある場合はアップロードするような同期処理を持つアプリケーションを作りたい場合、マルチパートアップロードであることを検知した時点で検証を断念し警告を出すか、もしくはそのまま同期をやり切ってしまうか、など仕様決めですね。
断念した分に関しては改めてマルチパートアップロードにならないようにアップロードするか(後述)、同期そのものから外すかなどですね。要するに運用で頑張ってもらう! というわけです。
サポートに相談してみた
サポートで詳しい内容と解決手段を聞いてみました。
結果、前述してきたように分割されたMD5の計算方法の開示は可能であるが、OCIではパーツ情報を得ることができないため事実上検証はできないという結論に至りました。
というよりも、そもそもパーツ情報が得られない事実をサポート側も把握していないようでした。はじめは検証可能なように話が進んだので驚いたのですが、結果的にできないという結論です。仕様くらいはスムーズに回答してほしいですね。
おまけ:いろいろ検証してみる
結果は見えてますが、一応検証として以下のことを確認しました。
1.公式コンソールは64MiB以外で分割するよう変更可能なのか?
できません。固定です(サポートでも確認済み)。CLIは128MiBから変更可能です(--part-sizeもしくは--no-multipartオプション)。
2.APIでも強制的に分割されてしまうようなことがあるのか?
AWSのS3と同じでマルチパートアップロードになりません。自分の開発環境で試してみました。
1GBのファイルをPutObject APIでStreamにそのまま載せて、POSTしてみました。結果、分割はされずにそのままの状態でObjectStorageに登録されました。どうやらAPIの場合はリクエストされた仕事をそのままこなす様ですね。おそらく公式SDK利用でも同じです。
3.マルチパートアップロードされたオブジェクトにPutObject APIで上書きをするとどうなるのか?
通常のMD5ハッシュを持つオブジェクトに上書きされます。
なので、マルチパートアップロードされたファイルを通常のMD5ハッシュで検証したい場合、無理やりPutObjectでアップして上書きすることで、結果的に検証ができるようになります。
最後に
可能かどうかはさておき、パーツ情報取れるようにしてください。もしくは分割サイズがわかるようにヘッダに追加してください。とOCIにビジネスケース付きで提案するとよいかもしれません。
もしくは、分割時のサイズさえわかれば算出できるので、アップロードを行う運用部分で完全なルール化をして(コンソールが64MiB固定なのでそれに合わせる形)、アプリケーションがスムーズに検証できるようにするか、です。難しいかもしれませんが……。
一番いいのは、ヘッダに分割時のサイズが載ることでしょうか。様々な理由からできないのかもしれませんが。
ObjectStorageを利用したアプリケーション開発は今後も様々な場面であり得るので、こうした問題点に対する何らかのアクションが欲しいところです。