İçeriğe atla
0 XP 0
14 dkderinleşmeyle 15 dk50 XP7 etkinlik

HTTP isteği ve yanıtı

Bu derste
  • HTTP isteğini ve yanıtını satır satır okumayı, metotları, durum kodlarını, curl ile DevTools kullanımını ve güvenlik başlıklarını öğrenirsin.
  • Her tıklama bir metin mesajına dönüşür. Sunucu o mesajı okur ve yine metinle yanıtlar.
  • İstek üç parçadan oluşur: başlangıç satırı, başlıklar ve gövde.
  • Durum kodunu okumayı öğrenirsen, sunucunun ne düşündüğünü anlarsın.

Puan bütün etkinlikleri cevaplayınca hesaba yazılır. Bu dersi tamamlayınca +50 XP alacaksın. Giriş yapmadan ilerlersen puan yalnız bu tarayıcıda durur.

Tarayıcıda bir bağlantıya tıklarsın. Arka planda tarayıcı düz bir metin yazar ve sunucuya yollar. Sunucu o metni okur, yine metinle yanıt verir.

HTTP bundan ibarettir: iki taraf arasında kurallı metin alışverişi. Kuralı bilirsen isteği kendin de yazarsın, araya girip değiştirirsin ve yanıtı satır satır yorumlarsın. Web güvenliğindeki her konu bu mesajların üstüne kurulur.

Çerez bir başlıktır, enjeksiyon bir parametredir, CORS bir yanıt başlığıdır. Bu derste isteğin parçalarını, metotları, durum kodlarını ve temel güvenlik başlıklarını öğreneceksin. Sonunda aynı isteği hem tarayıcının geliştirici araçlarında hem curl ile inceleyeceksin.

Bir isteğin anatomisi

İstek üç parçaya ayrılır. İlk satıra başlangıç satırı denir ve üç öğe taşır: metot, istek hedefi ve sürüm. Altında başlıklar gelir; her biri ad, iki nokta ve değerden oluşan bir satırdır. Başlıklar bitince bir boş satır gelir.

En sonda, isteğe bağlı olarak gövde yer alır. RFC 9112 bu sırayı harfiyen tanımlar: başlangıç satırı, başlık satırları, boş satır, gövde. Satırlar CRLF ile, yani satır başı ve alt satır karakterleriyle biter. Aşağıda bir giriş formunun sunucuya yolladığı ham isteği görüyorsun.

POST /api/giris HTTP/1.1
Host: hedef.example
Content-Type: application/json
Content-Length: 40

{"kullanici":"ege","parola":"deneme123"}
Ham bir HTTP isteği. Başlıklar ile gövde arasında boş satır durur.

Satır satır oku. İlk satır der ki: /api/giris hedefine POST metoduyla, HTTP/1.1 kurallarıyla yazıyorum.

İkinci satırdaki Host, isteğin hangi siteye gittiğini söyler. Tek bir sunucu onlarca siteyi aynı IP adresinde barındırabilir ve hangisinin istendiğini bu başlıktan anlar. Bu yüzden HTTP/1.1'de Host zorunludur; eksikse sunucu 400 döner.

Üçüncü satır gövdenin biçimini, dördüncü satır uzunluğunu bayt olarak verir. Gövde tam 40 bayttır. Ardından gelen boş satır başlıkların bittiğini işaretler. Son satır gövdenin kendisidir: kullanıcı adı ve parola, JSON biçiminde.

Başlık adları büyük küçük harfe duyarsızdır; content-type ile Content-Type aynı başlıktır. Farklı adlı başlıkların sırası anlamı değiştirmez. Yine de RFC 9110, Host gibi yönlendirme bilgisini ilk sıraya koymayı iyi uygulama sayar.

Sunucu gövdeyi Content-Type başlığına bakarak ayrıştırır. Klasik bir HTML formu application/x-www-form-urlencoded yollar ve gövde kullanici=ege&parola=deneme123 gibi görünür. Bir API ise çoğu zaman application/json bekler.

Tür yanlışsa sunucu gövdeyi okuyamaz ya da yanlış yorumlar. Content-Length da bir sözdür. Değer gerçek gövdeden kısaysa sunucu gövdeyi erken keser, uzunsa eksik baytları bekleyip zaman aşımına düşer.

