Keamanan REST API Symfony: Autentikasi, JWT dan Pertanyaan Interview 2026
Panduan lengkap keamanan REST API Symfony dengan LexikJWTAuthenticationBundle 3.2. Pelajari konfigurasi JWT, refresh token, voter, rate limiting, dan pertanyaan interview umum untuk Symfony 7.2.

Keamanan REST API Symfony memerlukan kombinasi autentikasi, otorisasi, dan manajemen token yang tepat. LexikJWTAuthenticationBundle versi 3.2, yang kompatibel dengan Symfony 7.2 dan PHP 8.3, menyediakan fondasi yang kuat untuk autentikasi berbasis JWT pada API produksi.
Klien mengirim kredensial ke /api/login_check. Server memvalidasinya, menghasilkan JWT yang ditandatangani dengan kunci privat, dan mengembalikannya. Permintaan berikutnya menyertakan token ini dalam header Authorization untuk autentikasi stateless.
Menginstal LexikJWTAuthenticationBundle di Symfony 7.2
Bundle ini terintegrasi dengan komponen keamanan Symfony dan menangani pembuatan token, validasi, dan pemuatan pengguna dari payload token.
# Install the bundle
composer require lexik/jwt-authentication-bundle
# Generate RSA keys for token signing
php bin/console lexik:jwt:generate-keypairPerintah keypair membuat config/jwt/private.pem dan config/jwt/public.pem. Kunci-kunci ini menandatangani dan memverifikasi token. Simpan passphrase dalam variabel environment JWT_PASSPHRASE.
# config/packages/lexik_jwt_authentication.yaml
lexik_jwt_authentication:
secret_key: '%env(resolve:JWT_SECRET_KEY)%'
public_key: '%env(resolve:JWT_PUBLIC_KEY)%'
pass_phrase: '%env(JWT_PASSPHRASE)%'
token_ttl: 3600 # 1 hourPengaturan token_ttl mengontrol masa berlaku token. Token dengan masa hidup pendek mengurangi jendela eksploitasi pencurian token.
Mengonfigurasi Firewall Keamanan untuk Autentikasi API
Komponen keamanan Symfony memerlukan konfigurasi firewall untuk melindungi rute API. Autentikator json_login menangani validasi kredensial, sementara jwt mengamankan permintaan selanjutnya.
# config/packages/security.yaml
security:
enable_authenticator_manager: true
providers:
app_user_provider:
entity:
class: App\Entity\User
property: email
firewalls:
login:
pattern: ^/api/login
stateless: true
json_login:
check_path: /api/login_check
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
api:
pattern: ^/api
stateless: true
jwt: ~
access_control:
- { path: ^/api/login, roles: PUBLIC_ACCESS }
- { path: ^/api/docs, roles: PUBLIC_ACCESS }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }Pengaturan stateless: true mencegah pembuatan sesi. Setiap permintaan diautentikasi secara independen melalui JWT di header Authorization.
Membuat Entity User dengan Hashing Password
Entity User mengimplementasikan UserInterface dan PasswordAuthenticatedUserInterface. Symfony 7.2 menggunakan interface ini untuk terintegrasi dengan sistem keamanan.
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;
#[ORM\Entity]
#[ORM\Table(name: 'users')]
class User implements UserInterface, PasswordAuthenticatedUserInterface
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 180, unique: true)]
private ?string $email = null;
#[ORM\Column]
private array $roles = [];
#[ORM\Column]
private ?string $password = null;
public function getUserIdentifier(): string
{
return (string) $this->email;
}
public function getRoles(): array
{
$roles = $this->roles;
$roles[] = 'ROLE_USER'; // Every user has ROLE_USER
return array_unique($roles);
}
public function getPassword(): ?string
{
return $this->password;
}
public function eraseCredentials(): void
{
// Clear temporary sensitive data if stored
}
}Hashing password menggunakan konfigurasi password_hashers Symfony. Algoritma auto memilih hasher terkuat yang tersedia.
Mengimplementasikan Refresh Token untuk Sesi Panjang
Token JWT akan kedaluwarsa. Tanpa refresh token, pengguna harus melakukan autentikasi ulang secara sering. JWTRefreshTokenBundle memperluas autentikasi dengan refresh token berumur panjang yang disimpan dalam database.
# Install refresh token bundle
composer require gesdinet/jwt-refresh-token-bundle# config/packages/gesdinet_jwt_refresh_token.yaml
gesdinet_jwt_refresh_token:
refresh_token_lifetime: 2592000 # 30 days
user_identity_field: email
ttl_update: true # Extend TTL on each useEndpoint refresh menukar refresh token yang valid dengan access token baru tanpa memerlukan kredensial.
# config/packages/security.yaml (add to firewalls)
refresh:
pattern: ^/api/token/refresh
stateless: true# config/routes.yaml
api_refresh_token:
path: /api/token/refresh
controller: gesdinet.jwtrefreshtoken::refreshAplikasi klien menyimpan refresh token dengan aman dan menggunakannya ketika access token kedaluwarsa.
Siap menguasai wawancara Symfony Anda?
Berlatih dengan simulator interaktif, flashcards, dan tes teknis kami.
Security Voter untuk Otorisasi Detail
Aturan firewall menangani autentikasi. Voter menangani otorisasi, menentukan apakah pengguna yang terautentikasi dapat melakukan tindakan spesifik pada resource tertentu.
namespace App\Security\Voter;
use App\Entity\Article;
use App\Entity\User;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
class ArticleVoter extends Voter
{
public const EDIT = 'ARTICLE_EDIT';
public const DELETE = 'ARTICLE_DELETE';
protected function supports(string $attribute, mixed $subject): bool
{
return in_array($attribute, [self::EDIT, self::DELETE])
&& $subject instanceof Article;
}
protected function voteOnAttribute(
string $attribute,
mixed $subject,
TokenInterface $token
): bool {
$user = $token->getUser();
if (!$user instanceof User) {
return false; // Not logged in
}
/** @var Article $article */
$article = $subject;
return match ($attribute) {
self::EDIT => $this->canEdit($article, $user),
self::DELETE => $this->canDelete($article, $user),
default => false,
};
}
private function canEdit(Article $article, User $user): bool
{
// Authors can edit their own articles
return $article->getAuthor() === $user;
}
private function canDelete(Article $article, User $user): bool
{
// Only admins can delete
return in_array('ROLE_ADMIN', $user->getRoles(), true);
}
}Controller menggunakan $this->isGranted() atau atribut #[IsGranted] untuk menegakkan keputusan voter.
#[Route('/api/articles/{id}', methods: ['PUT'])]
public function update(Article $article, Request $request): JsonResponse
{
$this->denyAccessUnlessGranted(ArticleVoter::EDIT, $article);
// Update logic here
return $this->json($article);
}Rate Limiting Percobaan Login
Serangan brute force menargetkan endpoint login. Komponen rate limiter Symfony membatasi jumlah percobaan per IP atau username.
# config/packages/rate_limiter.yaml
framework:
rate_limiter:
login_limiter:
policy: 'sliding_window'
limit: 5
interval: '1 minute'namespace App\EventListener;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
use Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException;
use Symfony\Component\RateLimiter\RateLimiterFactory;
use Symfony\Component\Security\Http\Event\CheckPassportEvent;
#[AsEventListener]
class LoginAttemptListener
{
public function __construct(
private RateLimiterFactory $loginLimiter
) {}
public function __invoke(CheckPassportEvent $event): void
{
$request = $event->getRequest();
$limiter = $this->loginLimiter->create($request->getClientIp());
if (!$limiter->consume()->isAccepted()) {
throw new TooManyRequestsHttpException(
60,
'Too many login attempts. Try again in 1 minute.'
);
}
}
}Listener ini aktif sebelum validasi password, mencegah serangan timing yang mengungkap username yang valid.
Mengkustomisasi Payload JWT dengan Event Listener
Payload JWT default berisi data pengguna minimal. Klaim kustom menambahkan role, permission, atau data spesifik aplikasi tanpa query database tambahan.
namespace App\EventListener;
use App\Entity\User;
use Lexik\Bundle\JWTAuthenticationBundle\Event\JWTCreatedEvent;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
#[AsEventListener(event: 'lexik_jwt_authentication.on_jwt_created')]
class JWTCreatedListener
{
public function __invoke(JWTCreatedEvent $event): void
{
$user = $event->getUser();
if (!$user instanceof User) {
return;
}
$payload = $event->getData();
$payload['userId'] = $user->getId();
$payload['roles'] = $user->getRoles();
$payload['permissions'] = $user->getPermissions(); // Custom method
$event->setData($payload);
}
}Hindari menyimpan data sensitif dalam payload. JWT ditandatangani, bukan dienkripsi, sehingga klien dapat mendekode dan membaca payload.
Pertanyaan Interview Umum tentang Keamanan API Symfony
Interview teknis untuk posisi Symfony sering mencakup keamanan API. Pertanyaan-pertanyaan ini menilai pemahaman tentang alur autentikasi, manajemen token, dan praktik keamanan terbaik.
Apa perbedaan antara autentikasi dan otorisasi?
Autentikasi memverifikasi identitas: "Siapa pengguna ini?" Alur login JWT menangani autentikasi. Otorisasi menentukan permission: "Bisakah pengguna ini melakukan tindakan ini?" Voter dan aturan access control menangani otorisasi.
Mengapa menggunakan JWT daripada autentikasi berbasis sesi untuk API?
JWT memungkinkan autentikasi stateless. Server memvalidasi signature token tanpa menyimpan data sesi. Ini menyederhanakan horizontal scaling, karena instance server mana pun dapat memvalidasi token secara independen. Klien mobile dan SPA mendapat manfaat dari alur berbasis token yang bekerja lintas domain.
Bagaimana menangani kedaluwarsa token JWT?
Access token berumur pendek (15 menit hingga 1 jam) membatasi paparan jika dicuri. Refresh token, disimpan dengan aman oleh klien, mendapatkan access token baru tanpa autentikasi ulang. Endpoint refresh memvalidasi refresh token terhadap database dan mengeluarkan pasangan access/refresh baru.
Risiko keamanan apa yang ada dengan JWT?
Pencurian token melalui serangan XSS memungkinkan penyerang menyamar sebagai pengguna hingga kedaluwarsa. Algoritma signing lemah (seperti none atau HS256 dengan secret pendek) memungkinkan pemalsuan token. Menyimpan token di localStorage mengeksposnya ke XSS. Cookie HttpOnly memberikan perlindungan lebih baik untuk aplikasi web.
Bagaimana voter berbeda dari aturan access control?
Aturan access control dalam security.yaml berlaku untuk pola URL dengan pemeriksaan role. Voter mengevaluasi permission level objek secara dinamis. Aturan mungkin memberikan semua admin akses ke /api/admin/*, sementara voter menentukan apakah admin tertentu dapat mengedit resource tertentu berdasarkan kepemilikan atau logika bisnis lainnya.
Menguji Autentikasi JWT di Symfony
Tes fungsional memverifikasi bahwa autentikasi dan otorisasi bekerja dengan benar. Test client Symfony mendukung injeksi token untuk permintaan terautentikasi.
namespace App\Tests\Controller;
use App\Entity\User;
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class ArticleControllerTest extends WebTestCase
{
public function testAuthenticatedUserCanCreateArticle(): void
{
$client = static::createClient();
$container = static::getContainer();
// Get a test user from the database
$user = $container->get('doctrine')
->getRepository(User::class)
->findOneBy(['email' => 'test@example.com']);
// Generate JWT for this user
$jwtManager = $container->get(JWTTokenManagerInterface::class);
$token = $jwtManager->create($user);
// Make authenticated request
$client->request(
'POST',
'/api/articles',
[],
[],
[
'HTTP_AUTHORIZATION' => 'Bearer ' . $token,
'CONTENT_TYPE' => 'application/json',
],
json_encode(['title' => 'Test Article', 'content' => 'Content'])
);
$this->assertResponseStatusCodeSame(201);
}
public function testUnauthenticatedUserCannotAccessApi(): void
{
$client = static::createClient();
$client->request('GET', '/api/articles');
$this->assertResponseStatusCodeSame(401);
}
}Tes menghasilkan JWT nyata menggunakan JWTTokenManagerInterface bundle, memastikan seluruh alur autentikasi dijalankan.
Checklist Keamanan Produksi untuk API Symfony
Deploy API yang aman memerlukan perhatian pada konfigurasi, manajemen kunci, dan monitoring.
- Simpan kunci RSA di luar web root dengan permission file terbatas (600)
- Gunakan variabel environment untuk konfigurasi sensitif, jangan pernah commit secret
- Aktifkan HTTPS saja, redirect permintaan HTTP
- Atur header CORS yang sesuai untuk klien cross-origin
- Log kegagalan autentikasi untuk monitoring keamanan
- Rotasi kunci signing secara berkala, mendukung beberapa kunci valid selama transisi
- Validasi semua data input dengan serializer dan validator Symfony
- Gunakan masa token pendek dan implementasikan rotasi refresh token
Mulai berlatih!
Uji pengetahuan Anda dengan simulator wawancara dan tes teknis kami.
Poin-Poin Penting Keamanan REST API Symfony
- LexikJWTAuthenticationBundle 3.2 menyediakan autentikasi JWT siap produksi untuk Symfony 7.2 dengan signing kunci RSA
- Konfigurasi firewall stateless dengan
json_loginuntuk kredensial danjwtuntuk rute terlindungi - Implementasikan refresh token dengan JWTRefreshTokenBundle untuk mempertahankan sesi panjang tanpa menyimpan kredensial di sisi klien
- Gunakan voter untuk keputusan otorisasi level objek yang tidak dapat diekspresikan aturan access control
- Rate limit endpoint login untuk mencegah serangan brute force, aktif sebelum validasi password
- Tambahkan klaim kustom ke payload JWT melalui event listener, hindari data sensitif dalam payload yang dapat dibaca
- Uji alur autentikasi dengan test client Symfony dan token JWT yang diinjeksi
- Ikuti checklist produksi: rotasi kunci, HTTPS saja, validasi input, dan logging keamanan
Bisakah kamu menemukan bug di Symfony?
Satu potongan kode nyata, satu bug tersembunyi, satu percobaan per hari. Tanpa akun untuk mencoba.

Ditulis oleh
Anthony Fillion-MailletPendiri SharpSkill
Developer fullstack selama lebih dari 10 tahun. Ia menjalankan SharpSkill dan bertanggung jawab atas semua yang diterbitkan di sini.
Diperbarui 26 Agustus 2026
Bagikan
Artikel terkait

Keamanan REST API Symfony di 2026: OAuth2, Rate Limiting, dan Pertanyaan Interview
Pelajari cara mengamankan REST API Symfony dengan OAuth2 token introspection, rate limiting, dan validasi JWT. Membahas fitur keamanan Symfony 7.3, kerentanan umum, dan pertanyaan interview teknis.

API Platform dengan Symfony 2026: Arsitektur Modern dan Pertanyaan Wawancara
Panduan lengkap API Platform dengan Symfony di 2026, membahas arsitektur SmartPlatform, State Providers, custom filters, dan pertanyaan wawancara teknis untuk developer.

API Platform GraphQL Symfony: Skema, Mutasi, dan Pertanyaan Interview 2026
Panduan lengkap integrasi API Platform GraphQL dengan Symfony. Pelajari skema otomatis, mutasi, resolver kustom, keamanan, dan pertanyaan interview teknis untuk developer 2026.