Mengamankan AI Agent PHP dengan sistem authentication yang kuat: login user, API Key per-user, JWT authentication, dan rate limiting untuk mencegah penyalahgunaan.
Daftar Isi
Terakhir diperbarui:
TL;DR (Ringkasan Cepat)
- API Key cocok untuk komunikasi server-to-server.
- JWT cocok untuk autentikasi user di web/mobile app.
- Rate Limiting wajib ada untuk mencegah penyalahgunaan dan tagihan API berlebih.
- Simpan password dengan
password_hash(PASSWORD_BCRYPT)dan verifikasi denganpassword_verify().
Database untuk Authentication
CREATE TABLE users (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(150) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
api_key VARCHAR(64) UNIQUE NULL,
role ENUM('user','admin') DEFAULT 'user',
is_active TINYINT(1) DEFAULT 1,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;
CREATE TABLE rate_limits (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
identifier VARCHAR(100) NOT NULL,
requests INT DEFAULT 0,
window_end DATETIME NOT NULL,
INDEX idx_identifier (identifier)
) ENGINE=InnoDB;
API Key Authentication
<?php
namespace App\Auth;
class ApiKeyAuth
{
private PDO $pdo;
public function __construct(PDO $pdo)
{
$this->pdo = $pdo;
}
public function authenticate(string $apiKey): ?array
{
if (empty($apiKey) || strlen($apiKey) < 32) {
return null;
}
$stmt = $this->pdo->prepare(
'SELECT id, name, email, role FROM users
WHERE api_key = ? AND is_active = 1 LIMIT 1'
);
$stmt->execute([$apiKey]);
return $stmt->fetch(PDO::FETCH_ASSOC) ?: null;
}
public function generateApiKey(int $userId): string
{
$apiKey = bin2hex(random_bytes(32)); // 64 karakter hex
$stmt = $this->pdo->prepare('UPDATE users SET api_key = ? WHERE id = ?');
$stmt->execute([$apiKey, $userId]);
return $apiKey;
}
}
Rate Limiter
<?php
namespace App\Auth;
class RateLimiter
{
private PDO $pdo;
private int $maxRequests;
private int $windowSeconds;
public function __construct(PDO $pdo, int $maxRequests = 20, int $windowSeconds = 60)
{
$this->pdo = $pdo;
$this->maxRequests = $maxRequests;
$this->windowSeconds = $windowSeconds;
}
public function isAllowed(string $identifier): bool
{
$now = time();
$windowEnd = date('Y-m-d H:i:s', $now + $this->windowSeconds);
// Bersihkan entri yang sudah kadaluarsa
$this->pdo->prepare('DELETE FROM rate_limits WHERE window_end < NOW()')
->execute();
$stmt = $this->pdo->prepare(
'SELECT id, requests FROM rate_limits WHERE identifier = ? AND window_end > NOW() LIMIT 1'
);
$stmt->execute([$identifier]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$row) {
// Buat entri baru
$this->pdo->prepare(
'INSERT INTO rate_limits (identifier, requests, window_end) VALUES (?, 1, ?)')
->execute([$identifier, $windowEnd]);
return true;
}
if ($row['requests'] >= $this->maxRequests) {
return false; // Rate limit tercapai
}
// Increment counter
$this->pdo->prepare('UPDATE rate_limits SET requests = requests + 1 WHERE id = ?')
->execute([$row['id']]);
return true;
}
}
Middleware Authentication di Agent Endpoint
<?php
// api/agent.php
header('Content-Type: application/json');
$apiKey = $_SERVER['HTTP_X_API_KEY'] ?? '';
$auth = new App\Auth\ApiKeyAuth($pdo);
$user = $auth->authenticate($apiKey);
if (!$user) {
http_response_code(401);
echo json_encode(['error' => 'API Key tidak valid atau tidak ditemukan']);
exit;
}
// Rate limiting
$limiter = new App\Auth\RateLimiter($pdo, maxRequests: 20, windowSeconds: 60);
if (!$limiter->isAllowed("user:{$user['id']}")) {
http_response_code(429);
echo json_encode(['error' => 'Rate limit tercapai. Coba lagi dalam 60 detik.']);
exit;
}
// Lanjut proses agent...
$task = json_decode(file_get_contents('php://input'), true)['task'] ?? '';
$result = $agent->run($task);
echo json_encode(['result' => $result, 'user' => $user['name']]);
Poin Kunci Bab 17
Authentication dan rate limiting bukan opsional — mereka adalah wajib sebelum deploy ke production. Tanpa ini, siapa pun bisa menggunakan agent Anda dan menguras kredit API Anda dalam hitungan menit.