Nginx Rate Limit için Cookie Tabanlı Beyaz Liste ve WordPress Admin Paneline Girebilen Kullanıcılara Cookie Ayarlama


PHP yada benzeri statik olmayan web sayfları sunucudan her çağrıldığında arka planda işlem yapılır ve bu işlemci ve bellek kullanımına sebep olur. Bazı saldırganlar sunucuya bir birinden farklı istekleri göndererek arka planda işlemci kullanımını arttır ve buda diğer kullanıcılarınızın web sitenize girerken yavaş açılmasına yada hiç açılmamasına sebep verir.

Nginx Rate Limit fonksiyonu ile sunucuya gelen istekleri limitleyebiliyoruz bizde dijitaller.com üzerinde Nginx bu fonksiyonundan yaralanıyoruz.

WordPress metin editörünün değişmesi ile beraber sitedeki yazılarda etiket eklemede problemler yaşamaya başladık. Geliştirici aracına baktığımda direk olarak isteklerin rate limite takıldığımı gördüm. Klavyeden her bir tuşa bastığımızda sunucuya bir istek gittiği gözükyor. Bunu aşmanın yollarından biri o URL’a giden isteklerde rate limiti kaldırmak fakat bu bir güvenlik açığı olacağından bunu uygulamak istemedim. Bu yüzden sadece login olmuş olan kullanıcılardan rate limiti kaldırdım.

Login olan kullanıcılara rate limit nasıl kaldırılacak ?

Normal koşullarda nginx de yeni bir limit alanı oluşturmak için http bloğu içine limit_req_zone $binary_remote_addr zone=dijitaller:10m rate=1r/s; benzeri bir kod ekleriz. Nginx map fonksiyonundan yararlanarak bu kodu limit_req_zone $dijitaller_ratelimit zone=dijitaller:10m rate=1r/s; çeviriyoruz ve yeni bir map bloğu oluşturuyoruz.

map $cookie_ratelimit $dijitaller_ratelimit {
        default $binary_remote_addr;
        ratelimitcookiekey "" ;
}

Yukarıda görüldüğü gibi gelen normal isteklere her ip başına göre bir limitlendirme uygulanacak. Eğer gelen istekte ratelimit diye bir cookie varsa ve buda sizin belirlediğiniz bir şifreye (örneğimizde şifre ratelimitcookiekey) eşit ise limitlendirme boş olarak dönüyor ve sistem bu cookie barındıran isteklerin hiçbirini limitlendirmiyor. İnternette buna benzer örneklendirmenin ip tabanlı olduğunu gördüm fakat editörlerimiz Statik ip adresi kullanmıyor ve mobilden de erişmeleri gerekebiliyor. Kendi vpn yada proxy çözümlerimizi kullanabiliriz ama bazen insanın bunlarla uğraşası gelmiyor direk olarak işini halletmek istiyor ekstra bir adım daha uygulamak işinden vazgeçme sebebi olabiliyor.

Peki sitede yazı yazma yani wp-admin paneline girebilen editörlerimiz için bu cookie nasıl ayarlayacağız ?

Bunun en kolay ve iyi olabilecek yolu tabiki WordPress de işimize yarayacak bir PHP dosyasında değişiklik yaparak kullanıcı login olduğunda Suncucu cevabına set-cookie üst bilgisini eklemektir fakat bunu WordPress dosylarında hiçbir değişiklik yapmadan da Nginx üzerinde yapmanın bir yolu var. Böylece wordpress dosyası güncellendiğinde tekrar düzeltme ile uğraşmamıza gerek kalmıyor.

Kullanıcı eğer wordpress üzerinde /wp-admin/posts.php sayfasına erişmek istediğinde ve eğer login olmadıysa sunucudan 200 kodu değil 302 kodu ile beraber /wp-login.php sayfasına yönlendiriliyor. Yani sadece login olmuş kullanıcılar /wp-admin/posts.php sayfasında 200 cevabı ile karşılaşıyorlar. Bizde buna istinaden /wp-admin/posts.php sayfasına bir kullanıcı girdiğinde bu istek 200 olarak dönüyorsa Nginx add_header fonksiyonunu kullanarak Set-Cookie üst bilgisini ekliyoruz ve bu bilgide ratelimit=ratelimitcookiekey;Domain=$host;Path=/;Max-Age=100000 şeklinde oluyor .