Etkinlik 1 / 7

Sunucu JSON gövdeyi ayrıştıramıyor. Eksik başlığı doğru değerle doldur.

İsteği tamamla ve gönder.

POST /api/giris HTTP/1.1
Host: hedef.example
Content-Length: 40
Content-Type

Etkinlik 2 / 7

Bir HTTP isteğinin parçalarını yazılma sırasına koy.

Sürükleyip doğru sıraya koy.

Bir öğeye odaklan, yukarı ve aşağı ok tuşlarıyla taşı. Home tuşu en üste, End tuşu en alta alır.

  • 1Gövde
  • 2Boş satır
  • 3Content-Type başlığı
  • 4Metot, yol ve sürüm satırı
  • 5Host başlığı

Metotlar: isteğin fiili

Metot niyeti anlatır. GET bir kaynağı okur. HEAD aynı işi yapar ama yalnız başlıkları ister, gövdeyi istemez. POST sunucuya işlenecek veri yollar ve çoğu zaman yeni kayıt yaratır.

PUT bir kaydın tamamını verilen içerikle değiştirir. PATCH kaydın yalnız bir kısmını günceller. DELETE kaydı siler. OPTIONS, hedefin hangi metotları desteklediğini sorar; tarayıcı CORS ön kontrolünde bu metodu kullanır.

Metot ile yol birlikte bir niyet cümlesi kurar. GET /urun/42 kırk iki numaralı ürünü göster demektir. DELETE /urun/42 aynı kaydı sil demektir. Aynı yol, farklı metot, bambaşka sonuç.

Standart metotları iki özelliğe göre ayırır. Güvenli metot sunucudaki durumu değiştirmez, yalnız okur: GET, HEAD ve OPTIONS böyledir. İdempotent metotta aynı isteği bir kez de yollasan on kez de yollasan etki aynı kalır.

Güvenli metotların hepsi idempotenttir; PUT ve DELETE de bu gruba girer. Aynı kaydı iki kez silmek, bir kez silmekle aynı sonucu doğurur.

POST ve PATCH ise ne güvenli ne idempotent sayılır. Aynı sipariş formunu iki kez yollarsan iki sipariş oluşabilir. Standart bu yüzden istemcinin idempotent olmayan bir isteği kendiliğinden tekrarlamamasını ister.

Güvenlik açısından bu sözün tutulması önemlidir. Durum değiştiren bir işi GET ile yaparsan, örneğin /hesap/sil?id=7 gibi bir bağlantıyla, o bağlantı bir e-postada ya da bir resim etiketinde kendiliğinden tetiklenir. Siteler arası istek sahteciliği bu tür uçları sever. Test eden kişi de metodu değiştirerek denetimi sınar.

Uygulama DELETE isteğinde yetki kontrolü yapıp aynı işi gören POST ucunu unutmuş olabilir. Savunma basittir: okuma GET ile, değişiklik öbür metotlarla yapılır. Desteklenmeyen metoda sunucu 405 Method Not Allowed dönmeli, beklenmeyen metodu sessizce kabul etmemelidir.

Etkinlik 3 / 7

Metotları standardın tanımladığı özelliklere göre ayır.

Önce bir seçeneğe, sonra aşağıdaki uygun kutuya bas. İstersen seçeneği kutuya sürükleyip bırak. Yerini değiştirmek için aynı işlemi tekrarla.

0/7 seçenek yerleşti.

Yanıt ve durum kodları

Yanıt da aynı yapıyı taşır. İlk satıra durum satırı denir: sürüm, üç haneli durum kodu ve kısa bir açıklama. Altında başlıklar, boş satır ve gövde gelir. Kodun ilk hanesi aileyi belirler.

2xx başarıyı, 3xx yönlendirmeyi, 4xx istemcinin hatasını, 5xx sunucunun hatasını anlatır. Açıklama metni yalnız insan içindir; programlar kararı sayıya bakarak verir. Aşağıdaki yanıt yeni bir kaydın yaratıldığını söylüyor. Location başlığı yeni kaydın adresini verir, gövde de kaydın kendisini döndürür.

