439 lines
9.3 KiB
Markdown
439 lines
9.3 KiB
Markdown
# 🔒 FAST API HARDENING - IMPLEMENTASI
|
|
|
|
## ✅ Status: HARDENING COMPLETED
|
|
|
|
---
|
|
|
|
## 🎯 FITUR HARDENING YANG DIIMPLEMENTASI
|
|
|
|
### **1. Rate Limiting** ✅
|
|
|
|
**Implementasi:**
|
|
- ✅ File-based rate limiting (bisa upgrade ke Redis nanti)
|
|
- ✅ Default: **100 requests per minute** per API key
|
|
- ✅ Configurable per API key via database
|
|
- ✅ Response headers: `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`
|
|
- ✅ Return **429 Too Many Requests** jika limit exceeded
|
|
|
|
**Location:**
|
|
- `src/Helpers/RateLimitHelper.php` - `checkRateLimit()`
|
|
- `src/Middleware/ApiKeyMiddleware.php` - Rate limit check
|
|
|
|
**Configuration:**
|
|
```sql
|
|
-- Set custom rate limit per API key
|
|
UPDATE api_keys
|
|
SET rate_limit_per_minute = 200,
|
|
rate_limit_window = 60
|
|
WHERE id = 1;
|
|
```
|
|
|
|
**Response Headers:**
|
|
```
|
|
X-RateLimit-Limit: 100
|
|
X-RateLimit-Remaining: 95
|
|
X-RateLimit-Reset: 1705123456
|
|
Retry-After: 45
|
|
```
|
|
|
|
---
|
|
|
|
### **2. IP Whitelist** ✅
|
|
|
|
**Implementasi:**
|
|
- ✅ IP whitelist per API key (optional)
|
|
- ✅ Support single IP atau CIDR notation (e.g., `192.168.1.0/24`)
|
|
- ✅ Support comma-separated atau JSON array
|
|
- ✅ Default: **disabled** (allow all IPs)
|
|
- ✅ Return **403 Forbidden** jika IP tidak di whitelist
|
|
|
|
**Location:**
|
|
- `src/Helpers/RateLimitHelper.php` - `checkIpWhitelist()`
|
|
- `src/Middleware/ApiKeyMiddleware.php` - IP whitelist check
|
|
|
|
**Configuration:**
|
|
```sql
|
|
-- Enable IP whitelist untuk API key
|
|
UPDATE api_keys
|
|
SET enable_ip_whitelist = 1,
|
|
ip_whitelist = '192.168.1.100,10.0.0.0/24,203.0.113.0/24'
|
|
WHERE id = 1;
|
|
|
|
-- Atau menggunakan JSON array
|
|
UPDATE api_keys
|
|
SET enable_ip_whitelist = 1,
|
|
ip_whitelist = '["192.168.1.100", "10.0.0.0/24"]'
|
|
WHERE id = 1;
|
|
```
|
|
|
|
**Format IP Whitelist:**
|
|
- Single IP: `192.168.1.100`
|
|
- CIDR: `192.168.1.0/24`
|
|
- Comma-separated: `192.168.1.100,10.0.0.0/24`
|
|
- JSON array: `["192.168.1.100", "10.0.0.0/24"]`
|
|
|
|
---
|
|
|
|
### **3. API Key Expiration** ✅
|
|
|
|
**Implementasi:**
|
|
- ✅ Expiration date per API key (optional)
|
|
- ✅ Check expiration saat validation
|
|
- ✅ Return **401 Unauthorized** jika expired
|
|
- ✅ Default: **never expires** (NULL)
|
|
|
|
**Location:**
|
|
- `src/Helpers/RateLimitHelper.php` - `checkExpiration()`
|
|
- `src/Middleware/ApiKeyMiddleware.php` - Expiration check
|
|
|
|
**Configuration:**
|
|
```sql
|
|
-- Set expiration date untuk API key
|
|
UPDATE api_keys
|
|
SET expires_at = '2025-12-31 23:59:59'
|
|
WHERE id = 1;
|
|
|
|
-- Remove expiration (never expires)
|
|
UPDATE api_keys
|
|
SET expires_at = NULL
|
|
WHERE id = 1;
|
|
```
|
|
|
|
---
|
|
|
|
### **4. Request Timestamp Validation** ✅
|
|
|
|
**Implementasi:**
|
|
- ✅ Optional timestamp validation untuk prevent replay attack
|
|
- ✅ Default: **disabled** (log only, tidak block)
|
|
- ✅ Max age: **5 minutes** (300 seconds)
|
|
- ✅ Header: `X-Timestamp` atau body `timestamp`
|
|
|
|
**Location:**
|
|
- `src/Helpers/RateLimitHelper.php` - `validateTimestamp()`
|
|
- `src/Middleware/ApiKeyMiddleware.php` - Timestamp validation
|
|
|
|
**Usage:**
|
|
```http
|
|
POST /fast/check_bill
|
|
X-Client-ID: your_client_id
|
|
X-Client-Secret: your_client_secret
|
|
X-Timestamp: 1705123456
|
|
```
|
|
|
|
**Enable Blocking:**
|
|
Uncomment line di `ApiKeyMiddleware.php`:
|
|
```php
|
|
// Uncomment untuk block request dengan timestamp invalid
|
|
return ResponseHelper::json($handler->handle($request)->withStatus(400),
|
|
['status' => 'error', 'message' => $timestampValidation['message']], 400);
|
|
```
|
|
|
|
---
|
|
|
|
## 📊 DATABASE SCHEMA
|
|
|
|
### **New Columns di `api_keys` Table:**
|
|
|
|
```sql
|
|
ALTER TABLE api_keys
|
|
ADD COLUMN rate_limit_per_minute INT DEFAULT 100,
|
|
ADD COLUMN rate_limit_window INT DEFAULT 60,
|
|
ADD COLUMN enable_ip_whitelist TINYINT(1) DEFAULT 0,
|
|
ADD COLUMN ip_whitelist TEXT NULL,
|
|
ADD COLUMN expires_at DATETIME NULL,
|
|
ADD COLUMN last_used_at DATETIME NULL,
|
|
ADD COLUMN created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
ADD COLUMN updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;
|
|
|
|
-- Indexes
|
|
CREATE INDEX idx_api_keys_expires_at ON api_keys(expires_at);
|
|
CREATE INDEX idx_api_keys_is_active ON api_keys(is_active);
|
|
CREATE INDEX idx_api_keys_last_used_at ON api_keys(last_used_at);
|
|
```
|
|
|
|
### **Migration Script:**
|
|
|
|
```bash
|
|
php run_hardening_migration.php
|
|
```
|
|
|
|
**Note:** Script akan otomatis skip jika column/index sudah ada.
|
|
|
|
---
|
|
|
|
## 🔧 IMPLEMENTATION DETAILS
|
|
|
|
### **1. Rate Limiting**
|
|
|
|
**Storage:**
|
|
- File-based cache di `storage/cache/rate_limit/`
|
|
- Format: JSON file per API key
|
|
- Auto cleanup saat window expired
|
|
|
|
**Algorithm:**
|
|
- Sliding window per API key
|
|
- Counter reset setiap window seconds
|
|
- Thread-safe dengan file locking
|
|
|
|
**Upgrade Path:**
|
|
- Bisa upgrade ke Redis/Memcached nanti
|
|
- Interface sudah abstract, cukup ganti storage layer
|
|
|
|
### **2. IP Whitelist**
|
|
|
|
**Validation:**
|
|
- Check exact match untuk single IP
|
|
- Check CIDR range untuk subnet
|
|
- Support IPv4 (IPv6 bisa ditambahkan nanti)
|
|
|
|
**Performance:**
|
|
- Cached di memory per request
|
|
- No database query jika disabled
|
|
|
|
### **3. API Key Expiration**
|
|
|
|
**Validation:**
|
|
- Check saat API key validation
|
|
- Compare dengan current timestamp
|
|
- Log expired attempts
|
|
|
|
### **4. Request Timestamp**
|
|
|
|
**Validation:**
|
|
- Optional (tidak block by default)
|
|
- Max age: 5 minutes
|
|
- Prevent replay attack
|
|
|
|
---
|
|
|
|
## 🛡️ SECURITY FLOW
|
|
|
|
```
|
|
Request → ApiKeyMiddleware
|
|
↓
|
|
1. Extract X-Client-ID & X-Client-Secret
|
|
↓
|
|
2. Validate API Key (database)
|
|
↓
|
|
3. ✅ Check Expiration (if column exists)
|
|
↓
|
|
4. ✅ Check IP Whitelist (if enabled)
|
|
↓
|
|
5. ✅ Check Rate Limit (always enabled)
|
|
↓
|
|
6. ✅ Validate Timestamp (optional, log only)
|
|
↓
|
|
7. Attach API Key to Request
|
|
↓
|
|
8. Add Rate Limit Headers to Response
|
|
↓
|
|
Response
|
|
```
|
|
|
|
---
|
|
|
|
## 📝 USAGE EXAMPLES
|
|
|
|
### **Example 1: Basic API Call (No Hardening Config)**
|
|
|
|
```http
|
|
POST /fast/check_bill
|
|
X-Client-ID: ABC_1234567890_abcdef
|
|
X-Client-Secret: 64_char_hex_string
|
|
|
|
Response:
|
|
HTTP/1.1 200 OK
|
|
X-RateLimit-Limit: 100
|
|
X-RateLimit-Remaining: 99
|
|
X-RateLimit-Reset: 1705123456
|
|
```
|
|
|
|
### **Example 2: Rate Limit Exceeded**
|
|
|
|
```http
|
|
POST /fast/check_bill
|
|
X-Client-ID: ABC_1234567890_abcdef
|
|
X-Client-Secret: 64_char_hex_string
|
|
|
|
Response:
|
|
HTTP/1.1 429 Too Many Requests
|
|
X-RateLimit-Limit: 100
|
|
X-RateLimit-Remaining: 0
|
|
X-RateLimit-Reset: 1705123456
|
|
Retry-After: 45
|
|
|
|
{
|
|
"status": "error",
|
|
"message": "Rate limit exceeded. Please try again later.",
|
|
"retry_after": 45
|
|
}
|
|
```
|
|
|
|
### **Example 3: IP Not Whitelisted**
|
|
|
|
```http
|
|
POST /fast/check_bill
|
|
X-Client-ID: ABC_1234567890_abcdef
|
|
X-Client-Secret: 64_char_hex_string
|
|
IP: 192.168.1.200 (not in whitelist)
|
|
|
|
Response:
|
|
HTTP/1.1 403 Forbidden
|
|
|
|
{
|
|
"status": "error",
|
|
"message": "IP address not allowed"
|
|
}
|
|
```
|
|
|
|
### **Example 4: API Key Expired**
|
|
|
|
```http
|
|
POST /fast/check_bill
|
|
X-Client-ID: ABC_1234567890_abcdef
|
|
X-Client-Secret: 64_char_hex_string
|
|
|
|
Response:
|
|
HTTP/1.1 401 Unauthorized
|
|
|
|
{
|
|
"status": "error",
|
|
"message": "API key has expired"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## ⚙️ CONFIGURATION
|
|
|
|
### **Environment Variables (Optional):**
|
|
|
|
```env
|
|
# Rate Limiting (defaults)
|
|
RATE_LIMIT_DEFAULT=100
|
|
RATE_LIMIT_WINDOW=60
|
|
|
|
# Timestamp Validation (defaults)
|
|
TIMESTAMP_MAX_AGE=300
|
|
```
|
|
|
|
### **Database Configuration:**
|
|
|
|
```sql
|
|
-- Set custom rate limit
|
|
UPDATE api_keys
|
|
SET rate_limit_per_minute = 200,
|
|
rate_limit_window = 60
|
|
WHERE id = 1;
|
|
|
|
-- Enable IP whitelist
|
|
UPDATE api_keys
|
|
SET enable_ip_whitelist = 1,
|
|
ip_whitelist = '192.168.1.100,10.0.0.0/24'
|
|
WHERE id = 1;
|
|
|
|
-- Set expiration
|
|
UPDATE api_keys
|
|
SET expires_at = '2025-12-31 23:59:59'
|
|
WHERE id = 1;
|
|
```
|
|
|
|
---
|
|
|
|
## 🔍 MONITORING & LOGGING
|
|
|
|
### **API Logs:**
|
|
|
|
Semua hardening events di-log ke `api_logs` table:
|
|
|
|
```sql
|
|
-- View rate limit events
|
|
SELECT * FROM api_logs
|
|
WHERE status = 'rate_limited'
|
|
ORDER BY created_at DESC;
|
|
|
|
-- View IP blocked events
|
|
SELECT * FROM api_logs
|
|
WHERE status = 'ip_blocked'
|
|
ORDER BY created_at DESC;
|
|
|
|
-- View expired API keys
|
|
SELECT * FROM api_logs
|
|
WHERE status = 'expired'
|
|
ORDER BY created_at DESC;
|
|
```
|
|
|
|
### **Rate Limit Cache Files:**
|
|
|
|
```bash
|
|
# View rate limit cache
|
|
ls -la storage/cache/rate_limit/
|
|
|
|
# Clear rate limit cache (emergency)
|
|
rm -rf storage/cache/rate_limit/*
|
|
```
|
|
|
|
---
|
|
|
|
## ✅ BACKWARD COMPATIBILITY
|
|
|
|
**Semua hardening features adalah backward compatible:**
|
|
|
|
1. ✅ **Rate Limiting** - Always enabled, default 100 req/min
|
|
2. ✅ **IP Whitelist** - Default disabled (allow all IPs)
|
|
3. ✅ **Expiration** - Default never expires (NULL)
|
|
4. ✅ **Timestamp** - Optional, tidak block by default
|
|
|
|
**Jika column belum ada di database:**
|
|
- Hardening features akan **skip gracefully**
|
|
- Error di-log tapi request tetap di-allow (fail open)
|
|
- Tidak ada breaking changes untuk existing API keys
|
|
|
|
---
|
|
|
|
## 🚀 UPGRADE PATH
|
|
|
|
### **Future Enhancements:**
|
|
|
|
1. **Redis/Memcached untuk Rate Limiting**
|
|
- Ganti file-based cache dengan Redis
|
|
- Better performance untuk high traffic
|
|
|
|
2. **Advanced Rate Limiting**
|
|
- Per-endpoint rate limiting
|
|
- Burst protection
|
|
- Adaptive rate limiting
|
|
|
|
3. **Request Signature (HMAC)**
|
|
- HMAC SHA256 signature validation
|
|
- Prevent request tampering
|
|
- Replay attack protection
|
|
|
|
4. **API Key Rotation**
|
|
- Automatic key rotation
|
|
- Grace period untuk old keys
|
|
- Notification sebelum expiration
|
|
|
|
---
|
|
|
|
## 📋 CHECKLIST
|
|
|
|
- ✅ Rate Limiting implemented
|
|
- ✅ IP Whitelist implemented
|
|
- ✅ API Key Expiration implemented
|
|
- ✅ Request Timestamp validation implemented
|
|
- ✅ Database migration script created
|
|
- ✅ Backward compatible (fail open)
|
|
- ✅ Error handling & logging
|
|
- ✅ Response headers added
|
|
- ✅ Documentation created
|
|
|
|
---
|
|
|
|
**Status:** ✅ **HARDENING COMPLETED**
|
|
|
|
**Level Security:** **ENHANCED** (dari basic ke hardened)
|
|
|
|
**Production Ready:** ✅ **YES** (backward compatible)
|