Python: 文字列の実行でeval,execからは卒業しよう
はじめに
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)
まとめ
exec
やeval
は注意深く利用しましょう。ast.literal_eval()
などの代替手段を検討しましょう。
Discussion