Gönül isterdiki Nginx server bloğu içinde .php bloğunda if ile add_header kullanalım ama maalesef bu mümkün değil. if içinde if tekrar kullanamıyoruz. Değişken ile birden çok if bir arada kullanılabiliyor fakat if te $status kullanamıyoruz. if içindeki işlem response_code değişiklik yapabileceğinden if($status=200) diye bir ifade yada $status a bağlı bir map da kullanılamıyor.

Bu sorunu tekrardan map kullanarak aşıyoruz.

map "$status$uri" $ratelimit_key {
        "cevap_kodu/istenensayfa"        ratelimitcookiekey;
        "200/wp-admin/post-new.php"        ratelimitcookiekey;
        default no;
 }

Yukarıdaki map bloğunu add_header Set-Cookie "ratelimit=$ratelimit_key;Domain=$host;Path=/;Max-Age=100000"; şeklinde kullanabiliriz fakat burada şöyle bir problem oluşuyor belirlenen bir sayfa dışında bir sayfaya erişmek istediğimizde ratelimit=no olarak eşitleniyor ve admin kullanıcımız yine rate limite takılmış oluyor.

map  $ratelimit_key $ratelimit_if {
         ratelimitcookiekey   ratelimit;
         default         svauth;
 }

Yukarıda yazığımız map fonksiyonunda rate_limit anahtarının döndüğü durumlarda bize ratelimit cevabı veren dönmediğinde ise svauth cevabı veren bir $ratelimit_if değişkeni var. Bu değişkeni add header fonksiyonunda Set-Cookie değişkeninde add_header Set-Cookie "$ratelimit_if=$ratelimit_key;Domain=$host;Path=/;Max-Age=100000"; olarak kullanarak istediğimiz sayfa ve cevap kodu olmayan durumlarda svauth=no diye bir cookie belirliyoruz ama istediğimiz durum ve sayfa olduğunda ise ratelimit=ratelimitcookiekey eşitlemiş oluyoruz ve böylece listede olmayan bir sayfada ratelimit çerezi “no” olarak belirlenmemiş oluyor.

Rehberde bulunan ratelimitcookiekey sizin belirlediğiniz bir şifre ile değiştirerek, dijitallercom yazan yerleride yine kendi bilgilerinize göre uygulayıp kendi sunucunuzda kullanabilirsiniz. Örnek olması amacıyla aşağıdaki örnek suncu ayar dosyasına bakabilirsiniz. Örnekte dikkat etmeniz gereken nokra map kısmını ve limit_req_zone’u server bloğu dışına eklemeniz ve server/php bloğu içinde limit_req ve add_header kısımlarını eklemeniz dir.

map $cookie_ratelimit $dijitaller_ratelimit {
        default $binary_remote_addr;
        C8141t4966y "" ;
}

map "$status$uri" $ratelimit_key {
        "200/wp-admin/index.php"        C8141t4966y;
        "200/wp-admin/post-new.php"        C8141t4966y;
        default no;
}

map  $ratelimit_key $ratelimit_if {
        C8141t4966y    ratelimit;
        default         sv-auth;
}

limit_req_zone $dijitaller_ratelimit zone=dijitaller:10m rate=1r/s;

...

server {
    listen [::]:443 http2 ssl default;
    root /usr/share/www.dijitaller.com ;

    server_name www.dijitaller.com;
    charset utf-8;
...
location ~ \.php$ {
...
        limit_req zone=dijitaller burst=1 nodelay;
        add_header Set-Cookie "$ratelimit_if=$ratelimit_key;Domain=$host;Path=/;Max-Age=100000";
}

...
...
location ~ /readme.html {
        deny all;
    }

    location / {
            try_files $uri $uri/ /index.php?$args;
    }

}

Ahmet ÖZER
Dijitaller'in Sunucu ve Ağ altyapısını kuran, düzenleyen. Genel olarak teknoloji ve bilim ile uğraşmayı severim . Detaylı bilgi için https://ahmetozer.org. Lütfen sorularınızı Dijitaller.com Yorumlar veya Twitter üzerinden iletin.

0 Yorum

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir