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

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

Django SessionSESSION_ENGINEcached_db

~12 دقیقه مطالعه • بروزرسانی ۲۳ اسفند ۱۴۰۴

مقدمه

جنگو از سیستم session برای ذخیره‌سازی داده‌های مرتبط با هر کاربر استفاده می‌کند. این داده‌ها در سمت سرور ذخیره می‌شوند و تنها یک session ID در کوکی کاربر قرار می‌گیرد. این ساختار باعث افزایش امنیت و انعطاف‌پذیری در مدیریت وضعیت کاربران می‌شود.


فعال‌سازی Session

برای فعال‌سازی سشن، باید SessionMiddleware را در تنظیمات پروژه فعال کنید:


MIDDLEWARE = [
    ...
    "django.contrib.sessions.middleware.SessionMiddleware",
    ...
]

اگر قصد استفاده از سشن را ندارید، می‌توانید این خط را از MIDDLEWARE و همچنین 'django.contrib.sessions' را از INSTALLED_APPS حذف کنید.


پیکربندی موتور Session

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


استفاده از دیتابیس برای Session

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

  • 'django.contrib.sessions' را به INSTALLED_APPS اضافه کنید.
  • دستور manage.py migrate را اجرا کنید.

این روش پایدار و مناسب برای اکثر پروژه‌ها است.


استفاده از کش برای Session

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


نکته مهم:

  • فقط از Memcached یا Redis استفاده کنید.
  • کش محلی (locmem) مناسب تولید نیست.

۱. موتور cached_db

این موتور داده‌ها را هم در دیتابیس و هم در کش ذخیره می‌کند:


SESSION_ENGINE = "django.contrib.sessions.backends.cached_db"

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


۲. موتور cache

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


SESSION_ENGINE = "django.contrib.sessions.backends.cache"

این روش سریع‌تر است اما در صورت پاک شدن کش، داده‌های سشن از بین می‌روند.


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


استفاده از فایل برای Session

برای ذخیره‌سازی فایل‌محور:


SESSION_ENGINE = "django.contrib.sessions.backends.file"

برای تعیین مسیر فایل‌ها:


SESSION_FILE_PATH = "/path/to/sessions"

مطمئن شوید که وب‌سرور دسترسی خواندن و نوشتن دارد.


استفاده از کوکی برای Session

برای ذخیره‌سازی سشن در کوکی:


SESSION_ENGINE = "django.contrib.sessions.backends.signed_cookies"

در این روش داده‌ها با SECRET_KEY امضا می‌شوند اما رمزگذاری نمی‌شوند.


نکات امنیتی مهم

  • داده‌ها قابل خواندن هستند، اما قابل تغییر نیستند.
  • کوکی‌ها محدودیت 4096 بایت دارند.
  • این روش در برابر حملات replay آسیب‌پذیر است.
  • سشن‌ها پس از logout باطل نمی‌شوند.

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


SESSION_COOKIE_HTTPONLY = True

عملکرد

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


جمع‌بندی

جنگو گزینه‌های متنوعی برای ذخیره‌سازی سشن ارائه می‌دهد. انتخاب بهترین موتور به نیازهای پروژه، سطح امنیت، پایداری و سرعت مورد انتظار بستگی دارد. برای پروژه‌های تولیدی، استفاده از cached_db یا database معمولاً بهترین انتخاب است.


مقدمه

زمانی که SessionMiddleware فعال باشد، هر شیء HttpRequest شامل ویژگی session خواهد بود. این ویژگی یک آبجکت شبیه دیکشنری است که امکان ذخیره و بازیابی داده‌های کاربر را فراهم می‌کند. شما می‌توانید در هر نقطه از ویو، داده‌های سشن را بخوانید یا تغییر دهید.


کلاس SessionBase

تمام سشن‌ها در جنگو از کلاس SessionBase ارث‌بری می‌کنند. این کلاس مجموعه‌ای از متدهای دیکشنری‌مانند را ارائه می‌دهد که هم نسخه هم‌زمان دارند و هم نسخه غیرهم‌زمان.


متدهای دیکشنری‌مانند

