🕶️

Python: 文字列の実行でeval,execからは卒業しよう

2023/05/17に公開

はじめに

Pythonには、文字列の評価や動的なコード操作を行うための複数の手法が存在します。

文字列の実行では、eval()exec()がポピュラーですが、本記事では安全で信頼性の高い方法としてast.literal_eval()getattr()の活用方法を紹介します。

exec,evalとそのセキュリティリスク

Pythonのexec()を使用すると、文字列をPythonのコードとして実行することができます。以下はexec()の基本的な使用例です。

agg = "sum"
l = [1, 2, 3]
exec(f'result = {agg}(l)')
print(result)

eval()を使用すると、文字列をPythonの式として評価し、結果を返すことができます。以下はeval()の基本的な使用例です。

agg = "sum"
result = eval(f'{agg}(l)')
print(result)

外部からの入力を直接実行する場合、exec()eval()を使用するとインジェクション攻撃や予期せぬバグの発生など、セキュリティ上のリスクが高まります。

リスクの例としてeval("os.remove('*')", {})が実行されてしまうことです。

外部からの入力を全く受け付けないプログラムならexec()eval()を使用しても良いですが、今回はexec()eval()を用いない方法で文字列をそのまま扱う方法を見ていきます。

リテラルのみを含む式の変換

Pythonのコード上での書き方で記述された文字列に対してast.literal_eval()を用いることでをリストや辞書に変換できます。

import ast
l = ast.literal_eval('["x", "y"]')
print(l)
# ['x', 'y']

ast.literal_evalでは、eval("os.remove('*')", {})といった関数の実行はできないのでevalよりも格段に安全です。

クラスのメソッドの実行

クラスのメソッドが文字列で与えられる場合にはgetattr()を用いることでメソッドを実行できます。

# getattr関数の利用例
class MyClass:
    def my_method(self):
        print("Hello, World!")
obj = MyClass()
method_name = "my_method"
# getattrを使用して動的にメソッドを実行
getattr(obj, method_name)()
# 結果: Hello, World!

getattr()を使用して動的にオブジェクトのメソッドを実行しています。

exec()eval()と比べて自由度が下がっており、クラスのメソッドしか利用できないのでexec()eval()と比ベると安全です。

組み込み関数の実行

組み込み関数はbuiltinsに含まれているので、上記と同様にgetattr()を用いることで実行できます。

import builtins
func = getattr(builtins, agg)
result = func(l)
print(result) 

まとめ

execevalは注意深く利用しましょう。ast.literal_eval()などの代替手段を検討しましょう。

Discussion