4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

この記事は 「Pythonって、たぶんそういう意味じゃない」シリーズ
「誰でもわかるセキュリティの話」シリーズ の両方に属する記事です。
Python歴5年以上、メインで使い続けてきた変態が書いています。

この記事は 個人の経験に基づくポエムです。
環境・用途・経験によって異なる場合があります。

Discordの電卓ボットを作っていた。

ユーザーが2^3+1と入力したら答えを返す。シンプルな機能だ。
実装はこうした。

expr = user_input.replace("^", "**")
result = eval(expr)

^**に変換して、あとはeval()に投げるだけ。
動く。シンプル。最高じゃないか。

「使うな」とよく言われるけど、なぜかはあまり考えていなかった。
サーバー内だけで使うボットだから、まあいいかと思っていた。

eval()は何をするのか

result = eval("1 + 2 * 3")
print(result)  # 7

文字列をPythonのコードとして実行する。
動的に式を評価したいとき、これが手っ取り早い。

「数式を文字列で受け取って計算したい」というケースに、ぴったりはまる。

なぜ「使うな」と言われるのか

問題は何でも実行できるという点だ。

# こんな入力が来たら?
eval("__import__('os').system('rm -rf /')")

計算式のつもりで受け取った文字列が、
システムコマンドの実行になる。

電卓ボットに2+2ではなく、これを入力されたら——
サーバーのファイルが消える。

「ユーザーの入力をそのままeval()に渡す」は致命的なセキュリティホールになる。

「サーバー内だけだからいい」は本当か

自分もそう思っていた。
身内だけのDiscordサーバーなら問題ないんじゃないか、と。

でも考えてみると——

  • サーバーメンバーが増える可能性がある
  • コードを他のプロジェクトに流用するかもしれない
  • 悪意がなくても変な入力をする人はいる

「今は安全」は「ずっと安全」じゃない。

じゃあどうするか

数式の評価なら、より安全な代替手段がある。

入力を数式のみに制限する

import re

def safe_eval(expr):
    # 数字と演算子以外を弾く
    if not re.match(r'^[\d\s\+\-\*\/\.\(\)\*\^]+$', expr):
        raise ValueError("無効な入力です")
    expr = expr.replace("^", "**")
    return eval(expr)

ast.literal_eval()を使う
リテラルのみ評価するので任意コードは実行できない。
ただし数式の評価はできないので用途が限られる。

sympynumexprを使う
数式評価に特化したライブラリ。
任意コードの実行リスクがなく、複雑な計算式も扱える。

  • どうしてもeval()入力を正規表現で厳密に制限する
  • 数式評価に特化 → sympynumexpr
  • リテラルのみ → ast.literal_eval()

まとめ

電卓ボットにeval()を使うこと自体は間違いじゃない。
でも入力を信頼しすぎるのが問題だ。

「使うな」の本当の意味は「入力を検証せずに使うな」だと思っている。


「Pythonって、たぶんそういう意味じゃない」 シリーズでは、
こういう"なんとなくで語られがちなPython"を言語化していきます。

👉 ストックをフォローしておくと次の記事を見逃しません!


「誰でもわかるセキュリティの話」 シリーズでは、
セキュリティの仕組みを言語化していきます。

👉 ストックをフォローしておくと次の記事を見逃しません!

4
3
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?