Symfony: Route bazında IP kısıtlaması
Selamlar,
Bugün sizlerle Symfony Kernel Event Subscriber kullanarak route’larımızı nasıl IP kısıtlaması ile koruyabileceğimizi inceleyeceğiz. Event Subscriber kavramını daha iyi anlayabilmek için adım adım Symfony Kernel ile başlayarak ilerleyelim.
1. Nedir bu Symfony Kernel?
Symfony Kernel, diğer bir deyişle Symfony Framework’ün kalbi.
The Kernel is the heart of the Symfony system.
It manages an environment made of bundles.
~Fabien Potencier
Temel olarak görevi, framework içerisinde kullanılacak tüm paketleri(bundle) ve konteyneri ayağa kaldırıp birbirleri ile senkronize bir şekilde çalışmalarını sağlamak. Gelen istekleri karşılayıp üretilen cevapları iletmek.
2. Peki ya Symfony Kernel Event?
Kernel çalışmasının her bir adımında farklı bir event tetikler. Bunlar aşağıdaki gibidir; hepsinin detayına girmeyeceğim, dördüncü adımda geliştireceğimiz örnekte kullanılacağı için sadece RequestEvent’in detayına gireceğiz.
- ControllerArgumentsEvent
- ControllerEvent
- ExceptionEvent
- FinishRequestEvent
- RequestEvent
- ResponseEvent
- TerminateEvent
- ViewEvent
RequestEvent
Symfony bir HTTP isteğini işlerken öncelikle RequestEvent tetikler. Bu framework’e bir istek geldiğini gösterir ve bir sonraki başlıkta anlatacağımız Event Subscriber ile dinlenilebilir. Eğer RequestEvent Subscriber tarafından işlenip bir response tanımlanırsa doğrudan bu response yanıt olarak client’a gönderilir. Eğer RequestEvent için bir response tanımlı değil ise kod çalışmaya devam ederek gerekli yanıtı üretir.
3. Symfony Kernel Event Subscriber
Bir önceki başlıkta bahsettiğimiz tüm Kernel Eventleri Subscriber ile dinleyip işleyebiliriz. Projemizin src/EventSubscriber
dizininde EventSubscriberInterface
ile oluşturacağımız her sınıf Symfony tarafından otomatik olarak yüklenip çalışmaya başlayacaktır. EventSubscriberInterface
tarafından zorunlu tutulan getSubscribedEvents()
metodu ile yazdığımız Subscriber sınıfı içerisindeki hangi metodun hangi event için çalışacağını ve istersek önceliklendirmesini belirtebiliriz. Bir sonraki başlıkta uygulamalı olarak nasıl kullanılacağını göreceksiniz.
Not: Buna ek olarak unutmamamız gereken konu biz her event için kendi içerisinde önceliklendirme yapabiliriz. ControllerEvent, RequestEvent gibi tetiklenme sırasını HttpKernel içerisindeki handleRaw
metodunu overwrite etmeden değiştiremeyiz.
4. Symfony Event Subscriber ile route bazında IP kısıtlaması nasıl yapılır?
Önceki başlıklarda Kernel, Event, Subscriber gibi terimlere değindik. Şimdi hepsini bir araya getirerek uygulamamızdaki bazı adresleri IP kısıtlaması ile koruyalım.
Yapacağımız örnekte bir sanal posun kullandığı webhook adresini IP kısıtlaması ile başka kaynaklardan gelen istekleri kabul etmemesi için koruyacağız.
Öncelikle bir Subscriber sınıfı oluşturuyoruz.
php bin/console make:subscriber IPRestriction
Bu komutu çalıştırdığımızda bize sistem içerisindeki Subscribe olabileceğimiz eventleri listeyecek ve aralarından bir seçim yapmamızı isteyecek.
Seçimimizi yaptıktan sonra ise sınıfımızı oluşturuyor ve bizi ihtiyaç halinde kullanmamız için dokümantasyon adresine yönlendiriyor.
Bu işlemler sonrasında aşağıdaki gibi bir dosya oluşuyor.
<?php
namespace App\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
class IPRestrictionSubscriber implements EventSubscriberInterface
{
public function onKernelRequest(RequestEvent $event)
{
// ...
}
public static function getSubscribedEvents()
{
return [
'kernel.request' => 'onKernelRequest',
];
}
}
Şimdi dosya içerisinde hakimiyetimizi artırmak bir kaç değişiklik yapalım.
İlk olarak onKernelRequest
metodunun ismini checkForIpRestriction
olarak değiştiriyorum. Subscriber sınıfı içerisinde bu metodun ismini istediğimiz gibi değiştirmekte özgürüz. getSubscribedEvents
metodunda ise yeni belirlediğimiz metod ismini kullanacağız.
class IPRestrictionSubscriber implements EventSubscriberInterface
{
public function checkForIpRestriction(RequestEvent $event): void
{
// TODO:
}
public static function getSubscribedEvents(): array
{
return [
KernelEvents::REQUEST => 'checkForIpRestriction',
];
}
}
Sonrasında bu sınıfa iki property ekleyerek ip whitelist ve korunacak route isimlerini yazalım.
class IPRestrictionSubscriber implements EventSubscriberInterface
{
private array $whitelist = [
'85.111.48.36', // iyzico sandbox ip
'85.111.9.165', // iyzico sandbox ip
];
private array $protectedRoutes = [
'iyzico.webhook',
];
// ...
}
Son olarak checkForIpRestriction
metodu içerisinde gerekli kontrolleri sağlayalım.
class IPRestrictionSubscriber implements EventSubscriberInterface
{
// ...
public function checkForIpRestriction(RequestEvent $event): void
{
$request = $event->getRequest();
$clientIp = $request->getClientIp();
$route = $request->attributes->get('_route');
if (in_array($route, $this->protectedRoutes) && !in_array($clientIp, $this->whitelist)) {
$event->setResponse(new Response('You are not allowed to access this service.', 403));
}
}
// ...
}
checkForIpRestriction
metodu özetle RequestEvent üzerinde request
bilgilerine erişiyor ve isteği gönderen client ip bilgisi ile isteğin yapıldığı route ismini alıyoruz. Sonrasında ise isteğin geldiği route ve istek yapan ip adresini kontrol ederek gerekli durumlarda isteği engelliyoruz. Bunun için $event
objesinin setResponse
metodu ile isteğe karşılık dönmesini istediğimiz yanıtı veriyoruz.
Peki ya Symfony Kernel Event?
başlığı altında da bahsettiğimiz gibi eğer $event
objesine bir response tanımlandı ise framework doğrudan bu yanıtı iletecek ve controller katmanına geçmeden isteği sonlandıracaktır. Bu sayede istediğimiz servisleri yabancılardan gelecek isteklere karşı korumuş olacağız.
Makalemizin sonuna geldik. Konuyla alakalı soru ve görüşlerinizi ister yorum olarak isterseniz de profilimdeki twitter adresimden iletebilirsiniz. Yeni makalelerde görüşmek üzere hoşçakalın.