TL;DR: API'nizin girdisi bir saldırı yüzeyidir, bu yüzden öyle test edin. Büyük boyutlu alanlar, yanlış türler, hatalı biçimlendirilmiş gövdeler ve enjeksiyon dizeleri gönderen olumsuz durumlar yazın, ardından uç noktanın bir 4xx yanıt verdiğinden ve asla bir 5xx yanıt vermediğinden emin olun. Şema doğrulamasını `additionalProperties: false`, enum'lar ve uzunluk sınırları ile bir güvenlik kontrolüne dönüştürün. Her değişiklikte tüm paketi CI'da çalıştırın. Yapay zeka ajanları bunu acil hale getiriyor: yükleri makine hızıyla oluşturup iletiyorlar, bu nedenle "bu veriyi yükle"nin sessizce "bu kodu çalıştır"a dönüşmesi artık ölçekleniyor.
Çoğu test paketi, arayan kişi nazik olduğunda API'nizin çalıştığını kanıtlar. Geçerli bir gövde gönderirsiniz, 200 alırsınız, onaylama geçer. Bu sonuç, gövde düşmanca olduğunda ne olacağı hakkında neredeyse hiçbir şey söylemez. Güvenilmeyen girdi, uç noktanızın kendi kendine oluşturmadığı herhangi bir veridir: istek gövdeleri, sorgu dizeleri, başlıklar, dosya yüklemeleri, webhook yükleri ve bir yapay zeka ajanının anında bir araya getirdiği JSON. Hepsi aynı varsayımı hak eder: birisinin sonunda mümkün olan en kötü sürümünü göndereceği.
Temmuz 2026'da Hugging Face, giriş vektörünün çalınmış bir parola değil, veri olduğu bir güvenlik olayını açıkladı. Bu ihlalden çıkarılan dersleri ayrı olarak ele aldık; bu kılavuz ise uygulamanın yarısıdır. Bir saldırganın göndereceği türde girdileri gönderen testler oluşturacak ve ardından bunları her değişiklikte otomatik olarak çalıştıracaksınız. Kategoriler, bir sekmede açık tutmaya değer olan OWASP API Güvenlik En İyi 10 ile uyumludur. Apidog, sözleşmeyi tasarlamanın ve bu testleri çalıştırmanın bir yoludur, ancak fikirler zaten kullandığınız herhangi bir çerçevede geçerlidir.
Girdi bir saldırı yüzeyidir, bir form alanı değil
Doğrulama genellikle kullanıcı deneyimi nezaketi olarak ele alınır: boş e-postayı yakala, kırmızı bir kenarlık göster, devam et. Bu çerçeve sorunun kendisidir. API'nizin kabul ettiği her alan, arayanın bozabileceği bir sözdür ve her bozuk söz, mantığınıza giden bir yoldur. Küçük bir tamsayı olmasını beklediğiniz bir `limit` parametresi `999999999` olur. Tek bir kelime olmasını beklediğiniz bir `filename` `../../etc/passwd` olur. Ayarları tutmasını beklediğiniz bir `config` nesnesi bir dizi talimata dönüşür.
Güvenlik testi, sona eklenen ayrı bir disiplin değildir. Bu, zaten bildiğiniz aynı negatif testtir ve size en çok zarar verme olasılığı olan alanları hedefler. "Bu alana sığan en kötü şey nedir" diye sorma alışkanlığını edinirseniz, API güvenlik en iyi uygulamaları kılavuzumuzdaki uygulamaların büyük bir kısmına ulaşmış olursunuz. Bu makalenin geri kalanı, bu tek soruyu çalıştırabileceğiniz somut testlere dönüştürüyor.
"Bu veriyi yükle" nasıl "bu kodu çalıştır" oldu
Hugging Face olayı, girdinin neden bu kadar dikkat edilmesi gerektiğinin açık bir örneğidir. Hugging Face, giriş vektörünün kötü niyetli veri kümeleri olduğunu söyledi: özel olarak hazırlanmış bir veri kümesi uzaktan kod veri kümesi yükleyicisini tetikledi ve bir şablon enjeksiyonu bir veri kümesi yapılandırmasının içine yerleştirildi. Şirketin kendi açıklamasını güvenlik olayı raporunda okuyabilirsiniz.
Bu hatanın şekli üzerinde durun. Bir uç nokta, veri olarak tanımlanan bir şeyi kabul etti. Bu verinin yüklenmesi, saldırgan tarafından kontrol edilen komutları çalıştırabilecek bir kod yolunu başlattı. "Bu veriyi yükle" "bu kodu çalıştır" haline geldi. Şablon enjeksiyonu, daha küçük ölçekte aynı hikaye: eylemsiz bir metin olması beklenen bir yapılandırma değeri değerlendirildi, böylece metin yürütme haline geldi.
Buradan çıkarılacak ders "Hugging Face nadir bir hata yaptı" değil. Herhangi bir yükleyici adı, biçim, şablon, serileştirilmiş nesne veya yapılandırma blobu kabul eden herhangi bir uç noktanın, siz istemeseniz bile talimatları kabul ettiğidir. Bu uç noktaya düşmanca bir yapılandırma gönderen bir test yazmadıysanız, eylemsiz kaldığı varsayımını aslında hiç kontrol etmemişsinizdir. Test edilmeyen bu varsayım, tüm güvenlik açığıdır.
Şema doğrulamasını bir güvenlik kontrolü olarak kullanmak
Ekleyebileceğiniz en ucuz kontrol, uç noktada katı bir şemadır. Şema sadece dokümantasyon değildir. Eşleşmeyen her şeyi reddettiğinizde, şema iş mantığınız isteği görmeden önce çalışan bir filtre haline gelir. JSON Schema, bu filtreyi sıkılaştırmak için size temel öğeler sunar.
İşte hikayeden alınan veri kümesi yapılandırması için bir şema, çoğu düşmanca girdinin uygulama koduna asla ulaşmamasını sağlayacak şekilde yazılmıştır:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"additionalProperties": false,
"required": ["loader", "name"],
"properties": {
"loader": { "enum": ["csv", "json", "parquet"] },
"name": { "type": "string", "maxLength": 128, "pattern": "^[\\w .-]+$" },
"rows": { "type": "integer", "minimum": 0, "maximum": 1000000 }
}
}
Bunu dört ayrı savunma olarak okuyun. `additionalProperties: false` gizlice sokulmuş bir `template` alanını doğrudan reddeder, böylece bir saldırgan ekleyemez. `loader` enum'ı, `pickle://` veya herhangi bir uzaktan kod yükleyicisinin geçerli bir değer olmadığı anlamına gelir. `maxLength`, belleği tüketmeyi hedefleyen çok megabaytlık dizeyi öldürür. `name` üzerindeki `pattern`, `{{` ve `'; DROP TABLE` karakterlerini daha fazla ilerlemeden reddeder. Bu satırların hiçbiri saldırganları bilmez. Yalnızca gerçekten desteklediğiniz dar bir girdi kümesini kabul ederler ve bu darlık güvenlik özelliğidir.
Bu tür sözleşme doğrulama, her açığı yakalamaz ve hiçbir şema yakalayamaz. Kapattığı şey, belirli ve yaygın bir kategoridir: "bu uç noktanın neyi kabul ettiğini hiç kontrol etmedik" hatası. Bu kategori, şaşırtıcı sayıda ihlalin başladığı yerdir.
Negatif test: uç noktanın hayır dediğini kanıtla
Başarılı yol testleri, iyi girdinin iyi çıktı ürettiğini doğrular. Negatif testler, kötü girdinin kontrollü bir ret ürettiğini doğrular. Bu ayrım önemlidir çünkü ret bir özelliktir: açık bir hata mesajı ile 400, API'nizin sınırını savunmasıdır. 500 ise API'nizin kontrolünü kaybetmesidir.
Negatif durumları her seferinde aynı şekilde oluşturun. Her alan için, neyi reddetmesi gerektiğini yazın: yanlış tür, gerekli olduğunda eksik, yasak olduğunda mevcut, çok uzun, aralık dışında ve biçimine uyan enjeksiyon dizeleri. Ardından yanıtta iki şeyi doğrulayın. Birincisi, durum kodu 4xx'tir, genellikle 400 veya 422. İkincisi, durum kodu asla 5xx değildir. 500, düşmanca girdinizin buna hazır olmayan koda ulaştığı anlamına gelir ki bu, bir saldırganın tam olarak istediği erişilebilirliktir. API güvenlik test kontrol listemiz, uyarlayabileceğiniz alan bazında bir başlangıç listesine sahiptir.
Tek bir kural bunu dürüst tutar: davranışa odaklanın, hata metnine değil. Mesajın "geçersiz yükleyici" olduğunu doğrularsanız, zararsız bir yeniden düzenleme testinizi bozar ve ekibe onu gevşetmeyi öğretir. Durum kodunu doğrulayın ve yapabildiğiniz yerde, hiçbir yan etkinin meydana gelmediğini doğrulayın.
Özel bir test gerektiren enjeksiyon sınıfları
Birkaç enjeksiyon ailesi o kadar sık ortaya çıkar ki, her biri bir kerelik manuel kontrol yerine sürekli test senaryolarını hak eder. Burada kapsamlı olmanıza gerek yok. Her sınıf için bir araştırma senaryosuna ihtiyacınız var, böylece bir regresyon yüksek sesle başarısız olur. Otomatik API güvenlik açığı tespiti yapan araçlar kapsamı daha sonra genişletebilir, ancak birkaç el yazması senaryo ilk önce bariz delikleri yakalar.
- SQL enjeksiyonu: Sorguya ulaşan herhangi bir alana `1); DROP TABLE datasets;--` gönderin. Uç nokta bunu değişmez bir değer olarak işlemeli ve 400 yanıtı vermeli veya boş bir sonuç döndürmeli ve asla bir veritabanı hatası ortaya çıkarmamalıdır.
- Şablon enjeksiyonu: Ad ve etiket alanlarına `{{ 7*7 }}` ve `{{ config.__class__ }}` gönderin. Yanıt asla `49` içeriyorsa, bir şablon motoru girdinizi değerlendirmiş demektir ve bu, gerçekleşmeyi bekleyen uzaktan koddur.
- Güvenli olmayan serileştirme ve uzaktan kod yükleyiciler: Düz bir değerin olması gereken yere `pickle://` yükleyicisi veya serileştirilmiş bir nesne gönderin. Bu, tam olarak Hugging Face şeklidir. Uç nokta, bilinmeyen yükleyicileri beyaz liste ile reddetmeli, yardımcı olmaya çalışmamalıdır.
- Komut enjeksiyonu: Bir dosya adı veya dönüştürme seçeneği gibi bir shell argümanı olabilecek herhangi bir alana `; id` ve `$(id)` gönderin. Bir kullanıcı kimliğini sızdıran bir 200, bir merak değil, kritik bir bulgudur.
Büyük boyutlu, hatalı biçimlendirilmiş ve içerik türü karmaşası
Her düşmanca girdi akıllı bir dize değildir. Bazıları basitçe çok büyük veya yanlış şekillendirilmiştir ve bunlar genellikle doğrulama mantığınız çalışmadan önce ayrıştırıcıları bozar.
Büyük boyutlu bir yük gönderin: tek bir karakterden oluşan beş megabaytlık bir alan veya bir milyon öğeli bir JSON dizisi. Sağlıklı bir API, bir gövde boyutu sınırını uygular ve bellek tükenene kadar tahsis etmek yerine 413 döndürür. Hatalı biçimlendirilmiş gövdeler de gönderin: kesik JSON, sondaki bir virgül veya yığın tükenmesini araştırmak için binlerce düzey derinliğinde iç içe geçmiş JSON. Doğru yanıt hızlı bir 400'dür, takılı kalmış bir çalışan değil.
İçerik türü karmaşası sessiz olanıdır. `Content-Type: application/json` bildirip XML gönderin veya `application/xml` bildirip XXE'yi araştırmak için harici bir varlık içeren bir yük gönderin. Tersi yönde yapın ve gevşek bir ayrıştırıcının yine de kabul edip etmediğini görmek için JSON'u `text/plain` olarak gönderin. Her uyumsuzluk, sunucunuzun başlığa mı, gövdeye mi güvendiğini veya ikisinin uyumlu olup olmadığını kontrol edip etmediğini test eder. Herhangi bir şeyi ayrıştırmadan önce uyumluluk gerektirmelidir.
Yapay zeka ajanları neden riskleri artırıyor
Yukarıdakilerin hepsi ajanlar var olmadan önce de geçerliydi. Ajanlar hacmi ve hızı değiştirir. İnsan bir saldırgan, her seferinde bir düşmanca istek yazar. Bir yapay zeka ajanı, yükleri makine hızıyla oluşturup iletir ve bir insanın asla denemeyi düşünmeyeceği girdileri memnuniyetle oluşturur.
Üç özellik bunu daha da kötüleştirir. Ajanlar girdi sentezler, bu nedenle hiçbir insanın yazmadığı ve hiçbir testin öngörmediği alan değerleri üretirler. Ajanlar yeniden dener ve çağrıları zincirler, böylece tek bir zehirli yukarı akış belgesi, uç noktanıza saniyeler içinde binlerce düşmanca isteğe dönüşebilir. Ve ajanlar güvenmeleri söylenen verileri iletir, bu da bir veri kümesinde veya bir webhook'ta gizlenmiş bir yükün API'nize gerçek bir isteğe dönüşme şeklidir. "Bu veriyi yükle"nin "bu kodu çalıştır"a dönüştüğü Hugging Face modeli, bir ajanın fark etmeden bir güven sınırını aşacağı talimat türüdür. API ekipleri için istem enjeksiyonu hakkındaki notumuz, bu aktarımda daha derine iner. Savunma değişmez; sadece otomatik olması gerekir, çünkü ajan trafiğini elle inceleyemezsiniz.
Negatif paketi oluşturun ve her değişiklikte CI'da çalıştırın
Yukarıdaki durumları, her çekme isteğinde çalışan bir pakete dönüştürün. İşte pytest'te bir hazırlık uç noktasına vuran ve kontrollü bir ret doğrulayan kompakt parametrelendirilmiş bir sürüm:
import httpx
import pytest
BASE = "https://staging.internal/v1"
HOSTILE_CONFIGS = [
{"loader": "pickle://s3/models/payload.pkl", "format": "auto"}, # uzaktan kod yükleyici
{"loader": "csv", "name": "{{ 7*7 }}"}, # şablon enjeksiyonu
{"loader": "csv", "name": "{{ config.__class__ }}"}, # nesne geçişi
{"loader": "csv", "filter": "1); DROP TABLE datasets;--"}, # SQL enjeksiyonu
{"loader": "csv", "name": "A" * 5_000_000}, # aşırı büyük alan
]
@pytest.mark.parametrize("config", HOSTILE_CONFIGS)
def test_dataset_config_is_refused(config):
r = httpx.post(f"{BASE}/datasets", json={"config": config}, timeout=10)
assert r.status_code in (400, 413, 422), r.text # hayır diyen bir sınır
assert r.status_code < 500, "5xx, yükün ulaşmaması gereken mantığa ulaştığı anlamına gelir"
assert "49" not in r.text, "şablon işlendi: sunucu tarafı şablon enjeksiyonu"
Birleştirmeleri engellemesi için bunu CI'a bağlayın. Minimal bir GitHub Actions işi bunu yapar:
name: api-abuse-tests
on: [push, pull_request]
jobs:
negative-input:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: pytest tests/negative_input.py -q
İşte şema öncelikli bir aracın yerini kazandığı yer. Apidog'da uç noktayı bir OpenAPI sözleşmesine göre tasarlarsınız, böylece test ederken her istek ve yanıt o sözleşmeye göre kontrol edilir. Negatif senaryoları başarılı yol senaryolarının hemen yanına kaydedebilirsiniz: büyük boyutlu alanlar, yanlış türler ve yukarıdaki enjeksiyon dizeleri, her biri durumun 4xx olduğunu doğrulayan bir iddia ile. Ardından aynı senaryoları Apidog CLI aracılığıyla CI'da çalıştırırsınız, böylece doğrulamayı sessizce gevşeten bir değişiklik gönderilmek yerine yapıyı bozar. Denemek isterseniz, Apidog'u indirin ve mevcut bir uç noktanıza bir negatif senaryo ekleyin.
Sınır konusunda net olun. Apidog bir tasarım, test, taklit ve dokümantasyon aracıdır. Bir web uygulama güvenlik duvarı çalıştırmaz, canlı trafiği filtrelemez veya bir SIEM'in yerini almaz ve test sırasında sözleşme doğrulama her açığı yakalamaz. İyi yaptığı şey, sözleşmeyi açık hale getirmek ve bir uç noktanın neyi kabul ettiği konusunda dürüst olmanızı sağlamaktır, böylece "hiç kontrol etmedik" kategorisi üretimde sizi şaşırtan bir şey olmaktan çıkar.
Sıkça sorulan sorular
Negatif test ile fuzzing arasındaki fark nedir? Negatif test, bilerek seçtiğiniz, önemsediğiniz her hata için bir adet, belirli kötü girdiler gönderir. Fuzzing, düşünmediğiniz durumları bulmak için büyük hacimlerde rastgele veya mutasyona uğramış girdiler gönderir. Hızlı, deterministik ve CI'da çalıştırması kolay oldukları için negatif testlerle başlayın. Kendi hayal gücünüzün ötesinde bir genişlik istediğinizde fuzzing ekleyin.
Bu testler üretim ortamında çalıştırılmalı mı? Hayır. Bunları hazırlık veya izole bir ortamda çalıştırın. Büyük boyutlu yük veya komut enjeksiyonu probu gibi bazı durumlar sistemi stres altına almak için tasarlanmıştır ve birkaç tanesi bir hata varsa verileri değiştirebilir. Özel bir test ortamı, testlerin gerçek kullanıcılara herhangi bir risk olmaksızın agresif olmasına olanak tanır.
Bir güvenlik duvarı veya WAF bunu zaten yakalamaz mı? Bir WAF, derinlemesine savunma için yararlıdır, ancak uygulamanın kötü girdiyi reddetmesinin yerini tutmaz. Kurallar atlatılabilir ve bir WAF iş mantığınızı bilemez. Bu testlerin amacı, uç noktanın kendisinin hayır dediğini kanıtlamaktır, böylece tam olarak kontrol etmediğiniz bir filtreye güvenmezsiniz.
Uç nokta başına kaç negatif senaryo yeterlidir? Her alan için, maruz kalabileceği her hata sınıfı için bir senaryo hedefleyin: yanlış tür, aralık dışı, çok uzun, yasak alan ve biçimine uyan herhangi bir enjeksiyon dizesi. Bu genellikle uç nokta başına yüzlerce değil, birkaç senaryodur. Sınıfların kapsanması, ham sayıdan daha önemlidir.
Şema doğrulama enjeksiyonu tamamen durdurur mu? Hayır, ve tek katmanınız olmamalıdır. Katı bir şema, hatalı biçimlendirilmiş ve büyük boyutlu girdilerin büyük bir kısmını ortadan kaldırır ve beklenmedik alanları engeller, ancak bir değer şema açısından geçerli olabilir ve yine de bir SQL veya şablon enjeksiyonu olabilir. Parametrelenmiş sorguları, güvenli serileştirmeyi ve çıktı kodlamayı yerinde tutun ve şemayı, bu katmanların savunması gereken yüzeyi daraltmak için kullanın.
