أمان التطبيقات
أمان API
4 دقيقة للقراءة
واجهات برمجة التطبيقات (APIs) هي العمود الفقري للتطبيقات الحديثة. يغطي هذا الدرس أنماط المصادقة والثغرات الشائعة وأفضل ممارسات الأمان التي ستواجهها في المقابلات.
أنماط المصادقة
مفاتيح API مقابل OAuth 2.0 مقابل JWT
| الطريقة | حالة الاستخدام | مستوى الأمان | الإدارة |
|---|---|---|---|
| مفاتيح API | خادم إلى خادم، تطبيقات بسيطة | منخفض (تتسرب بسهولة) | تدوير يدوي |
| OAuth 2.0 | وصول الطرف الثالث، موافقة المستخدم | عالي | تحديث الرمز |
| JWT | مصادقة بدون حالة | متوسط-عالي | التحقق من التوقيع |
سؤال المقابلة
س: "متى تستخدم مفاتيح API مقابل OAuth 2.0؟"
الإجابة:
- مفاتيح API: الخدمات الداخلية، معرّف تحديد المعدل، العمليات غير الحساسة
- OAuth 2.0: تكاملات الطرف الثالث، الوصول لبيانات المستخدم، التفويض المفوض
- JWT: تطبيقات الجوال، الخدمات المصغرة، عندما تحتاج مصادقة بدون حالة مع مطالبات
تدفقات OAuth 2.0
تدفق رمز التفويض (الأكثر أماناً)
┌─────────┐ ┌──────────────┐
│ المستخدم│ │ خادم المصادقة│
└────┬────┘ └──────┬───────┘
│ │
│ 1. النقر على "تسجيل الدخول بـ Google" │
├────────────────────────────────────────▶│
│ │
│ 2. إعادة التوجيه لخادم المصادقة │
│◀────────────────────────────────────────┤
│ │
│ 3. المستخدم يصادق ويوافق │
├────────────────────────────────────────▶│
│ │
│ 4. إعادة التوجيه مع رمز المصادقة │
│◀────────────────────────────────────────┤
│ │ │
│ │ 5. استبدال الرمز بالرموز │
│ └───────────────────────────────▶│
│ │
│ 6. إرجاع رموز الوصول والتحديث │
│◀────────────────────────────────────────┤
امتداد PKCE (مطلوب للعملاء العامين)
import hashlib
import base64
import secrets
# إنشاء رمز التحقق
code_verifier = secrets.token_urlsafe(32)
# إنشاء تحدي الرمز
code_challenge = base64.urlsafe_b64encode(
hashlib.sha256(code_verifier.encode()).digest()
).decode().rstrip('=')
# الاستخدام في طلب التفويض
auth_url = f"{AUTH_ENDPOINT}?client_id={CLIENT_ID}&code_challenge={code_challenge}&code_challenge_method=S256"
OWASP API Security Top 10
| الترتيب | الثغرة | مثال |
|---|---|---|
| API1 | تفويض مستوى الكائن المعطل | /api/users/123 متاح للمستخدم 456 |
| API2 | المصادقة المعطلة | رموز ضعيفة، لا تحديد معدل على تسجيل الدخول |
| API3 | تفويض مستوى خاصية الكائن المعطل | تعيين جماعي يسمح برفع الدور |
| API4 | استهلاك موارد غير مقيد | لا حدود للصفحات، استنفاد الذاكرة |
| API5 | تفويض مستوى الوظيفة المعطل | المستخدم يصل لنقاط نهاية المشرف |
| API6 | وصول غير مقيد لتدفقات العمل الحساسة | سمسرة التذاكر الآلية |
| API7 | تزوير طلب جانب الخادم | جلب URLs عشوائية عبر API |
| API8 | التكوين الأمني الخاطئ | أخطاء مطولة، بيانات اعتماد افتراضية |
| API9 | إدارة المخزون غير السليمة | إصدارات API غير موثقة/منسية |
| API10 | استهلاك غير آمن للـ APIs | الوثوق باستجابات API الطرف الثالث |
تفويض مستوى الكائن المعطل (BOLA)
# ثغرة - لا فحص ملكية
@app.get("/api/orders/{order_id}")
def get_order(order_id: int):
return db.query(Order).get(order_id)
# آمن - التحقق من الملكية
@app.get("/api/orders/{order_id}")
def get_order(order_id: int, current_user: User = Depends(get_current_user)):
order = db.query(Order).get(order_id)
if order.user_id != current_user.id:
raise HTTPException(status_code=403, detail="Forbidden")
return order
تحديد المعدل
أنماط التنفيذ
from fastapi import Request, HTTPException
from datetime import datetime, timedelta
import redis
redis_client = redis.Redis()
def rate_limit(key: str, limit: int, window: int):
"""
تحديد معدل دلو الرموز.
Args:
key: معرف فريد (user_id، IP، مفتاح API)
limit: أقصى طلبات لكل نافذة
window: نافذة الوقت بالثواني
"""
current = redis_client.get(key)
if current is None:
# أول طلب
redis_client.setex(key, window, 1)
return True
if int(current) >= limit:
raise HTTPException(
status_code=429,
detail="Rate limit exceeded",
headers={"Retry-After": str(window)}
)
redis_client.incr(key)
return True
# الاستخدام
@app.get("/api/search")
def search(request: Request, q: str):
rate_limit(f"search:{request.client.host}", limit=100, window=60)
return perform_search(q)
التحقق من المدخلات
from pydantic import BaseModel, Field, validator
import re
class UserCreateRequest(BaseModel):
username: str = Field(..., min_length=3, max_length=30)
email: str = Field(..., max_length=255)
password: str = Field(..., min_length=12)
@validator('username')
def username_alphanumeric(cls, v):
if not re.match(r'^[a-zA-Z0-9_]+$', v):
raise ValueError('Username must be alphanumeric')
return v
@validator('email')
def email_valid(cls, v):
if not re.match(r'^[^@]+@[^@]+\.[^@]+$', v):
raise ValueError('Invalid email format')
return v.lower()
@validator('password')
def password_strong(cls, v):
if not re.search(r'[A-Z]', v):
raise ValueError('Password must contain uppercase')
if not re.search(r'[a-z]', v):
raise ValueError('Password must contain lowercase')
if not re.search(r'\d', v):
raise ValueError('Password must contain digit')
return v
رؤوس الأمان
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
# تكوين CORS
app.add_middleware(
CORSMiddleware,
allow_origins=["https://trusted-domain.com"],
allow_credentials=True,
allow_methods=["GET", "POST"],
allow_headers=["Authorization", "Content-Type"],
)
@app.middleware("http")
async def add_security_headers(request, call_next):
response = await call_next(request)
response.headers["X-Content-Type-Options"] = "nosniff"
response.headers["X-Frame-Options"] = "DENY"
response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains"
response.headers["Content-Security-Policy"] = "default-src 'self'"
return response
نصيحة المقابلة: عند مناقشة أمان API، اذكر دائماً مبدأ الدفاع في العمق: المصادقة في البوابة، التفويض في مستوى الخدمة، والتحقق من المدخلات في كل نقطة نهاية.
في الدرس التالي، سنغطي DevSecOps وأمان CI/CD. :::