Mengamankan server Swoole: input validation di layer server, pembatasan koneksi dengan max_conn dan timeout, heartbeat untuk membunuh koneksi basi, serta strategi proteksi terhadap Slowloris dan connection flooding.

Setelah di episode 16 kita membangun microservices — pada episode kali ini kita berhenti sejenak dari membangun fitur dan mengamankan apa yang sudah kita bangun. Server Swoole adalah server jaringan publik: ia terhubung langsung ke internet, dan karena itu menjadi sasaran serangan yang jauh lebih beragam daripada PHP-FPM yang bersembunyi di balik Nginx.
Mengapa episode ini penting? Dua alasan. Pertama, Swoole biasanya dipakai tanpa web server di depan (bukan di balik Nginx), jadi semua pertahanan menjadi tanggung jawab kalian. Kedua, serangan yang menargetkan koneksi (Slowloris, flooding) persis menyerang titik yang menjadi keunggulan Swoole — dan menutupnya adalah skill wajib.
Jangan harap satu konfigurasi menyelesaikan semuanya. Lapisi tiga layer:
| Layer | Fungsi | Contoh |
|---|---|---|
| 1. Infrastruktur | Filtering di depan | Firewall, Cloudflare, rate limit LB |
| 2. Swoole | Batasan koneksi & waktu | max_conn, heartbeat, timeout |
| 3. Aplikasi | Validasi logika | Input validation, auth, business rules |
Episode ini fokus pada layer 2 dan 3.
Setiap input dari client adalah tidak tepercaya. Validasi sebelum menyentuh logika bisnis:
$server->on('Request', function (Request $req, Response $res) {
$id = $req->get['id'] ?? null;
if ($id === null || !ctype_digit($id) || (int)$id > PHP_INT_MAX) {
$res->status(400);
$res->end('Invalid id');
return;
}
$name = $req->post['name'] ?? '';
if (mb_strlen($name) > 255 || mb_strlen($name) < 2) {
$res->status(422);
$res->end('Nama harus 2-255 karakter');
return;
}
// ... lanjut proses
});Aturan praktis:
package_max_length, dan cek strlen di kode).json_decode selalu cek json_last_error().Swoole menyediakan pembatas yang menghentikan serangan di gerbang:
$server = new Server('0.0.0.0', 9501);
$server->set([
'max_conn' => 10000, // batas koneksi total
'heartbeat_check_interval' => 30, // cek tiap 30 detik
'heartbeat_idle_time' => 120, // tutup koneksi idle > 120 detik
'package_max_length' => 2 * 1024 * 1024, // batas paket 2MB
'buffer_output_size' => 4 * 1024 * 1024, // batas output 4MB
'open_tcp_keepalive' => true,
'dispatch_mode' => 3,
]);| Setelan | Melindungi dari |
|---|---|
max_conn | Connection flooding — menolak koneksi melebihi batas |
heartbeat_idle_time | Koneksi basi & Slowloris — koneksi yang "setengah hidup" dibunuh |
package_max_length | Request raksasa / buffer overflow |
buffer_output_size | Response membengkak (memory exhaustion) |
open_tcp_keepalive | Dead peer di sisi TCP |
Warning
heartbeat_idle_time membunuh koneksi yang tidak mengirim apa pun. Hati-hati: WebSocket idle (user yang tidak mengetik) juga akan diputus. Solusinya heartbeat ping/pong dari episode 6 — kirim ping berkala sehingga koneksi tidak pernah tampak idle. Jangan aktifkan heartbeat buta pada server WebSocket.
Slowloris adalah serangan yang membuka banyak koneksi lalu mengirim data sangat pelan (misal 1 byte per menit) untuk menahan resource server selama mungkin. Pertahanan utamanya:
max_conn — membatasi total koneksi yang bisa ditahan attacker.heartbeat_check_interval + heartbeat_idle_time — koneksi yang menahan request sebagian (header belum lengkap) tidak akan mengirim data dalam interval waktu tertentu → diputus.$server->set([
'socket_buffer_size' => 2 * 1024 * 1024,
'socket_read_timeout' => 10, // 10 detik untuk membaca
'socket_write_timeout' => 10, // 10 detik untuk menulis
]);Dengan timeout ini, koneksi yang mengirim setengah request lebih dari 10 detik akan diputus oleh kernel/swoole — persis serangan yang coba ditahan Slowloris.
Connection flooding (SYN flood / banyak koneksi cepat) ditangani berlapis:
$server->on('Connect', function ($server, $fd, $reactorId) {
$ip = $server->getClientInfo($fd)['remote_ip'] ?? 'unknown';
if (isFlooding($ip)) {
$server->close($fd); // tolak cepat
}
});Kombinasi terbaik untuk flooding:
iptables rate limit, Cloudflare, atau LB — layer 1 paling efektif.max_conn: batas keras total koneksi.backlog: ukuran antrian koneksi pending.<?php
use Swoole\Http\Server;
use Swoole\Http\Request;
use Swoole\Http\Response;
$server = new Server('0.0.0.0', 9501);
$server->set([
'worker_num' => swoole_cpu_num(),
'max_conn' => 10000,
'dispatch_mode' => 3,
'package_max_length' => 2 * 1024 * 1024,
'buffer_output_size' => 4 * 1024 * 1024,
'heartbeat_check_interval' => 30,
'heartbeat_idle_time' => 120,
'socket_read_timeout' => 10,
'socket_write_timeout' => 10,
'open_tcp_keepalive' => true,
'backlog' => 512,
]);
$server->on('Request', function (Request $req, Response $res) {
$path = $req->server['request_uri'] ?? '/';
$allowed = ['/health', '/api/users'];
if (!in_array($path, $allowed, true)) {
$res->status(404);
$res->end('Not Found');
return;
}
$res->end('OK');
});
$server->start();Setelan ini bisa kalian jadikan template dasar produksi — kita akan mengevaluasi ulang trade-off-nya di episode 20 (performance tuning).
| Masalah | Penyebab | Solusi |
|---|---|---|
| WebSocket user terputus | Heartbeat salah konfigurasi | Aktifkan ping/pong aplikasi agar koneksi aktif |
| Request valid ditolak | max_conn terlalu kecil | Ukur beban puncak, naikkan batas |
| Timeout pemrosesan lama | socket_read/write_timeout terlalu ketat | Sesuaikan dengan latensi upstream (bukan request ringan) |
| Rate limit bypass | Limit per IP dihapus karena worker beda | Pakai Swoole\Table (episode 12) — shared lintas worker |
Pada episode 17 ini, kalian telah mengamankan server Swoole.
Inti yang harus dibawa pulang:
max_conn, heartbeat, dan socket timeout melawan Slowloris & flooding.Di episode 18 selanjutnya, kita mengunci jalur data: TLS/SSL, HTTPS & WSS — konfigurasi sertifikat di HTTP/WebSocket server dan redirect HTTP ke HTTPS. Sampai jumpa di episode 18!