HTTP/1.1 201 Created
Location: /api/urun/43
Content-Type: application/json
Content-Length: 39

{"id":43,"ad":"Kablosuz fare","stok":5}
Yeni kayıt yaratan bir POST isteğine gelen yanıt.

Başarı ailesinde üç kod sık görünür. 200 isteğin tamamlandığını, 201 yeni bir kaynak yaratıldığını söyler. 204 işin bittiğini ama dönecek gövde olmadığını belirtir; başarılı bir silme çoğu zaman 204 döner.

Yönlendirme ailesinde yanıt, Location başlığıyla yeni adresi gösterir. 301 kalıcı taşınmadır, 302 ve 307 geçici yönlendirmedir.

307 ile 308, tarayıcının metodu ve gövdeyi değiştirmeden yeni adrese gitmesini şart koşar. Yönlendirme güvenlikte de önemlidir. Location değerini kullanıcı girdisinden alan uygulama açık yönlendirme (open redirect) açığı doğurur ve oltalama bağlantısına güvenilir bir alan adı kazandırır. Önlemi basittir: yönlendirme hedefini kullanıcıdan olduğu gibi alma, sabit bir izin listesinden seç.

İstemci hatalarını yakından tanı. 400 isteğin bozuk olduğunu söyler. 401 der ki: kim olduğunu söylemedin.

Standart bu yanıtın WWW-Authenticate başlığıyla kimliğin nasıl sunulacağını anlatmasını ister. 403 der ki: kim olduğunu biliyorum, yine de giremezsin. 404 kaynağın bulunmadığını, 405 metodun desteklenmediğini anlatır.

429 hız sınırıdır: çok hızlısın, biraz bekle. Yanıtta Retry-After başlığı varsa, ne kadar beklemen gerektiğini yazar. 401 ile 403'ü karıştırmak çok yaygındır. İlki kimlik doğrulamayla, ikincisi yetkilendirmeyle ilgilidir.

Sunucu hataları da iz bırakır. 500 uygulamanın beklenmeyen bir durumda çöktüğünü söyler. 502 ve 504, önde duran bir ağ geçidinin arkadaki sunucudan sorun gördüğünü anlatır: 502'de geçersiz yanıt aldı, 504'te zamanında yanıt alamadı.

503 geçici aşırı yük ya da bakım demektir. Güvenlik gözüyle 500 değerli bir sinyaldir. Tek bir tırnak işaretiyle 500 dönen form, girdinin arka tarafta beklenmedik bir yere ulaştığını ele verir.

Savunma iki adımdır. Kullanıcıya ayrıntısız bir hata sayfası göster, yığın izini yalnız loga yaz. Aynı kaynaktan ani 500 artışını da olası bir girdi denemesi olarak incele.

Etkinlik 4 / 7

Sunucu isteği anladı, kullanıcıyı da tanıdı, ama bu kaynağa yetkisi yok. Hangi kodu döner?

Etkinlik 5 / 7

Sunucu istenen kaydı bulamazsa ____ kodunu döner.

İsteği incelemek: DevTools ve curl

Bir isteği görmenin en kolay yolu tarayıcının geliştirici araçlarıdır. F12 ile açılan panelde Network sekmesine geç ve sayfayı yenile. Her satır bir istektir: sayfanın kendisi, stil dosyaları, betikler, API çağrıları.

Bir satıra tıkladığında Headers bölümünde başlıkları, Payload bölümünde gövdeyi, Response bölümünde dönen içeriği görürsün. Preserve log kutusunu işaretlersen sayfa yönlendirilse de kayıtlar silinmez; giriş akışını izlerken bu çok işe yarar. Satıra sağ tıklayıp Copy as cURL seçersen, aynı isteği komut satırında tekrar çalıştıracak hazır bir komut alırsın.

curl komut satırının HTTP istemcisidir ve neredeyse her sistemde kuruludur. Birkaç bayrak yeter. -v giden isteği ve gelen yanıtı ayrıntılı gösterir.

-i yanıt başlıklarını gövdenin önüne ekler. -I yalnız başlıkları ister ve HEAD isteği yollar. -X metodu seçer, -H başlık ekler, -d gövde yollar.