خواندن و نوشتن مقدار

  • request.session['fav_color'] → خواندن مقدار
  • request.session['fav_color'] = 'blue' → نوشتن مقدار
  • del request.session['fav_color'] → حذف مقدار
  • 'fav_color' in request.session → بررسی وجود کلید

استفاده از get() و aget()

fav_color = request.session.get('fav_color', 'red')
fav_color = await request.session.aget('fav_color', 'red')

به‌روزرسانی چند مقدار

request.session.update({'fav_color': 'red'})
await request.session.aupdate({'fav_color': 'red'})

استفاده از pop() و apop()

fav_color = request.session.pop('fav_color', 'blue')
fav_color = await request.session.apop('fav_color', 'blue')

دریافت کلیدها و مقادیر

  • keys() / akeys()
  • values() / avalues()
  • items() / aitems()

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

  • setdefault() / asetdefault()
  • clear()

متدهای مدیریتی سشن

flush() / aflush()

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


is_empty()

اگر سشن هیچ کلیدی نداشته باشد، مقدار True برمی‌گرداند.


کوکی‌های تست

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


  • set_test_cookie() / aset_test_cookie()
  • test_cookie_worked() / atest_cookie_worked()
  • delete_test_cookie() / adelete_test_cookie()

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


مدیریت زمان انقضای سشن

set_expiry(value) / aset_expiry(value)

این متد زمان انقضای سشن را تعیین می‌کند. مقدار ورودی می‌تواند:

  • عدد صحیح: انقضا پس از N ثانیه (مثلاً set_expiry(300) → پنج دقیقه)
  • datetime یا timedelta: انقضا در زمان مشخص
  • 0: انقضا هنگام بستن مرورگر
  • None: استفاده از تنظیمات پیش‌فرض

نکته: خواندن سشن فعالیت محسوب نمی‌شود؛ فقط تغییرات باعث تمدید زمان انقضا می‌شوند.


get_expiry_age() / aget_expiry_age()

تعداد ثانیه‌های باقی‌مانده تا انقضای سشن را برمی‌گرداند.


get_expiry_date() / aget_expiry_date()

تاریخ دقیق انقضای سشن را برمی‌گرداند.


get_expire_at_browser_close()

بررسی می‌کند که آیا سشن با بسته شدن مرورگر منقضی می‌شود یا خیر.


پاک‌سازی سشن‌ها

clear_expired() / aclear_expired()

سشن‌های منقضی‌شده را از ذخیره‌ساز حذف می‌کند. دستور clearsessions از این متد استفاده می‌کند.


امنیت: جلوگیری از Session Fixation

cycle_key() / acycle_key()

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


جمع‌بندی

سیستم سشن جنگو یک API قدرتمند و انعطاف‌پذیر برای مدیریت داده‌های کاربر ارائه می‌دهد. با پشتیبانی از متدهای هم‌زمان و غیرهم‌زمان، کنترل دقیق زمان انقضا، کوکی‌های تست و قابلیت‌های امنیتی مانند cycle_key() و flush()، توسعه‌دهندگان می‌توانند ویژگی‌های مبتنی بر سشن را با اطمینان و کارایی بالا پیاده‌سازی کنند.


مقدمه

جنگو داده‌های session را با استفاده از یک سیستم سریال‌سازی ذخیره می‌کند. به‌صورت پیش‌فرض، این داده‌ها با JSON سریال‌سازی می‌شوند. این روش امن، سازگار و مناسب برای بک‌اندهای مختلف، به‌ویژه بک‌اند cookie است. با استفاده از تنظیم SESSION_SERIALIZER می‌توان این رفتار را تغییر داد.


سریال‌سازی پیش‌فرض با JSON

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


نمونه تبدیل کلیدها

# مقداردهی اولیه
request.session[0] = "bar"

# پس از سریال‌سازی و بازیابی
request.session[0]      # KeyError
request.session["0"]    # 'bar'

همچنین داده‌هایی که قابل تبدیل به UTF-8 نیستند، مانند بایت‌های '\xd9'، قابل ذخیره‌سازی نیستند.


