throttling, abuse-protection & logging/monitoring pass
This commit is contained in:
@@ -4,6 +4,7 @@ from drf_spectacular.types import OpenApiTypes
|
||||
from drf_spectacular.utils import OpenApiParameter, extend_schema
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.throttling import ScopedRateThrottle
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from . import bazaar
|
||||
@@ -21,6 +22,39 @@ from .serializers import (
|
||||
from .services import apply_order_payment_result, create_order
|
||||
|
||||
|
||||
class GuestAwareBazaarThrottle(ScopedRateThrottle):
|
||||
"""
|
||||
⚠️ اضافه شده: همون مشکلِ GuestAwareAIThrottle (ai_tutor/views.py)،
|
||||
GuestAwareFamilyThrottle (family/views.py) و GuestAwareDiscountThrottle/
|
||||
GuestAwarePurchaseThrottle (discount/views.py، subscription/views.py)
|
||||
اینجا هم هست. ScopedRateThrottle استاندارد برای درخواستِ احرازهویتشده
|
||||
از request.user.pk بهعنوانِ کلیدِ throttle استفاده میکنه؛ چون
|
||||
GuestSessionView با هر deviceId جدید یک کاربرِ مهمانِ تازه (pk جدید)
|
||||
میسازه، یک مهاجم میتونست با ساختنِ پیدرپیِ حسابهای مهمان، برای هر
|
||||
حساب یک bucket تازه از throttle_scope 'bazaar_verify' بگیره — درحالیکه
|
||||
هر درخواستِ پذیرفتهشده یک فراخوانیِ سرور-به-سرور به API بازار رو
|
||||
تریگر میکنه.
|
||||
|
||||
دقیقاً طبقِ همون الگو: فقط برای کاربرهای مهمان (is_guest=True) کلیدِ
|
||||
cache از pk کاربر به IP کلاینت (get_ident) تغییر میکنه، پس همهی
|
||||
حسابهای مهمانِ ساختهشده از یک IP یک bucket مشترک دارن و چرخشِ حساب
|
||||
دیگه bucket تازه نمیده. رفتارِ throttle برای کاربرهای غیرمهمان
|
||||
(ثبتنامشده) دقیقاً مثلِ قبل (کلید بر اساسِ pk) باقی میمونه؛ منطقِ
|
||||
verify/order ownership/transaction/apply-payment دستنخوردهست.
|
||||
"""
|
||||
|
||||
def get_cache_key(self, request, view):
|
||||
user = request.user
|
||||
if user and user.is_authenticated and getattr(user, 'is_guest', False):
|
||||
ident = f'guest-ip:{self.get_ident(request)}'
|
||||
elif user and user.is_authenticated:
|
||||
ident = user.pk
|
||||
else:
|
||||
ident = self.get_ident(request)
|
||||
|
||||
return self.cache_format % {'scope': self.scope, 'ident': ident}
|
||||
|
||||
|
||||
class CreateOrderView(APIView):
|
||||
"""
|
||||
POST /api/payment/orders — ساخت سفارش (فراخوانیشده از اپ Subscription)
|
||||
@@ -87,6 +121,16 @@ class PaymentWebhookView(APIView):
|
||||
"""
|
||||
|
||||
permission_classes = [HasWebhookSecret]
|
||||
# ⚠️ اضافه شده: این endpoint قبلاً هیچ throttleای نداشت، درحالیکه فقط
|
||||
# با مقایسهی یک هدرِ ثابت (X-Webhook-Secret) محافظت میشه؛ بدونِ
|
||||
# throttle، تعدادِ نامحدودی تلاش برای حدسِ این سکرت (یا fuzz کردنِ
|
||||
# orderId بعد از افشای احتمالیِ سکرت) ممکن بود، درحالیکه یک حدسِ
|
||||
# درست باعثِ paid شدنِ واقعیِ سفارش و فعالسازیِ اشتراک میشه. چون
|
||||
# این caller یک کاربرِ اپ نیست (خودِ درگاهِ پرداخته)، از یک
|
||||
# ScopedRateThrottleِ سادهٔ IP-محور استفاده شده، نه الگویِ
|
||||
# GuestAware*Throttle (که نیازمندِ request.user است).
|
||||
throttle_scope = 'payment_webhook'
|
||||
throttle_classes = [ScopedRateThrottle]
|
||||
|
||||
# ⚠️ افزوده شده: مستندسازی هدر X-Webhook-Secret که قبلاً در Swagger UI
|
||||
# قابل تنظیم/تست نبود.
|
||||
@@ -138,6 +182,21 @@ class BazaarVerifyPurchaseView(APIView):
|
||||
"""
|
||||
|
||||
permission_classes = [IsAuthenticated]
|
||||
# ⚠️ افزوده شده: این endpoint یک فراخوانیِ سرور-به-سرور به API بازار
|
||||
# (verify_purchase) رو تریگر میکنه؛ بدون throttle مخصوص، یک کاربر
|
||||
# میتونست با ارسال مکرر purchaseToken نامعتبر، بار زیادی روی سرویس
|
||||
# بازار و بکاند بندازه. throttle_scope مجزا (bazaar_verify) جدا از
|
||||
# نرخهای دیگه (مثل otp_*) تنظیم شده.
|
||||
# ⚠️ اصلاح شده: چون کاربرهای مهمان (GuestSessionView) با هر deviceId
|
||||
# جدید یک pk تازه میگیرن، ScopedRateThrottleِ ساده (کلید بر اساسِ
|
||||
# user.pk) توسطِ چرخشِ حساب مهمان قابلدورزدن بود. حالا از
|
||||
# GuestAwareBazaarThrottle استفاده میشه که برای کاربرهای مهمان کلید
|
||||
# رو بر اساسِ IP میسازه و برای کاربرهای غیرمهمان رفتارِ per-user
|
||||
# قبلی رو دستنخورده نگه میداره. نرخ هم از ۲۰/hour به ۱۰/hour
|
||||
# سختگیرانهتر شد چون هر درخواستِ پذیرفتهشده یک تماسِ سرور-به-سرورِ
|
||||
# واقعی با بازار میزنه.
|
||||
throttle_scope = 'bazaar_verify'
|
||||
throttle_classes = [GuestAwareBazaarThrottle]
|
||||
|
||||
@extend_schema(
|
||||
request=BazaarVerifyRequestSerializer,
|
||||
|
||||
Reference in New Issue
Block a user