امروزه تقریباً هر وبسایت یا اپلیکیشن مدرنی برای ارتباط بین بخشهای مختلف خود از API استفاده میکند. اپلیکیشن موبایل، پنل مدیریت، سایت فروشگاهی، سرویسهای شخص ثالث و حتی بسیاری از ابزارهای اتوماسیون، همگی میتوانند از طریق API با سرور ارتباط داشته باشند.
اما ساختن یک API فقط به معنی دریافت Request و برگرداندن JSON نیست.
اگر API بهدرستی امن نشود، مهاجم میتواند اطلاعات کاربران را مشاهده کند، به منابع دیگران دسترسی پیدا کند، درخواستهای زیادی به سرور ارسال کند، توکنهای کاربران را سرقت یا دوباره استفاده کند و حتی در شرایطی به دیتابیس آسیب بزند.
در این مقاله با مفهوم امنیت API در PHP آشنا میشویم و یک معماری عملی برای ساخت API امن بررسی میکنیم.
امنیت API در PHP چیست؟
امنیت API مجموعهای از روشها و مکانیزمهایی است که برای محافظت از API در برابر دسترسی غیرمجاز، سرقت اطلاعات، سوءاستفاده از Endpointها، حملات خودکار و دستکاری درخواستها استفاده میشود.
یک API امن معمولاً فقط به یک مکانیزم محدود نمیشود.
برای مثال، معماری امنیتی یک API میتواند شامل این لایهها باشد:
HTTPS
↓
CORS
↓
Rate Limiting
↓
Authentication
↓
Authorization
↓
Input Validation
↓
Database Security
↓
Error Handling
↓
Logging & Monitoring
بنابراین نباید تصور کنیم که استفاده از JWT یا API Key بهتنهایی API را امن میکند.
# مهمترین تهدیدهای امنیتی API
قبل از طراحی سیستم امنیتی باید بدانیم API در برابر چه تهدیدهایی قرار دارد.
برخی از مهمترین موارد عبارتاند از:
- دسترسی بدون احراز هویت
- دسترسی کاربر به اطلاعات کاربر دیگر
- سرقت Token
- استفاده مجدد از Token
- Brute Force
- Credential Stuffing
- SQL Injection
- Mass Assignment
- سوءاستفاده از Endpointها
- ارسال دادههای نامعتبر
- حملات Replay
- سوءاستفاده از Webhook
- افشای اطلاعات حساس در Errorها
- ثبت Token و Password در Log
- حملات مربوط به CORS
- سوءاستفاده از Upload API
در ادامه مهمترین این موارد را بررسی میکنیم.
# HTTPS اولین لایه امنیت API است
قبل از هر چیز باید ارتباط بین Client و Server رمزنگاری شود.
API نباید در محیط Production از HTTP معمولی استفاده کند.
بهجای:
http://example.com/api/users
باید از:
https://example.com/api/users
استفاده شود.
HTTPS از افشای اطلاعاتی مانند موارد زیر در مسیر ارتباط جلوگیری میکند:
Username
Password
API Key
Bearer Token
JWT
Session Cookie
نکته مهم این است که JWT یا Bearer Token جایگزین HTTPS نیست.
حتی اگر Token بسیار امن باشد، ارسال آن از طریق HTTP میتواند باعث افشای Token شود.
# Authentication و Authorization چه تفاوتی دارند؟
یکی از مهمترین مفاهیم امنیت API تفاوت بین Authentication و Authorization است.
Authentication
Authentication پاسخ این سؤال است:
> این کاربر یا Client چه کسی است؟
مثلاً:
User ID = 25
یا:
API Client = mobile-app
Authorization
Authorization پاسخ این سؤال است:
> آیا این کاربر اجازه انجام این عملیات را دارد؟
مثلاً:
User 25
↓
مشاهده پروفایل خودش → مجاز
↓
مشاهده پروفایل User 30 → غیرمجاز
احراز هویت بهتنهایی کافی نیست.
ممکن است کاربر کاملاً معتبر باشد، اما اجازه دسترسی به یک Resource خاص را نداشته باشد.
# API Key چیست؟
API Key یکی از روشهای ساده برای شناسایی Client است.
برای مثال:
GET /api/products
Authorization: ApiKey YOUR_API_KEY
یا:
X-API-Key: YOUR_API_KEY
API Key معمولاً برای شناسایی یک Application یا سرویس استفاده میشود.
برای مثال:
Mobile App
↓
API Key
↓
API Server
ساخت API Key امن در PHP
برای تولید کلید نباید از روشهایی مانند rand() استفاده کنیم.
بهتر است از random_bytes() استفاده کنیم:
$apiKey = bin2hex(random_bytes(32));
echo $apiKey;
خروجی چیزی شبیه این خواهد بود:
a8e4c1...
این مقدار باید بهصورت تصادفی و غیرقابل حدس تولید شود.
# آیا API Key را باید Plain Text ذخیره کنیم؟
در بسیاری از طراحیها بهتر است API Key خام را در دیتابیس ذخیره نکنیم.
مثلاً:
$apiKeyHash = hash('sha256', $apiKey);
و سپس فقط Hash را ذخیره کنیم.
هنگام دریافت API Key:
$receivedKey = $_SERVER['HTTP_X_API_KEY'] ?? '';
$hash = hash('sha256', $receivedKey);
سپس Hash را با مقدار ذخیرهشده مقایسه میکنیم.
به این ترتیب اگر دیتابیس لو رفت، کلید خام مستقیماً در اختیار مهاجم قرار نمیگیرد.
البته طراحی دقیق Storage به نوع Token و نیاز سیستم بستگی دارد.
# API Key باید قابلیت Rotation داشته باشد
کلیدهای API نباید برای همیشه معتبر باشند.
بهتر است سیستم امکان موارد زیر را داشته باشد:
- ایجاد Key جدید
- غیرفعال کردن Key
- حذف Key
- Rotation
- تعیین تاریخ انقضا
- تعیین Scope
مثلاً:
API Key
├── Name
├── Hash
├── User ID
├── Scopes
├── Expires At
├── Revoked At
└── Created At
# Bearer Token چیست؟
یکی دیگر از روشهای رایج احراز هویت API استفاده از Bearer Token است.
در Request معمولاً Header زیر ارسال میشود:
Authorization: Bearer eyJhbGciOi...
سرور Token را دریافت کرده و بر اساس آن Client را شناسایی میکند.
نکته مهم:
هر کسی که Bearer Token را در اختیار داشته باشد، میتواند از آن بهعنوان Credential استفاده کند.
به همین دلیل نباید Token را در مواردی مانند:
URL
Query String
Log
Screenshot
Frontend Source Code
قرار داد.
# دریافت Bearer Token در PHP
میتوانیم Header را به این شکل دریافت کنیم:
$authorization = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
سپس بررسی کنیم که با Bearer شروع شده است:
if (!str_starts_with($authorization, 'Bearer ')) {
http_response_code(401);
echo json_encode([
'message' => 'Unauthorized'
]);
exit;
}
$token = substr($authorization, 7);
اکنون $token شامل Token ارسالشده توسط Client است.
# JWT چیست؟
JWT یا JSON Web Token یکی از روشهای معروف برای Authentication در APIها است.
ساختار JWT معمولاً شامل سه بخش است:
Header.Payload.Signature
برای مثال:
xxxxx.yyyyy.zzzzz
Header مشخصات Token و Algorithm را مشخص میکند.
Payload اطلاعاتی مانند موارد زیر را میتواند داشته باشد:
{
"sub": 25,
"iat": 1750000000,
"exp": 1750003600
}
و Signature برای بررسی صحت Token استفاده میشود.
JWT رمزنگاری نیست
یک اشتباه بسیار رایج این است که تصور کنیم اطلاعات داخل JWT مخفی است.
JWT معمولاً برای Integrity و Authentication استفاده میشود، نه برای مخفی کردن اطلاعات.
بنابراین نباید اطلاعات حساس مانند:
Password
Credit Card Number
Private Key
Secret
را داخل Payload قرار دهیم.
# Claimهای مهم JWT
برخی Claimهای مهم عبارتاند از:
sub
شناسه Subject یا معمولاً User ID:
{
"sub": 25
}
iat
زمان ایجاد Token:
{
"iat": 1750000000
}
exp
زمان انقضا:
{
"exp": 1750003600
}
iss
صادرکننده Token.
aud
مخاطب Token.
jti
شناسه یکتای Token.
# Algorithm JWT را کورکورانه قبول نکنید
یکی از نکات مهم در پیادهسازی JWT این است که Server نباید صرفاً بر اساس Algorithm موجود در Token تصمیم بگیرد.
Algorithmهای مجاز باید در سمت Server مشخص باشند.
برای مثال:
Allowed Algorithms:
RS256
و نه اینکه Server هر Algorithm اعلامشده توسط Client را قبول کند.
# Access Token و Refresh Token
در معماریهای حرفهای معمولاً Access Token عمر کوتاهی دارد.
مثلاً:
Access Token
↓
15 دقیقه
و برای دریافت Token جدید از Refresh Token استفاده میشود.
معماری کلی:
Login
↓
Access Token + Refresh Token
↓
API Requests
↓
Access Token Expired
↓
Refresh Token
↓
New Access Token
این طراحی باعث میشود در صورت افشای Access Token، مدت اعتبار آن محدود باشد.
# Authorization و مشکل IDOR / BOLA
یکی از خطرناکترین مشکلات API این است که کاربر بتواند ID یک Resource را تغییر دهد.
فرض کنید Endpoint زیر داریم:
GET /api/orders/100
کاربر خودش Order شماره 100 را دارد.
اما اگر بتواند درخواست زیر را ارسال کند:
GET /api/orders/101
و اطلاعات سفارش کاربر دیگری را دریافت کند، API دچار مشکل Authorization است.
این مشکل در APIها معمولاً با مفاهیمی مانند:
IDOR
BOLA
شناخته میشود.
# فقط Authentication کافی نیست
کد اشتباه:
$user = authenticateUser();
$order = Order::find($_GET['id']);
return $order;
ممکن است کاربر Login شده باشد، اما هنوز بررسی نکردهایم که سفارش متعلق به او است.
باید چیزی شبیه این داشته باشیم:
$order = Order::where('id', $orderId)
->where('user_id', $user->id)
->firstOrFail();
در این حالت Resource به User وابسته میشود.
# Input Validation در API
هیچ دادهای که از Client دریافت میکنیم نباید بدون بررسی وارد منطق برنامه شود.
فرض کنید Client این JSON را ارسال کند:
{
"name": "Ali",
"age": 25
}
نباید فرض کنیم:
name همیشه String است
age همیشه Integer است
باید آن را Validate کنیم.
مثلاً:
$data = json_decode(
file_get_contents('php://input'),
true,
512,
JSON_THROW_ON_ERROR
);
سپس:
if (
!isset($data['name']) ||
!is_string($data['name']) ||
mb_strlen($data['name']) > 100
) {
http_response_code(422);
echo json_encode([
'message' => 'Invalid name'
]);
exit;
}
# هیچوقت User ID را از Client اعتماد نکنید
فرض کنید کاربر Login شده است.
Client ارسال میکند:
{
"user_id": 25,
"name": "Ali"
}
نباید از user_id ارسالشده استفاده کنیم.
شناسه کاربر باید از Authentication به دست بیاید:
$userId = $authenticatedUser->id;
نه:
$userId = $data['user_id'];
این موضوع یکی از اصول مهم امنیت API است.
# SQL Injection در API
API نیز مانند هر بخش دیگری از برنامه PHP میتواند در معرض SQL Injection قرار بگیرد.
کد خطرناک:
$sql = "SELECT * FROM users WHERE email = '$email'";
راه درست استفاده از Prepared Statement است:
$stmt = $pdo->prepare(
'SELECT * FROM users WHERE email = :email'
);
$stmt->execute([
'email' => $email
]);
Prepared Statement باید به یک استاندارد ثابت در پروژه تبدیل شود.
# Mass Assignment چیست؟
فرض کنید Endpoint زیر داریم:
PUT /api/profile
و Client این داده را ارسال میکند:
{
"name": "Ali",
"email": "ali@example.com",
"is_admin": true
}
اگر تمام دادههای JSON را مستقیماً به دیتابیس منتقل کنیم، ممکن است Client بتواند فیلدهای حساس را تغییر دهد.
بهجای:
$user->update($data);
از Allowlist استفاده کنیم:
$allowed = [
'name',
'email'
];
$updateData = [];
foreach ($allowed as $field) {
if (array_key_exists($field, $data)) {
$updateData[$field] = $data[$field];
}
}
در این حالت فقط فیلدهایی که صراحتاً اجازه دادهایم قابل تغییر هستند.
# CORS در امنیت API
CORS مشخص میکند Browser از چه Originهایی اجازه ارسال درخواست Cross-Origin به API را دارد.
برای API خصوصی نباید بدون بررسی از این استفاده کنیم:
Access-Control-Allow-Origin: *
بهخصوص زمانی که Credential یا Cookie در میان باشد.
بهتر است Originهای مجاز مشخص باشند:
$allowedOrigins = [
'https://example.com',
'https://app.example.com',
];
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
if (in_array($origin, $allowedOrigins, true)) {
header("Access-Control-Allow-Origin: $origin");
header('Vary: Origin');
}
جزئیات کامل CORS را در مقاله قبلی بررسی کردهایم.
# CSRF و API
نوع Authentication در API روی نحوه برخورد با CSRF تأثیر دارد.
اگر Authentication با Cookie انجام شود:
Browser
↓
Session Cookie
↓
API
موضوع CSRF باید جدی گرفته شود.
اما اگر API از Bearer Token استفاده کند:
Authorization: Bearer TOKEN
مدل تهدید متفاوت است.
با این حال استفاده از Bearer Token به معنی حذف سایر لایههای امنیتی نیست.
# Rate Limiting برای API
یک مهاجم ممکن است Endpoint شما را هزاران بار در دقیقه فراخوانی کند.
بنابراین API باید Rate Limiting داشته باشد.
مثلاً:
Login:
5 requests / 15 minutes
یا:
Public API:
60 requests / minute
در صورت عبور از محدودیت:
HTTP/1.1 429 Too Many Requests
میتوانیم Header زیر را نیز ارسال کنیم:
Retry-After: 60
Rate Limiting برای موارد زیر اهمیت ویژهای دارد:
- Login
- OTP
- Password Reset
- Email Verification
- API Token
- Search
- Expensive Queries
- Public API
# Replay Attack چیست؟
در حمله Replay، مهاجم یک Request معتبر را ذخیره کرده و دوباره ارسال میکند.
مثلاً:
Request A
↓
Signature valid
↓
Attacker saves it
↓
Request A دوباره ارسال میشود
برای کاهش این خطر میتوان از مواردی مانند:
Timestamp
Nonce
jti
Short-lived Token
One-time Token
استفاده کرد.
این موضوع برای Webhookها اهمیت زیادی دارد.
# امنیت Webhook
فرض کنید یک سرویس خارجی به API شما Webhook ارسال میکند.
بهتر است فقط به IP اعتماد نکنیم.
یکی از روشهای رایج استفاده از HMAC است.
مثلاً:
$payload = file_get_contents('php://input');
$signature = hash_hmac(
'sha256',
$payload,
$secret
);
سپس Signature ارسالشده را با Signature محاسبهشده مقایسه میکنیم:
if (!hash_equals($signature, $receivedSignature)) {
http_response_code(401);
exit;
}
برای جلوگیری از Replay نیز میتوان Timestamp و شناسه یکتای Request را بررسی کرد.
# مدیریت خطا در API
یکی از اشتباهات خطرناک این است که Error داخلی Server را مستقیماً به Client برگردانیم.
مثلاً این اطلاعات نباید در Production نمایش داده شوند:
SQL Query
Database Password
File Path
Stack Trace
Secret Key
بهجای آن یک پاسخ استاندارد ایجاد کنیم:
{
"message": "Internal server error",
"request_id": "req_8f21..."
}
و جزئیات واقعی را فقط در Log نگهداری کنیم.
# Status Codeهای مهم API
برخی Status Codeهای رایج:
| Code | کاربرد |
|---|---|
| 200 | درخواست موفق |
| 201 | Resource ایجاد شد |
| 204 | موفق بدون Body |
| 400 | درخواست نامعتبر |
| 401 | احراز هویت انجام نشده |
| 403 | دسترسی ممنوع |
| 404 | Resource پیدا نشد |
| 409 | Conflict |
| 422 | Validation Error |
| 429 | Rate Limit |
| 500 | خطای داخلی Server |
تفاوت 401 و 403 را باید بهدرستی در API رعایت کرد.
# Pagination و محدود کردن Query
فرض کنید Endpoint زیر وجود دارد:
GET /api/products
نباید اجازه دهیم Client بدون محدودیت درخواست کند:
1000000 products
بهتر است:
?page=1&per_page=20
و Server حداکثر مقدار مشخصی را قبول کند:
$perPage = min(
max((int) ($_GET['per_page'] ?? 20), 1),
100
);
یعنی Client حداکثر میتواند:
100
مورد در هر Request دریافت کند.
# Sorting امن
این کد خطرناک است:
$order = $_GET['sort'];
$sql = "SELECT * FROM products ORDER BY $order";
چون نام Column مستقیماً از Client گرفته شده است.
بهتر است Allowlist داشته باشیم:
$allowedSorts = [
'name',
'price',
'created_at',
];
$sort = $_GET['sort'] ?? 'created_at';
if (!in_array($sort, $allowedSorts, true)) {
$sort = 'created_at';
}
# Logging و Monitoring
API امن فقط باید جلوی حمله را نگیرد؛ باید بتواند رفتار مشکوک را نیز شناسایی کند.
موارد مفیدی برای Log:
Request ID
User ID
Endpoint
HTTP Method
Status Code
Response Time
IP
User Agent
Authentication Result
اما موارد زیر نباید در Log ثبت شوند:
Password
OTP
API Key
Bearer Token
Refresh Token
Session ID
Private Key
# Request ID چیست؟
برای هر Request میتوان یک شناسه ایجاد کرد:
$requestId = bin2hex(random_bytes(16));
و آن را در Response نیز قرار داد:
header('X-Request-ID: ' . $requestId);
در نتیجه اگر کاربر بگوید:
> API خطا داد.
میتوان بر اساس Request ID آن Request را در Log پیدا کرد.
# Secretها را داخل Source Code قرار ندهید
این کار اشتباه است:
$secret = 'my-super-secret-key';
بهتر است Secretها از Environment دریافت شوند:
$secret = getenv('API_SECRET');
در Laravel نیز معمولاً از .env و Configuration استفاده میشود.
همچنین Secret نباید داخل Git Commit شود.
# معماری پیشنهادی یک API امن
یک معماری ساده میتواند اینگونه باشد:
Client
│
▼
HTTPS
│
▼
Reverse Proxy
│
▼
Rate Limiter
│
▼
Authentication
│
▼
Authorization
│
▼
Validation
│
▼
Business Logic
│
▼
Database
│
▼
Safe Response
│
▼
Client
در کنار این مسیر:
Logging
Monitoring
Audit
Alerting
نیز باید وجود داشته باشد.
# یک نمونه API امن در PHP
در ادامه یک نمونه ساده از Endpoint پروفایل ایجاد میکنیم.
فرض کنیم Endpoint ما این است:
GET /api/profile
و Client باید Bearer Token ارسال کند.
مرحله اول: دریافت Token
$authorization = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
if (!str_starts_with($authorization, 'Bearer ')) {
http_response_code(401);
echo json_encode([
'message' => 'Unauthorized'
]);
exit;
}
$token = trim(substr($authorization, 7));
if ($token === '') {
http_response_code(401);
echo json_encode([
'message' => 'Unauthorized'
]);
exit;
}
# مرحله دوم: پیدا کردن کاربر
فرض کنیم Tokenها را در جدول زیر نگهداری میکنیم:
api_tokens
ساختار ساده:
id
user_id
token_hash
expires_at
revoked_at
created_at
Token دریافتی را Hash میکنیم:
$tokenHash = hash('sha256', $token);
سپس:
$stmt = $pdo->prepare(
'SELECT id, user_id, expires_at, revoked_at
FROM api_tokens
WHERE token_hash = :token_hash
LIMIT 1'
);
$stmt->execute([
'token_hash' => $tokenHash
]);
$apiToken = $stmt->fetch(PDO::FETCH_ASSOC);
# مرحله سوم: بررسی اعتبار Token
if (!$apiToken) {
http_response_code(401);
echo json_encode([
'message' => 'Unauthorized'
]);
exit;
}
سپس بررسی انقضا:
if (
$apiToken['expires_at'] !== null &&
strtotime($apiToken['expires_at']) <= time()
) {
http_response_code(401);
echo json_encode([
'message' => 'Token expired'
]);
exit;
}
و Revocation:
if ($apiToken['revoked_at'] !== null) {
http_response_code(401);
echo json_encode([
'message' => 'Unauthorized'
]);
exit;
}
# مرحله چهارم: دریافت User
$stmt = $pdo->prepare(
'SELECT id, name, email
FROM users
WHERE id = :id
LIMIT 1'
);
$stmt->execute([
'id' => $apiToken['user_id']
]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
اگر User وجود نداشته باشد:
if (!$user) {
http_response_code(401);
echo json_encode([
'message' => 'Unauthorized'
]);
exit;
}
# مرحله پنجم: Response امن
در نهایت:
header('Content-Type: application/json; charset=utf-8');
echo json_encode([
'data' => [
'id' => $user['id'],
'name' => $user['name'],
'email' => $user['email'],
]
]);
توجه کنید که ما کل Object دیتابیس را برنگرداندیم.
فقط فیلدهایی را برگرداندیم که API نیاز دارد.
# یک Response استانداردتر
میتوانیم Responseهای API را استاندارد کنیم:
function apiResponse(
array $data,
int $status = 200
): never {
http_response_code($status);
header(
'Content-Type: application/json; charset=utf-8'
);
echo json_encode(
$data,
JSON_UNESCAPED_UNICODE |
JSON_UNESCAPED_SLASHES
);
exit;
}
سپس:
apiResponse([
'success' => true,
'data' => $user
]);
و برای خطا:
apiResponse([
'success' => false,
'message' => 'Unauthorized'
], 401);
# لایههای امنیتی Endpoint
در نهایت Endpoint ما باید چیزی شبیه این مسیر را طی کند:
Request
↓
HTTPS
↓
CORS
↓
Rate Limit
↓
Authentication
↓
Authorization
↓
Validation
↓
Business Logic
↓
Prepared Statement
↓
Safe Response
↓
Logging
هر لایه وظیفه متفاوتی دارد.
# اشتباهات رایج در امنیت API
1. استفاده از HTTP
http://example.com/api
بهجای HTTPS.
2. قرار دادن Token در URL
اشتباه:
/api/users?token=abc123
بهتر:
Authorization: Bearer abc123
3. ذخیره Token خام
در صورت امکان Hash Token را نگهداری کنید.
4. اعتماد به User ID ارسالی Client
{
"user_id": 50
}
این مقدار نباید مبنای Authorization قرار بگیرد.
5. نبود Object-Level Authorization
اینکه User Login شده، به معنی دسترسی او به تمام Resourceها نیست.
6. استفاده از * در CORS بدون دلیل
بهخصوص برای APIهای خصوصی.
7. نبود Rate Limiting
Endpointهای Login، OTP و APIهای عمومی بدون Rate Limit میتوانند هدف Abuse قرار بگیرند.
8. نمایش Stack Trace در Production
خطای داخلی را نباید مستقیماً به Client نشان داد.
9. ثبت Token در Log
این کار میتواند امنیت کل سیستم را تحت تأثیر قرار دهد.
10. اعتماد به تمام فیلدهای JSON
Client قابل اعتماد نیست.
هر دادهای باید Validate و محدود شود.
# چکلیست امنیت API در PHP
قبل از Production این موارد را بررسی کنید:
[ ] HTTPS فعال است
[ ] Authentication پیادهسازی شده
[ ] Authorization پیادهسازی شده
[ ] Object-Level Authorization وجود دارد
[ ] Tokenها Expire میشوند
[ ] Token امکان Revocation دارد
[ ] Secretها داخل Source Code نیستند
[ ] Input Validation فعال است
[ ] Prepared Statement استفاده میشود
[ ] Mass Assignment کنترل شده
[ ] CORS محدود شده
[ ] Rate Limiting فعال است
[ ] Pagination محدود شده
[ ] Sorting با Allowlist انجام میشود
[ ] Errorهای حساس نمایش داده نمیشوند
[ ] Tokenها در Log ثبت نمیشوند
[ ] Passwordها Hash میشوند
[ ] Webhookها Signature دارند
[ ] Replay Attack بررسی شده
[ ] Security Headers تنظیم شده
[ ] Audit Logging وجود دارد
[ ] API Uploadها ایمن شدهاند
# امنیت API در Laravel
اگر پروژه شما با Laravel ساخته شده باشد، بسیاری از این قابلیتها را میتوان با ساختارهای استاندارد Laravel پیادهسازی کرد.
برای مثال:
Middleware
Request Validation
Policies
Gates
Authentication
Rate Limiter
Sanctum
Authorization
API Resources
میتوانند هرکدام بخشی از معماری امنیت API را بر عهده بگیرند.
برای مثال بهجای اینکه Authorization را داخل Controllerهای مختلف تکرار کنیم، میتوانیم از Policy استفاده کنیم.
مثلاً:
$this->authorize('view', $order);
این کار باعث میشود منطق دسترسی متمرکزتر و قابل نگهداریتر باشد.
# API Key یا Bearer Token یا JWT؟
این سه روش را نباید صرفاً بر اساس محبوبیت انتخاب کرد.
| روش | کاربرد معمول |
|---|---|
| API Key | شناسایی Application یا Client |
| Bearer Token | احراز هویت API با Token |
| JWT | Token قابل اعتبارسنجی بر اساس Signature و Claims |
انتخاب روش به معماری پروژه بستگی دارد.
برای مثال یک API داخلی ممکن است به API Key نیاز داشته باشد، درحالیکه یک سیستم User-based میتواند از Access Token استفاده کند.
JWT نیز زمانی مفید است که معماری و نیازهای سیستم واقعاً استفاده از آن را توجیه کند.
# آیا JWT همیشه بهترین انتخاب است؟
خیر.
JWT یک ابزار است، نه هدف.
در برخی معماریها استفاده از Tokenهای معمولی که در دیتابیس نگهداری و Revocation میشوند، مدیریت سادهتری دارد.
در برخی سیستمهای Distributed نیز JWT میتواند مناسب باشد.
موضوع مهم این است که موارد زیر بهدرستی طراحی شوند:
Expiration
Rotation
Revocation
Audience
Issuer
Signature Verification
Authorization
Storage
Transport Security
# تفاوت امنیت API با امنیت سایت
امنیت API با امنیت یک Website کاملاً یکسان نیست.
در Website ممکن است:
Session Cookie
CSRF Token
Server-rendered HTML
اهمیت زیادی داشته باشد.
اما در API ممکن است:
Bearer Token
JWT
Rate Limiting
Object-Level Authorization
API Key
Webhook Signature
نقش پررنگتری داشته باشند.
بنابراین نباید یک مدل امنیتی را بدون تغییر روی تمام سیستمها اعمال کرد.
# جمعبندی
امنیت API در PHP فقط به استفاده از JWT یا یک Authentication ساده محدود نمیشود.
یک API امن باید چند لایه مختلف داشته باشد:
HTTPS
+
Authentication
+
Authorization
+
Validation
+
SQL Injection Protection
+
CORS
+
CSRF Protection where applicable
+
Rate Limiting
+
Replay Protection
+
Secure Error Handling
+
Logging
+
Secret Management
همچنین باید همیشه بین Authentication و Authorization تفاوت قائل شویم.
ممکن است یک کاربر کاملاً احراز هویت شده باشد، اما اجازه مشاهده یا تغییر یک Resource خاص را نداشته باشد.
در نهایت، امنیت API یک Feature واحد نیست؛ بلکه نتیجه ترکیب چندین لایه امنیتی در معماری برنامه است.
# سوالات متداول
آیا برای هر API باید JWT استفاده کنیم؟
خیر. انتخاب JWT به معماری و نیازهای سیستم بستگی دارد و API Key یا Tokenهای معمولی نیز در شرایط مناسب میتوانند استفاده شوند.
آیا API Key باید در دیتابیس ذخیره شود؟
بهتر است در طراحیهایی که امکان آن وجود دارد، مقدار خام API Key ذخیره نشود و مقدار Hash آن نگهداری شود.
آیا HTTPS برای JWT لازم است؟
بله. JWT نباید جایگزین HTTPS شود.
آیا CORS امنیت API را تضمین میکند؟
خیر. CORS یک مکانیزم کنترل دسترسی Browser است و جایگزین Authentication، Authorization یا سایر لایههای امنیتی نیست.
آیا Login بودن کاربر یعنی اجازه دسترسی به همه اطلاعات را دارد؟
خیر. Authentication هویت کاربر را مشخص میکند، اما Authorization مشخص میکند کاربر به چه Resourceهایی دسترسی دارد.
آیا Rate Limiting برای تمام APIها لازم است؟
سطح و نوع Rate Limiting باید بر اساس Endpoint و ریسک آن تعیین شود، اما برای Endpointهایی مانند Login، OTP، Password Reset و APIهای عمومی اهمیت ویژهای دارد.
آیا اطلاعات حساس را میتوان داخل JWT قرار داد؟
نباید فرض کرد Payload JWT محرمانه است. اطلاعات حساس نباید صرفاً با تکیه بر JWT مخفی تلقی شوند.
# مقاله بعدی
در مقاله بعدی میتوانیم یکی از مهمترین موضوعات Authentication در API را عمیقتر بررسی کنیم:
OAuth 2.0 در PHP چیست؟ آموزش Authorization و Access Token در API
در آن مقاله تفاوت OAuth 2.0 با JWT، مفهوم Authorization Server، Resource Server، Client، Access Token، Refresh Token و Flowهای مختلف را بررسی خواهیم کرد.