خطرات امنیتی استفاده از pickle

اگر از pickle برای سریال‌سازی سشن استفاده کنید و SECRET_KEY لو برود، مهاجم می‌تواند داده‌ای بسازد که هنگام unpickle شدن روی سرور کد مخرب اجرا کند. این یک آسیب‌پذیری جدی Remote Code Execution است.


نوشتن سریالایزر سفارشی

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


کلاس سریالایزر باید دو متد داشته باشد:

  • dumps(self, obj)
  • loads(self, data)

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


اصول کار با شیء سشن

  • از کلیدهای رشته‌ای استفاده کنید.
  • کلیدهایی که با _ شروع می‌شوند رزرو شده‌اند.
  • هرگز request.session را با یک آبجکت جدید جایگزین نکنید.
  • از آن مانند یک دیکشنری استفاده کنید.

مثال‌های کاربردی

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

def post_comment(request, new_comment):
    if request.session.get("has_commented", False):
        return HttpResponse("You've already commented.")
    c = comments.Comment(comment=new_comment)
    c.save()
    request.session["has_commented"] = True
    return HttpResponse("Thanks for your comment!")

ورود کاربر

def login(request):
    m = Member.objects.get(username=request.POST["username"])
    if m.check_password(request.POST["password"]):
        request.session["member_id"] = m.id
        return HttpResponse("You're logged in.")
    return HttpResponse("Your username and password didn't match.")

خروج کاربر

def logout(request):
    try:
        del request.session["member_id"]
    except KeyError:
        pass
    return HttpResponse("You're logged out.")

تست کوکی‌ها

برای بررسی پشتیبانی مرورگر از کوکی‌ها باید در یک درخواست set_test_cookie() و در درخواست بعدی test_cookie_worked() را فراخوانی کنید.


نمونه

def login(request):
    if request.method == "POST":
        if request.session.test_cookie_worked():
            request.session.delete_test_cookie()
            return HttpResponse("You're logged in.")
        return HttpResponse("Please enable cookies and try again.")
    request.session.set_test_cookie()
    return render(request, "foo/login_form.html")

استفاده از سشن خارج از ویو

برای مدیریت سشن‌ها خارج از ویو می‌توانید از SessionStore استفاده کنید. بهتر است آن را بر اساس SESSION_ENGINE بارگذاری کنید.


ایجاد و بازیابی سشن

from importlib import import_module
from django.conf import settings

SessionStore = import_module(settings.SESSION_ENGINE).SessionStore

s = SessionStore()
s["last_login"] = 1376587691
s.create()
key = s.session_key

s = SessionStore(session_key=key)
s["last_login"]

دسترسی مستقیم به مدل Session

from django.contrib.sessions.models import Session

s = Session.objects.get(pk=key)
data = s.get_decoded()

زمان ذخیره‌سازی سشن

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


نمونه

request.session["foo"] = "bar"      # ذخیره می‌شود
del request.session["foo"]          # ذخیره می‌شود
request.session["foo"] = {}         # ذخیره می‌شود

request.session["foo"]["bar"] = 1   # ذخیره نمی‌شود
request.session.modified = True     # اجبار ذخیره

سشن‌های موقت و پایدار

تنظیم SESSION_EXPIRE_AT_BROWSER_CLOSE تعیین می‌کند که سشن با بسته شدن مرورگر منقضی شود یا خیر. این مقدار را می‌توان با set_expiry() برای هر سشن تغییر داد.


برخی مرورگرها مانند Chrome ممکن است سشن‌ها را پس از بسته شدن نیز بازیابی کنند، بنابراین باید هنگام تست این موضوع را در نظر داشت.


جمع‌بندی

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


مقدمه

در فریم‌ورک Django مدیریت Session نقش مهمی در حفظ وضعیت کاربران دارد. اما اگر پاک‌سازی دوره‌ای انجام نشود، داده‌های سشن در Session Store انباشته شده و باعث افزایش حجم دیتابیس یا فایل‌ها می‌شود. در این مقاله به بررسی نحوه پاک‌سازی، تنظیمات مهم و روش‌های توسعه یک Session Engine سفارشی می‌پردازیم.


