サーバーサイドの暗号化によって Linode オブジェクトストレージ上のデータを保護できます。本記事は、独自の暗号化キーを使用して、データをディスクに保存する前にオブジェクトレベルで暗号化する方法について記載します。一度暗号化されると、同じ暗号化キーが検索リクエストで提供された場合のみデータを復号化します。現在、Linode オブジェクトストレージが対応している方式は、クライアント側でキーを管理し、サーバーサイドでそのキーを用いて暗号化、復号化する方式です。特に自社で暗号化キーを完全に管理したい場合や、Linode にキーを預けたくない場合に使用される暗号化方式です。
本記事は以下のドキュメントを参考にしています。
上記ドキュメントは、「Hello World!」というテキストを含む単純なファイルを、提供された暗号化キー (SSE-C) を使用して Linode オブジェクトストレージに暗号化してアップロードし、次に復号化してファイルの内容を読み込み、最後にファイルを削除するPythonスクリプトの例が記述されています。
本記事では、用意されたファイルに対して、 Python と AWS Boto3 SDK を用いて、 put、get、delete を実施します。利用するコードは下記から参照してください。
SSE-C とは
SSE-C (Server-Side Encryption with Customer-Provided Keys) は、Linode オブジェクトストレージでデータを保存する際にユーザーが自分で管理する暗号化キーを使用して、サーバー側でデータを暗号化する方法です。SSE-C を利用すると、Linode オブジェクトストレージは暗号化プロセスを処理しますが、暗号化キー自体は Linode には保存されず、ユーザーが完全に制御します。大きく3つのポイントがあります。ユーザー提供のキーであること、そのキーはサーバー側に保存されないこと、キーの制御はユーザーに委ねられることです。
ユーザー提供のキー
ユーザーが暗号化キーを用意し、オブジェクトをアップロードする際に Linode に提供します。Linode オブジェクトストレージはそのキーを使ってデータを暗号化・復号化しますが、キー自体は保存しません。
キーは保存されない
Linode オブジェクトストレージは暗号化・復号化のリクエストが来た際にのみ、ユーザーが提供したキーを使用します。キーは一時的に使用されるだけです。
フルコントロール
ユーザーが暗号化キーを完全に管理するため、キーのローテーション(交換)や無効化が自由に行えます。これにより、セキュリティポリシーや法規制に沿った柔軟な運用が可能です。
暗号化キーを変更する場合は、再度暗号化をする必要があります。キーの管理は利用者側の責任となります。キーを紛失した場合は、そのファイルは復号できません。
共通設定
API のアクセスキーとシークレットキーが必要になります。作成は下記資料などを参考にしてください。
作成したキーの情報は conf.json
に記載します。
conf.json
endpoint_url
には アクセスするドメイン名が含まれるエンドポイント URL を記載します。
var.json
encryption_key
には後述する openssl
コマンドで作成したキーをいれます。algorithm
には AES256
が固定値となります。bucket_name
にはバケットの中のトップに作成されるフォルダ名を入れます。s3_path
はその下のディレクトリ階層をパス形式で記載します。この例では myFolder/path1/
のフォルダの下でオブジェクトを操作します。
事前準備
クライアント側で Python3 が動くようになっているか確認します。
python3 --version
出力結果の例です。
Python 3.9.6
スクリプトで使用する32文字の暗号化キーを作成します。OpenSSLを使用すると、暗号化キーとして使用する32文字の16進文字を次のコマンドでランダムに生成できます。
openssl rand -hex 16
Linode オブジェクトストレージはデータを暗号化した後、暗号化キーを直ちに破棄します。SSE-C で暗号化されたオブジェクトストレージデータは、暗号化キーがないと復元できません。
put_object
put_object はオブジェクトストレージにファイルを保存する関数です。
github にあるコード put.py
を実行する前に、重要な部分のみを説明します。AWS が提供する Boto3 SDK を使いますので事前にインストールしてください。s3
を指定することで AWS S3 のインタフェースを使えます。Linode オブジェクトストレージは S3 互換のため、Boto3 SDK を使うときは s3
を指定します。aws_cfg
では、前に説明した config.json
の内容を渡しています。
# S3クライアントの作成
client = boto3.client("s3", **aws_cfg)
ファイルをオブジェクトストレージに置くには Boto3 SDK の put_object を使います。ここでは、キー、アルゴリズム、バケット名、キーと値(ファイルの中身) を指定しています。バケット名は実際にはバケット内のトップのフォルダー名になりますので、注意ください。
client.put_object(
SSECustomerKey=ENCRYPTION_KEY,
SSECustomerAlgorithm=ALGO,
Bucket=BUCKET,
Key=s3_file_path_name,
Body=file_data
)
暗号化したいファイルを用意します。
ls -g input.txt
-rw-r--r--@ 1 staff 1168 Oct 27 17:14 input.txt
Python のコードを実行します。
./put.py input.txt
次のように表示されたら成功です。
Uploading file to Object Storage and encrypting with SSE-C.
Upload and encryption successful.
Linode Cloud Manager では次のように見えると思います。
get_object
get_object はオブジェクトストレージに存在するファイルを読み取る関数です。
Boto3 SDK の get_object を使っています。ここでは、キー、アルゴリズム、バケット名、キーを指定しています。バケット名は実際にはバケット内のトップのフォルダー名になりますので、注意ください。
client.get_object(
SSECustomerKey=ENCRYPTION_KEY,
SSECustomerAlgorithm=ALGO,
Bucket=BUCKET,
Key=FILE
)
第一引数に取得したいファイル名、第二引数にはローカルに落としたいファイル名を渡します。
./get.py input.txt output.txt
次のような表示がされたら成功しています。
Downloading encrypted Object Storage file.
Decrypted object saved to: output.txt
ファイルが作成され、put_object
で渡したファイルと中身が一緒になっていると思います。
ls -g output.txt
-rw-r--r--@ 1 staff 1168 Oct 28 18:18 output.txt
encrytpion_key を指定しない場合
次のコードのように get_objects の引数に encyption_key を指定しない場合はエラーとなりファイルを読み込むことができません。
client.get_object(
Bucket=BUCKET,
Key=s3_file_path_name,
)
An error occurred: An error occurred (InvalidArgument) when calling the
GetObject operation: Requests specifying Server Side Encryption with
Customer provided keys must provide a valid encryption algorithm.
このようにファイルを保護できることが分かります。
誤った encryption_key を指定した場合
var.json
で指定する encryption_key
を誤ると次のエラーとなります。暗号化キーをなくすとファイルを復号化できないことが分かります。
An error occurred: An error occurred (InvalidArgument) when calling the
GetObject operation: The calculated MD5 hash of the key did not match the
hash that was provided.
誤った algorithm を指定した場合
var.json
で指定するアルゴリズムを変更した結果です。AES256
でなければならないことが分かります。
An error occurred: An error occurred (InvalidEncryptionAlgorithmError)
when calling the GetObject operation: The requested encryption algorithm is
not valid, must be AES256
誤った bucket_name を指定した場合
var.json
で指定する bucket_name
を誤ると NoSuchKey
と表示されます。Linode では キーとして扱われていることが分かります。
An error occurred: An error occurred (NoSuchKey) when calling the GetObject
operation: Unknown
誤ったキー(ファイル名)を指定した場合
get.py
の Python スクリプトで渡すファイル名を誤ると NoSuchKey
と表示されます。
An error occurred: An error occurred (NoSuchKey) when calling the GetObject
operation: Unknown
delete_object
delete_object はオブジェクトストレージに存在するファイルを削除する関数です。
delete_object では暗号化キーを渡す必要はありません。
client.delete_object(
Bucket=BUCKET,
Key=DELETE_FILE
)
var.json
で定義した設定に基づいたパスに対して次のようにファイルを削除できます。
./delete.py input.txt
本当に削除してよいかプロンプトがでます。 yes を選択するとファイルは削除されます。
Are you sure you want to delete 'input.txt' from bucket 'myFolder'?
Type 'yes' to confirm: yes
Deleting encrypted Object Storage file.
Deletion successful.
まとめ
オブジェクトストレージに配置するファイルを暗号化することは、データ保護とセキュリティの強化、法規制やコンプライアンスの遵守、データ漏洩リスクの低減などのメリットがあります。暗号化により、万が一、バケットへの不正アクセスやセキュリティ侵害が発生しても、暗号化されたデータは復号化されない限り意味を持ちません。データの内容が読み取られないため、重要な情報(個人情報、財務データ、知的財産など)の機密性が保たれます。特に昨今では、PCI DSS、HIPAA、GDPRなど、データの暗号化を要求する法規制が存在します。暗号化機能を使用することで、これらの規制に準拠し、必要なセキュリティ基準を満たすことができます。ファイルが誤って公開されてしまった場合でも、暗号化されていれば、攻撃者や第三者が内容を理解することは困難です。
本記事で紹介したカスタムキーを使ってオブジェクトストレージ側で暗号化する方式はキーの管理が課題にはなりますが、有効なケースが多数ありますので、是非ご検討ください。