Pythonのpickle
モジュールは、Pythonオブジェクトをバイトストリームにシリアライズ(直列化)し、後でデシリアライズ(逆直列化)するための便利なツールです。しかし、信頼できないソースからのデータをデシリアライズすると、深刻なセキュリティリスクが生じる可能性があります。
基本的な使い方
pickle
モジュールを使ってオブジェクトをシリアライズするには、pickle.dumps()
関数を使用します。
import pickle
data = {'name': 'Alice', 'age': 30}
pickled_data = pickle.dumps(data)
print(pickled_data) # b'\x80\x04\x95 ... ' のようなバイト列が出力される
デシリアライズするには、pickle.loads()
関数を使用します。
import pickle
pickled_data = b'\x80\x04\x95 ... ' # シリアライズされたデータ
original_data = pickle.loads(pickled_data)
print(original_data) # {'name': 'Alice', 'age': 30} が出力される
脆弱性
pickle
の脆弱性は、信頼できないソースからのデータをpickle.loads()
でデシリアライズする際に発生します。pickle
は任意のコードを実行できるため、悪意のあるコードが注入されたデータをデシリアライズすると、リモートコード実行(RCE)などの攻撃を受ける可能性があります。
__reduce__()
メソッドを悪用した攻撃が代表的です。このメソッドは、オブジェクトを再構築する方法を定義するために使用されますが、攻撃者はこれを悪用して任意のコードを実行させることができます。
import pickle
import os
class Exploit:
def __reduce__(self):
return (os.system, ('ls -l',)) # 危険なコマンド
# 攻撃者が作成する悪意のあるデータ
exploit = Exploit()
malicious_pickle = pickle.dumps(exploit)
# 信頼できないソースからデータを読み込む場合
# loaded_data = pickle.load(open("malicious_file.pkl", "rb"))
# 上記のようにファイルから読み込むだけでなく、ネットワーク経由で受け取ったデータなど、
# 信頼できないソースからのデータは全て危険です。
# デシリアライズ時に悪意のあるコードが実行される
loaded_data = pickle.loads(malicious_pickle)
Sleepy Pickle
Sleepy Pickleは、このpickle
の脆弱性を悪用した攻撃手法の一つです。機械学習モデルをpickle
形式で保存・配布する際に、悪意のあるコードを注入し、モデルの動作を改ざんしたり、機密データを盗み出したりします。
対処法
-
信頼できないソースからの
pickle
データは絶対にデシリアライズしない: ネットワーク経由で受信したデータや、検証されていないファイルなど、信頼できないソースからのpickle
データはpickle.loads()
で処理しないでください。 -
pickle
の代わりに安全なシリアライズ形式を使用する: JSONやYAMLなど、より安全なシリアライズ形式を使用することを検討してください。特に、外部とのデータ交換を行う場合は、pickle
の使用は避けるべきです。 -
制限付きのアンピクリング: どうしても
pickle
を使う必要がある場合、信頼できるクラスのホワイトリストを作成し、それ以外のクラスのデシリアライズを拒否するなどの対策を講じてください。 -
Safetensors: 機械学習モデルの保存には、
pickle
の代わりにSafetensorsの使用を検討してください。Safetensorsは、Pythonオブジェクトではなくテンソルデータのみを扱うため、任意コード実行のリスクを軽減します。
まとめ
pickle
は便利なツールですが、セキュリティリスクを理解し、適切な対策を講じる必要があります。信頼できないデータの取り扱いには細心の注意を払い、可能な限り安全な代替手段を使用してください。特に、機械学習モデルのような機密性の高いデータを扱う場合は、Sleepy Pickleのような攻撃に注意し、Safetensorsなどの安全な形式を使用することが重要です。