چرا Session Store نیاز به پاک‌سازی دارد؟

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


مشکل اصلی

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

پاک‌سازی سشن‌ها با دستور clearsessions

جنگو دستور clearsessions را برای حذف سشن‌های منقضی‌شده ارائه می‌دهد. اجرای این دستور باید به صورت دوره‌ای انجام شود؛ معمولاً از طریق cron job.


python manage.py clearsessions

این دستور رکوردهای منقضی‌شده را از django_session حذف می‌کند و فضای ذخیره‌سازی را آزاد نگه می‌دارد.


Backendهایی که نیاز به پاک‌سازی ندارند

  • Cache Backend: داده‌های منقضی‌شده را خودکار حذف می‌کند.
  • Cookie Backend: داده‌ها در مرورگر کاربر ذخیره می‌شود.

تنظیمات مهم مرتبط با Session

جنگو تنظیمات متعددی برای کنترل رفتار سشن ارائه می‌دهد:

  • SESSION_ENGINE
  • SESSION_COOKIE_AGE
  • SESSION_EXPIRE_AT_BROWSER_CLOSE
  • SESSION_SAVE_EVERY_REQUEST
  • SESSION_FILE_PATH
  • SESSION_SERIALIZER

امنیت Session

یکی از تهدیدهای مهم، حمله Session Fixation است. این حمله زمانی رخ می‌دهد که یک زیردامنه غیرقابل‌اعتماد بتواند کوکی سشن را برای کل دامنه تنظیم کند. بنابراین باید از تنظیم اشتباه SESSION_COOKIE_DOMAIN جلوگیری کرد.


نمونه سناریوی حمله

اگر good.example.com مقدار SESSION_COOKIE_DOMAIN را روی example.com تنظیم کند، زیردامنه‌ای مانند bad.example.com می‌تواند کوکی سشن مخرب ارسال کند.


جزئیات فنی SessionStore

جنگو برای مدیریت سشن‌ها از کلاس SessionStore استفاده می‌کند. این کلاس بسته به SESSION_ENGINE از ماژول‌های مختلف بارگذاری می‌شود.


متدهای مهم SessionStore

  • exists()
  • create()
  • save()
  • delete()
  • load()
  • clear_expired()

نسخه‌های Async

  • aexists()
  • acreate()
  • asave()
  • adelete()
  • aload()
  • aclear_expired()

ساخت Session Engine سفارشی

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


نمونه مدل سفارشی

from django.contrib.sessions.backends.db import SessionStore as DBStore
from django.contrib.sessions.base_session import AbstractBaseSession
from django.db import models

class CustomSession(AbstractBaseSession):
    account_id = models.IntegerField(null=True, db_index=True)

    @classmethod
    def get_session_store_class(cls):
        return SessionStore

نمونه SessionStore سفارشی

class SessionStore(DBStore):
    @classmethod
    def get_model_class(cls):
        return CustomSession

    def create_model_instance(self, data):
        obj = super().create_model_instance(data)
        try:
            account_id = int(data.get("_auth_user_id"))
        except (ValueError, TypeError):
            account_id = None
        obj.account_id = account_id
        return obj

Session IDs در URL

جنگو برخلاف برخی زبان‌ها مانند PHP، هرگز Session ID را در URL قرار نمی‌دهد. این تصمیم برای جلوگیری از حملات مبتنی بر Referer Header گرفته شده است.


جمع‌بندی

پاک‌سازی دوره‌ای سشن‌ها در پروژه‌های جنگو ضروری است، به‌ویژه زمانی که از Database Backend یا File Backend استفاده می‌کنید. با اجرای منظم clearsessions، تنظیم صحیح پارامترهای سشن و رعایت نکات امنیتی، می‌توان عملکرد و امنیت سیستم را تضمین کرد. همچنین امکان توسعه موتورهای سشن سفارشی، انعطاف‌پذیری بیشتری برای نیازهای خاص فراهم می‌کند.


نوشته و پژوهش شده توسط دکتر شاهین صیامی