Aşağıda hedef.example adresindeki bir API'ye atılmış isteğin ayrıntılı çıktısı var. Bu sunucu düz HTTP konuşuyor, bu yüzden çıktı kısa. HTTPS kullanan bir sitede TLS el sıkışmasının satırlarını da görürsün.

$ curl -v http://hedef.example/api/urun/42
*   Trying 203.0.113.10:80...
* Connected to hedef.example (203.0.113.10) port 80
> GET /api/urun/42 HTTP/1.1
> Host: hedef.example
> User-Agent: curl/8.7.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: application/json
< Content-Length: 42
< Cache-Control: no-store
<
{"id":42,"ad":"Kablosuz klavye","stok":12}
* Connection #0 to host hedef.example left intact
curl -v çıktısı. Yıldız curl'ün notu, > giden istek, < gelen yanıttır.

Satır başındaki işaret yönü söyler. Yıldızlı satırlar curl'ün kendi notlarıdır: 203.0.113.10 adresine 80 numaralı porttan bağlandı. Büyüktür işaretli satırlar senin yolladığın istektir; Host ve User-Agent başlıklarını curl kendisi ekledi. Tek başına duran büyüktür işareti boş satırdır.

Küçüktür işaretli satırlar yanıttır: 200 OK, JSON gövde, 42 baytlık uzunluk ve önbelleğe alınmaması istenen bir içerik. En alttaki satır gövdenin kendisidir. JSON gövdeli bir POST için -H ile türü, -d ile gövdeyi yazarsın: curl -X POST -H 'Content-Type: application/json' -d '{"kullanici":"ege"}' http://hedef.example/api/giris. -d tek başına türü form olarak işaretler, bu yüzden JSON'da -H şarttır.

Sürüm de bir şey anlatır. HTTP/1.1 isteği düz metin yazar ve bir bağlantıda istekleri sırayla taşır. HTTP/2 aynı anlamı ikili çerçevelere böler, başlıkları sıkıştırır ve tek bağlantıda birçok isteği paralel taşır. Başlangıç satırı kaybolmaz; :method ve :path gibi sözde başlıklara dönüşür.

HTTP/3 aynı fikri UDP üstünde çalışan QUIC protokolüne taşır, böylece kayıp bir paket öbür akışları bekletmez. Üç sürümde de metodun, durum kodunun ve başlıkların anlamı RFC 9110 ile sabit kalır. DevTools'ta Protocol sütununu açarsan isteğin http/1.1, h2 ya da h3 ile gittiğini görürsün.

Derinleş:HTTP'nin kısa tarihi

HTTP'nin ilk hâli 1991'de tek satırlık bir protokoldü. İstemci yalnız GET ve bir yol yazardı; başlık da durum kodu da yoktu. Bu sürüm sonradan HTTP/0.9 diye anıldı.

1996'da RFC 1945 HTTP/1.0'ı belgeledi ve başlıklarla durum kodları geldi. HTTP/1.1 önce 1997'de RFC 2068, sonra 1999'da RFC 2616 ile tanımlandı. Kalıcı bağlantı ve zorunlu Host başlığı bu sürümle yerleşti.

2014'te RFC 2616 altı ayrı belgeye bölündü (RFC 7230-7235). Bugünkü düzen Haziran 2022'de kuruldu: RFC 9110 anlamı, RFC 9111 önbelleği, RFC 9112 HTTP/1.1 söz dizimini, RFC 9113 HTTP/2'yi, RFC 9114 HTTP/3'ü tanımlar. Anlamın ayrı bir belgede durması bilinçli bir karardır. Taşıma biçimi yenilenir, metotların ve durum kodlarının anlamı aynı kalır.

Etkinlik 6 / 7

http://hedef.example/api/urun/42 adresinden yalnız yanıt başlıklarını iste. Gövde gelmesin.

ogrenci@lab

Yolculuk, savunma başlıkları ve izleme

TarayıcıistemciDNSad → IPGüvenlik duvarıport filtresiWeb sunucu443/tcpVeritabanı3306/tcp
İsteğin tarayıcıdan veritabanına uzanan yolu. Her durak isteği okur, değiştirebilir ve loglar.

