Symfony: Route bazında IP kısıtlaması

Mücahit Cücen
istanbulphp
Published in
4 min readApr 4, 2023

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.

vendor/symfony/http-kernel/HttpKernel.php

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. EventSubscriberInterfacetarafı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.

--

--