قبل از آشنایی با دلیل اشتباه بودن این کار باید با کار این توابع آشنا بشیم. در این مطلب از پایتون و C++ استفاده میشه اما این موارد تقریبا در بیشتر زبان های برنامه نویسی درسته.
این تابع کد پایتون ورودی را اجرا میکنه و خروجی آن را به خروجی تابع میده. مثال:
>>> eval('print("running in eval")') running in eval >>> eval("10+20") 30
یکی از استفاده های بسیار زیاد از این تابع در ماشین حساب ها هست. زیرا همینطور که در بالا میبینید این تابع خروجی عددی عبارت های ریاضی رو به خروجی میده. مثال:
>>> print(f"ans:{eval(input(':'))}") :10**20 ans:100000000000000000000
اما با همون آشنایی اول با این تابع متوجه میشیم که یک مشکل امنیتی بسیار بزرگی در برنامه ایجاد میکنه ، اگر کاربر کد مخرب پایتون به ورودی بده برنامه اون کد رو اجرا میکنه! مثل لیست فایل های روی سیستم رو بگیره ، مقدار متغییر هارو تغییر بده و ... مثال:
>>> password = "secret password" >>> print(f"ans:{eval(input())}") password ans:secret password
شاید بگید که به کاربر اجازه ورود چیزی بجز اعداد علامت ها نمیدیدید. اما در این صورت هم کاربر میتونه اعداد بسیار بزرگی بزنه و...
برای همین استفاده از تابع eval کار درستی نیست.
این تابع کامند یا فایل ورودی رو در shell سیستم عامل اجرا میکنه. برای دیدن محل فایل یک کامند میتونید از برنامه which یا whereis در ترمینال استفاده کنید:
alireza@fedora ~> which cat /usr/bin/cat
این برنامه ( و بیشتر برنامه ها ) از ENV variable استفاده میکنن تا بدونن توی کجا باید دنبال برنامه باشن. ENV variables مقدار هایی هستن که سیستم عامل به هربرنامه ای که اجرا میشه ارسال میکنه. یکی از این مقادیر PATH نام داره. هر برنامه ای که بخواد کامندی رو اجرا کنه داخل دایرکتوری های متغییر PATH جستوجو میکنه و اگر پیدا نشد دیگه ادامه نمیده.
متغییر PATH رو میشه به شکل زیر از ترمینال مشاهده کرد:
alireza@fedora ~> echo $PATH /usr/local/bin /usr/local/sbin /usr/bin /usr/sbin
حالا چرا از تابع system استفاده نکنیم؟
برنامه زیر رو در نظر بگیرید:
#include <iostream> int main() { system("date"); }
که با دستور زیر کامپایل شده:
g++ main.cpp -o main
برنامه کامپایل شده این خروجی میده:
Sun May 16 12:03:40 PM +0430 2021
حالا فرض کنید میخواید یک دستور دیگه بجز date اجرا کنید ( مثلا یک دستور مخرب ) اما دسترسی به ویرایش خود باینری برنامه ندارید.
یک برنامه جدید به اسم date کامپایل میکنیم:
#include <iostream> int main() { std::cout << "My evil program!!" << std::endl; }
و متغییر PATH رو به محل برنامه تغییر میدیم و برنامه رو اجرا میکنیم:
> export PATH=. > echo $PATH . > ./main My evil program!!
و از این بدتر ، اگر برنامه رو فقط بتونیم بخونیم و برنامه برای کاربر root باشه ، میتونیم دسترسی به shell کاربر root هم داشته باشیم!
برای همین استفاده از این دو تابع درست نیست و جزو توابع خطرناکه.