頭が悪い人のPythonのevalの使い方
- 作成日: 2022-03-21
- 更新日: 2023-12-25
- カテゴリ: Python
頭が悪い人のPythonのevalの使い方
Pythonの組み込み関数にevalがあります。
evalは式を評価します。
これらの関数は使い方を間違えると、システムの脆弱性になる可能性がある危険を持っている関数です。
頭が悪い人はこれらの関数を使うと痛い目を見るかもしれません。
この記事では頭が悪い人のevalの使い方を紹介します。
関連記事
頭が悪い人のPythonのevalの使い方
頭がいい人のPythonのexitの使い方
状態遷移による文字列パースのテクニック【Python】
形態素解析で代名詞+助詞+名詞を文章から抜き出す【Python, 自然言語処理, Janome】
在宅・未経験、Pythonで稼ぐ方法は?【取引・宣伝・広告】
evalの使い方
evalは引数の文字列を解析して、Pythonの式として評価し、その結果を返す関数です。
↓のように足し算などを計算できます。
result = eval('1 + 2')
print(result) # 3
式を評価する関数なので、↓のように文を実行することはできません。
eval('import os')
上記のコードを実行すると↓のようにエラーになります。
Traceback (most recent call last):
File "/tmp/evalstmt.py", line 1, in <module>
eval('import os')
File "<string>", line 1
import os
^
SyntaxError: invalid syntax
外部から変数を注入する
evalの第1引数には評価する式の文字列を渡しますが、第2引数にはglobals
を指定できます。
これはグローバルスコープの変数の定義です。
たとえば↓のように使います。
result = eval('a + 2', {'a': 1})
print(result) # 3
↑ではglobals
に{'a': 1}
を設定しています。
こうすると第1引数の式で変数a
を使うことができます。
第3引数にはlocals
を指定できます。
これはローカル変数の定義です。
result = eval('a + 2', {}, {'a': 1})
print(result) # 3
↑のように式で使うことができます。
外部から関数を注入する
globals
とlocals
には整数の他に関数なども渡すことができます。
def hello():
print('Hello')
return 1
result = eval('hello()', {'hello': hello})
print(result) # 1
↑のコードを実行すると↓の結果になります。
Hello
1
組み込みオブジェクトの利用を制限する
evalのglobals
に__builtins__
を指定すると、評価時に使える組み込み関数などを制限することができます。
__builtins__
にNone
を指定すると組み込み関数が使えなくなります。
eval('print(1)', {'__builtins__': None})
↑のコードを実行すると↓の結果になります。
Traceback (most recent call last):
File "/tmp/evalbuiltins.py", line 1, in <module>
eval('exec("hige")', {'__builtins__': None})
File "<string>", line 1, in <module>
TypeError: 'NoneType' object is not subscriptable
「Noneは呼び出しできない」と書かれています。
組み込みのprint()
がNone
になっているわけですね。
print()
のみを使えるようにしたい場合は↓のようにします。
eval('print(1)', {'__builtins__': {'print': print}})
↑のコードを実行すると↓の結果になります。
1
たとえばこの指定でlist()
などを呼び出してもうまくいきません。
eval('list()', {'__builtins__': {'print': print}})
Traceback (most recent call last):
File "/tmp/evallist.py", line 1, in <module>
eval('list()', {'__builtins__': {'print': print}})
File "<string>", line 1, in <module>
NameError: name 'list' is not defined
頭が悪い人のevalの使い方
evalの仕様について解説してきましたが、evalの使い方で注意が必要なのは、第1引数の指定方法です。
evalは式を評価しますが、組み込み関数なども呼び出すことができます。
これらの組み込み関数にはシステム側に影響を与えることも出来る関数なども含まれています。
たとえばWebアプリを作っていたとします。
そのWebアプリの機能で、ユーザーが入力した足し算の結果を表示する機能があったとします。
この機能を実装するにあたって、ユーザーの入力をevalに渡して、evalに足し算を計算させようとしたとします。
コードで言うと↓みたいな感じです。
def my_view():
expr = get_user_input() # ユーザーの入力を取得して
result = eval(expr) # evalに計算させて
render_result(result) # 結果を出力
こういったコードを書いてしまうと、Webアプリの脆弱性になってしまいます。
ソフトウェア開発では「ユーザーの入力は一切信用しない」という掟があります。
この掟を破ってしまうと↑のようなコードを書くことになってしまいます。
たとえばユーザーの入力に、危険な組み込み関数を呼び出すコードが書かれていたらどうなるでしょうか?
evalはおかまいなしに式を評価して実行してしまいます。
↑のようなコードを書くと、Webアプリのユーザーは自由にサーバー側のシステムにいたずらすることが出来るようになります。
というわけで、これは頭の悪い人のコードです。
もっとも、こういったミスはわれわれ凡人はみんなやることがあります。
ですので万が一頭の悪いコードを書いてしまっても気にしないようにしてください。
もちろん修正は必要ですけど。
おわりに
今回は頭の悪い人のevalの使い方を解説しました。
頭の悪い人と言っても、われわれはみな頭が悪い人になる可能性を秘めています。
要はそれを予防する意識が必要だということです。
ヒューマンエラーは0にすることはできませんが、0に近づけることは出来ます。
0に近づけていきたいところですね。
🦝 < evalはけっこうこわい関数
🐭 < 使う場合は要注意