Pythonのimportステートメントには、開発者や企業が注意する必要があるセキュリティリスクが伴う。これがどのように機能するのか、そしてなぜ簡単な解決策がないのかを解説する。
この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。
Pythonを使用する場合に対応が必要な、あまり注目されていないリスクが存在している。
最新のソフトウェア開発言語は全てモジュール式であるため、開発者はコードの大きなセクションを、より管理しやすい小さな部分に分割できる。これにより、通常はライブラリにグループ化されたコード単位を再利用できる。これらのライブラリは社内で作成されていないことが多く、グラフ作成、データベース接続、配列計算などの一般的なタスクを実行するために作成されたオープンソースコレクションだ。
コード単位が他のコード単位のメソッドおよびプロパティと連携するには、それらのコンポーネントを参照する必要がある。JavaやPythonを含む最新の言語は、この要件をimportステートメントの形式で実装する。これは、現代の全てのエンタープライズコードのバックボーンを形成する。
ただし、Pythonには、importステートメントの使用に伴う危険がある。本稿ではその理由を探り、なぜ簡単な解決策がないのかを解説する。
最新の言語では、ファイルをインポートするには使用するライブラリのインスタンスを作成するか、静的メソッドを直接呼び出す必要がある。いずれの場合でも、インポート内で何かを実行するには、コード内で何かを実行する。
Pythonはこの規則の例外に当たる。Pythonでファイルをインポートすると、メソッドではないトップレベルのコードがすぐに実行される。
多くの開発者は、このPythonインポート機能を有効に活用している。例えば、Pythonファイルのトップレベルの本体にコードを書き込んで、コードをテストしたり、ライブラリが依存関係に関して保持している前提を検証したりしている。
Pythonは基本的にスクリプト言語であり、ファイル本体から直接ロジックを作成して実行できる機能は魅力的だ。ただし、この機能はその性質上、実行するために明示的に何も呼び出されていないときにコードを実行する。これが大きな問題となる。
インポート時にコードを実行するPythonの機能は、企業に3つのレベルのリスク(偶発的リスク、意図的リスク、外部リスク)をもたらす。
偶発的なリスクは、開発者がテストなどを実施するためにインポートのトップレベルの本体に何かを変更または追加し、完了後にその特定のコードを削除するのを忘れる場合に発生する。これにより、不要なリソースが使用されたり、無意味なエラーメッセージがログファイルに追加されたり、無限ループや同様の問題を含むテストが実行されたりする可能性がある。コード自体はどこにも明示的に呼び出されないため、これらの問題を追跡するのは困難だ。
意図的なリスクは、不満を抱いた開発者が悪意を持ってインポートのトップレベル本文を変更する場合に発生する。これにより、例えばクリプトマイニングルーチンの実行、機密情報のネットワーク外へのエクスポート、ネットワークへのバックドア、トロイの木馬のインストール、さらには意図的に見つけにくい場所でコードをクラッシュさせることさえ可能になる。
外部リスクはおそらく最も潜在的なリスクだ。ここでは、開発者は誰も、偶然かどうかにかかわらず何も悪いことはしていない。代わりに悪意のある第三者が、企業が使用する外部ライブラリを侵害する。その後、悪意のある攻撃者が企業のネットワークにバックドアをインストールして、データを侵害したり、暗号通貨をマイニングしたり、ランサムウェア攻撃を開始したりする可能性がある。
このような攻撃を受けたライブラリが、企業に対する攻撃手段として使用されることが増えている。Python以外の言語では、依然として動作するためにコードの実行に依存している。
Pythonで必要なのは、インポートアクションだけである。
この問題に対処するため、Pythonは開発者が実行したいコードを、ファイルがアプリケーションとして実行されるときにだけ実行される特別なコードセクションに置くことを要求している。最初にそのファイルをインポートしたときには、そのコードは初めから実行されない。
これは、ifステートメントを使用して「__name__」が「__main__」であることを確認することによって実装される。これがどのように機能するかを示す簡単な例を下記に示す。
def some_method(): return "Hello, world!" print("This will always be printed") if __name__ == "__main__": print("This will only be printed if this is run directly")
ファイルがインポートされた場合は「This will always be printed」と表示される。直接実行すると、「This will always be printed」と表示され、また「This will only be printed if this is run directly」と表示される。
このアプローチの問題は、それが完全に作業者の良心に依存していることだ。通常の開発環境では十分に動作するが、唯一の懸念材料はアクシデントが起きたときだ。外部か内部かを問わず、悪意のある攻撃者に対しては全く防御できない。
「Java」や「C#」などの言語は、設計上、インポート時にコードを自動的に実行しない。開発者は、インポートからクラスを初期化するか、インポート内で静的メソッドを呼び出す必要がある。
これにより、悪意のあるコードがシステムを悪用するハードルが非常に大きいものとなる。
問題の重大度は、単体テストやソース管理などの企業の内部慣行に大きく依存する。残念なことに、このような標準的な方法では、悪意のある攻撃者を軽減するのにほとんど役に立たない。
まず初めにシェルを開き、リモートアクセスを可能にするポートに接続する単純なペイロードを想定する。次に、ペイロードは第2レベルのインポートを見つけて、コードをファイルの最後に追加する。あるいは、より良い方法として、コードをメソッドの間に配置する。
Pythonのimportステートメントの問題を悪用するのに必要なのはこの手順だけだ。
コードは大きくならず、必要なリソースも最小限で済む。これを大規模なコードのコミットで隠すのは簡単で、単体テストには影響しない。誰にとっても、シェルにアクセスして悪意のあるコードが実行されるまでは、全てが正常であるはずだ。
企業レベルでは、この問題は深刻に受け止められていない。
トップレベルのコードを自動的に実行するPythonのインポート機能は、企業での使用にとって大きな問題となる。
組織はトップレベルのコードの存在を示す静的コード分析ツールを積極的に組み込む必要がある。また、めったに使用されないライブラリが密かに編集されたことを認識するAI(人工知能)ベースのルーチンを採用する必要もある。
ただし、これらの手順はリスクを軽減するだけで、それを排除することはできない。本稿で紹介した危険性は、Python中心のコードベースを採用している全ての運用関係者が心に留めておくべき攻撃手段だ。
Copyright © ITmedia, Inc. All Rights Reserved.