İstek doğrudan uygulamaya varmaz. Önce alan adı DNS ile IP adresine çevrilir. Sonra istek bir güvenlik duvarından, çoğu zaman bir yük dengeleyiciden ve ters vekilden geçer.

Büyük kurumlarda araya içerik dağıtım ağı ve web uygulama güvenlik duvarı da girer. Her durak başlık ekler ya da çıkarır. Ters vekil, istemcinin gerçek adresini X-Forwarded-For başlığına yazar.

Arkadaki uygulama bu başlığa körü körüne güvenirse, istemci başlığı kendisi yazıp IP tabanlı sınırları aşar. Doğrusu, bu başlığı yalnız kendi vekilinden geldiğinde okumaktır. Bir durağın zararsız saydığı isteği öbür durak farklı yorumlarsa, arada açık doğar.

Derinleş:İstek kaçakçılığı

Gövdenin nerede bittiğini söylemenin iki yolu vardır. Content-Length bayt sayısını verir. Transfer-Encoding: chunked ise gövdeyi parçalar hâlinde yollar ve sıfır uzunluklu bir parçayla bitirir.

Bir istek iki başlığı birden taşırsa, RFC 9112'ye göre Transfer-Encoding önceliklidir ve böyle bir mesaj şüphelidir. Sorun, öndeki vekil ile arkadaki sunucunun bu kuralı farklı uygulamasıyla doğar. Biri gövdeyi daha uzun sayar, öbürü daha erken bitirir.

Artan baytlar arkadaki sunucuda bir sonraki isteğin başı gibi okunur ve başka bir kullanıcının isteğine karışır. Bu sınıfa HTTP istek kaçakçılığı (request smuggling) denir. Teknik ilk kez 2005'te bir araştırma raporunda tanımlandı ve 2019'da yeni çeşitleriyle yeniden gündeme geldi. Savunma belirsiz istekleri reddetmek, arka tarafta da HTTP/2 kullanmak ve şüpheli mesajdan sonra bağlantıyı kapatmaktır.

Yanıt başlıkları tarayıcıya güvenlik talimatı da verir. Strict-Transport-Security, yani HSTS, tarayıcıya bu siteye belirli bir süre yalnız HTTPS ile gelmesini söyler. Açık bir Wi-Fi ağında bağlantının düz HTTP'ye indirilmesini zorlaştırır. Content-Security-Policy, yani CSP, sayfanın hangi kaynaklardan betik yükleyebileceğini listeler ve XSS'in etkisini daraltır.

X-Content-Type-Options: nosniff, tarayıcının içerik türünü tahmin etmesini engeller. Bir sitenin bu başlıkları yollayıp yollamadığını curl -I ile bir saniyede görürsün. Eksik başlık tek başına açık değildir, ama bir savunma katmanının kapalı olduğunu gösterir.

HTTP/1.1 200 OK
Strict-Transport-Security: max-age=31536000; includeSubDomains
Content-Security-Policy: default-src 'self'; object-src 'none'
X-Content-Type-Options: nosniff
Güvenlik başlıkları taşıyan bir yanıttan kesit.

Durum kodu 200 dönmesi işin yolunda gittiği anlamına gelmez. Bazı API'ler hatayı da 200 ile döner ve gerçek sonucu gövdeye yazar: {"durum":"hata","mesaj":"yetersiz bakiye"} gibi. Bir ödeme API'sine çağrı yaptığını düşün.

Yanıtta 200 görüyorsun, ekranda ise işlemin tamamlanmadığı yazıyor. İzleme panelinde de o dakikada yüzlerce 200 satırı var ve hiçbiri kırmızı değil. Log incelerken yalnız durum koduna göre süzersen, 200 ile dönen başarısız işlemleri gözden kaçırırsın.

İzleme kuralını buna göre yaz: gövdedeki hata alanını da say. Özetle HTTP kurallı metindir. Metot niyeti, durum kodu sonucu anlatır, başlıklar ise bağlamı ve güvenlik talimatlarını taşır.

Etkinlik 7 / 7

Doğru mu, yanlış mı?

Bir API 200 döndürdüyse istek her zaman başarılı olmuştur.

Bu derste bir hata mı gördün? Bildir