"Gerçek" TC kimlik doğrulaması nedir?

Türkiye Cumhuriyeti Kimlik Numarası'nın (TCKN) matematiksel algoritması (d[0]*7 - ...) bir numaranın formatının doğru olup olmadığını söyler. Ama "bu TC kimlik no gerçekten Mehmet Yılmaz'a mı ait?" sorusunu cevaplamaz. İşte bu soru için Nüfus ve Vatandaşlık İşleri Genel Müdürlüğü'nün (NVİ) sunduğu MERNİS tabanlı web servisi devreye girer.

Bu yazıda servisin tam endpoint adresi, method imzası, istek/yanıt formatı, rate limit davranışı ve yaygın hata senaryoları anlatılıyor. Birlikte kullanacağınız kod örnekleri için TCKN/VKN API entegrasyonu yazımıza bakın; burada protokol seviyesinde detaylara odaklanıyoruz.

Önemli uyarı: servis "açık" olsa da adil kullanım (fair use) kurallarına tâbidir; yoğun çağrı yapan client'lar geçici olarak engellenebilir. Üretim sistemlerinde kullanımdan önce NVİ ile sözleşme/başvuru süreçlerini doğrulamanız önerilir; özellikle kurumsal entegrasyonlar için KPS (Kimlik Paylaşımı Sistemi) başvurusu söz konusu olabilir.

Endpoint ve method

Servis SOAP 1.1/1.2 üzerinden çalışır. Ana URL:

https://tckimlik.nvi.gov.tr/Service/KPSPublic.asmx

WSDL:

https://tckimlik.nvi.gov.tr/Service/KPSPublic.asmx?WSDL

Servis "KPSPublic" adından da anlaşılacağı gibi Kimlik Paylaşımı Sistemi'nin kamuya açık kısmı. Farklı endpoint'ler (KPS2, Yabancı Kimlik doğrulama için YabanciKimlikNoDogrula) ayrı servislerde bulunur.

Ana method:

  • TCKimlikNoDogrula(TCKimlikNo: long, Ad: string, Soyad: string, DogumYili: int) -> bool

Dört parametre zorunlu. Sadece TC kimlik numarası göndererek doğrulama yapılamaz — servis yalnızca dörtlü eşleşme doğrular. Bu tasarım tercihi, salt numara leakage'ini önler (Lokal geliştirmelerde MERNİS'e istek atıp limite takılmak yerine sadece format kontrolü yapıp tc no üret araçlarından beslenebilirsiniz): biri TCKN'yi ele geçirse bile ad+soyad+yıl olmadan doğrulama yapamaz.

Yabancı uyruklu kimlik numaraları için YabanciKimlikNoDogrula method'u kullanılır; imzası benzer ama TCKN yerine 11 haneli yabancı kimlik numarası + kimlik seri no alır.

Parametreler: incelikli noktalar

TCKimlikNo

11 haneli sayı. SOAP şemasında long tipli tanımlı, ancak XML içinde string gibi görünür. Leading zero problemi yoktur çünkü ilk hane zaten 0 olamaz.

Ad ve Soyad

Büyük harfli Türkçe karakter seti ile gönderilmelidir. Nüfus kayıtlarındaki isimler büyük harfle saklanır. "mehmet" yerine "MEHMET", "çağla" yerine "ÇAĞLA" göndermelisiniz. Küçük harfli gönderirseniz servis çoğunlukla false döner.

Pratikte locale='tr-TR' ile uppercase dönüşümü kritik:

// JavaScript'te dikkat
"ısı".toUpperCase();         // "ISI" — yanlış (Türkçe dotless i)
"ısı".toLocaleUpperCase('tr'); // "ISI" — doğru değer ama
// "kitap".toLocaleUpperCase('tr') → "KİTAP" — noktalı büyük İ

Türkçe locale farkı I/İ karakterinde kendini gösterir. Python'da str.upper() Unicode-aware olsa da yine de explicit mapping gerekebilir.

XML karakter escape de şart: ad/soyadda &, <, >, ", ' bulunursa mutlaka &amp;, &lt; vb. olarak dönüştürün. Aksi takdirde SOAP envelope parse fail olur.

DogumYili

4 haneli yıl. Tam doğum tarihi (gün/ay/yıl) değil sadece yıl. "1987-05-12" yerine 1987 gönderilir. Bu, tasarımı gereği: servis tam tarih bilgisini paylaşmıyor, sadece yıl eşleşmesi kontrol ediyor.

Örnek SOAP istek ve yanıt

İstek (SOAP 1.1 envelope):

POST /Service/KPSPublic.asmx HTTP/1.1
Host: tckimlik.nvi.gov.tr
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://tckimlik.nvi.gov.tr/WS/TCKimlikNoDogrula"

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <soap:Body>
    <TCKimlikNoDogrula xmlns="http://tckimlik.nvi.gov.tr/WS">
      <TCKimlikNo>12345678901</TCKimlikNo>
      <Ad>MEHMET</Ad>
      <Soyad>YILMAZ</Soyad>
      <DogumYili>1987</DogumYili>
    </TCKimlikNoDogrula>
  </soap:Body>
</soap:Envelope>

Başarılı yanıt:

HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <soap:Body>
    <TCKimlikNoDogrulaResponse xmlns="http://tckimlik.nvi.gov.tr/WS">
      <TCKimlikNoDogrulaResult>true</TCKimlikNoDogrulaResult>
    </TCKimlikNoDogrulaResponse>
  </soap:Body>
</soap:Envelope>

Eşleşmeyen kayıtta <TCKimlikNoDogrulaResult>false</TCKimlikNoDogrulaResult> döner. HTTP status yine 200.

Hata durumu (geçersiz parametre, servis hatası):

<soap:Fault>
  <faultcode>soap:Server</faultcode>
  <faultstring>System.Web.Services.Protocols.SoapException: ...</faultstring>
</soap:Fault>

SOAP Fault'ta HTTP status genelde 500 olur. Bu durumları "upstream error" olarak sınıflandırın ve retry'a alın.

REST alternatifi var mı?

NVİ'nin resmi REST endpoint'i bu işlev için yoktur. SOAP tek yoldur. Ancak pek çok entegratör (Logo, Uyumsoft, Mikro, ISBA, özel SaaS'lar) SOAP'u REST/JSON'a sarmalayan wrapper sunar:

POST https://entegrator.example.com/v1/tckn/verify
Authorization: Bearer <token>
Content-Type: application/json

{"tckn":"12345678901","ad":"MEHMET","soyad":"YILMAZ","dogumYili":1987}

Bunlar NVİ'nin arkasına kendi rate limit, retry, audit log katmanlarını ekler. Üretim sisteminde kendi SOAP client'ınızı yazmak zor değil; ama bir entegratör kullanmak SLA, destek ve kurumsal başvuru süreçlerini basitleştirir. Maliyet genelde çağrı başına kuruş mertebesindedir, ancak sağlayıcıya göre değişir — doğrulayın.

Timeout ve rate limit

NVİ servisi resmi olarak bir SLA yayınlamıyor. Pratik gözlemler:

  • Normal yanıt süresi: 150–500ms
  • Yoğun saatlerde yanıt 2-5 saniyeye çıkabilir
  • Client timeout'u en az 5 saniye, ideal 10 saniye olsun
  • Saniyede onlarca çağrıyı aşan client'lar geçici engellenebilir; kesin eşik kamuya açık değil, doğrulayın

Üretimde şu pattern önerilir:

  • Connection pool reuse (yeni TLS handshake her çağrıda pahalı)
  • Token bucket rate limiting (örn. saniyede 20 çağrı)
  • Circuit breaker (hata oranı %20'yi aşınca 60 saniye çağrı kapat)
  • Fallback: sadece format validation'a dön, NVİ bilgisi yok notu ekle

Detaylı retry/circuit breaker implementasyonları için API entegrasyonu yazımıza bakın.

Yabancı Kimlik Numarası doğrulama

Türkiye'de oturma izinli yabancılara verilen Yabancı Kimlik Numarası 99 ile başlayan 11 haneli sayılardır (TCKN algoritmasıyla aynı formata uyar). Servisi:

https://tckimlik.nvi.gov.tr/Service/KPSPublic.asmx

Method: YabanciKimlikNoDogrula. Parametreleri: YabanciKimlikNo, Ad, Soyad, DogumGun, DogumAy, DogumYil, KimlikSeriNo. TCKimlikNoDogrula'dan farklı olarak tam doğum tarihi ve pasaport/kimlik seri numarası ister.

Uygulamanız hem vatandaş hem yabancı kimlik kabul edecekse, ilk rakamlara bakarak doğru method'a yönlendirme yapabilirsiniz (99 ile başlıyorsa Yabancı). Ancak algoritma bakımından iki numara da aynı checksum kurallarına tâbi; format katmanı ortak kalır.

SOAP ve WS-Security

NVİ servisi bugünkü kamu arayüzünde WS-Security (wsse) istemez. Sadece basit XML envelope yeterli. Bu, public KPS endpoint'i için geçerli; kurumsal KPS başvurusu yaptığınızda (bankacılık, telekom) kimlik doğrulamalı ayrı endpoint'ler verilir ve oralarda sertifika/imza şartı olabilir.

Kamu endpoint'i üzerinden yapılan çağrılar HTTPS ile şifrelendiği için transport güvenliği sağlanır ama mesaj imzası yoktur. Çağrıyı kendi kaynak IP'nizden yapıyorsanız ayrı bir auth gerekmez.

Hata mesajları ve debugging

En sık görülen problemler:

  • Ad/soyad upper-case değil: false döner, neden belirsiz.
  • DogumYili yanlış: yine false. Nüfus kaydındaki yıl ile eşleşmiyor.
  • TC kimlik formata uyuyor ama gerçek değil: tombala gibi random üretilmiş bir TCKN, format geçer ama servis false der.
  • Türkçe karakter encoding: "Ğ" yerine "G" gönderirseniz fail. Tam karakter eşleşmesi şart.
  • SOAPAction header eksik: HTTP 500. Pek çok HTTP client'ta opsiyonel görünür ama NVİ zorunlu tutar.

Debug için Wireshark veya curl -v ile istek/yanıtı görüntüleyin. Request body'yi aynen kopyalayıp Postman'e yapıştırarak da test edebilirsiniz.

Cache etmek güvenli mi?

Kısa süreli cache (5-30 dakika) genelde güvenlidir. Bir kişinin TC kimlik numarası, adı, soyadı, doğum yılı birlikte bu kadar kısa sürede değişmez. Ancak:

  • Cache key olarak düz TCKN+ad+soyad+yıl tutmayın; hash'leyin (KVKK).
  • Cache TTL'i 24 saati aşmasın (evlilik/boşanma gibi isim değişiklikleri olur).
  • Cache store da encrypted olmalı (Redis ACL, at-rest encryption).

Cache stratejisi, NVİ'ye gelen yükü %50-80 azaltabilir, kullanıcı kaydı akışlarındaki "geri dön, düzelt, tekrar submit et" trafiği için özellikle etkilidir.

Format vs. kimlik: hangisi ne zaman?

İş akışınızın her adımında sorun: "Buraya format mı lazım kimlik mi?"

  • Kullanıcı form submit → önce format (anında, yerel)
  • Sigorta teklifi oluşturma → sadece format (henüz sözleşme yok)
  • Üyelik açma, fatura kesme → kimlik (NVİ çağrısı)
  • Para transferi, yüksek risk → kimlik + ek seviyeler (SMS OTP, video KYC)

Her adımda en ucuz katmandan başlayıp gerektikçe derinleşmek hem UX hem maliyet olarak en doğrusudur. Format validation katmanını atlayanlar, NVİ'ye gereksiz yere trafik gönderir; NVİ'yi atlayanlar ise gerçek olmayan kimliklerle sistem doldurur.

KVKK notları

NVİ servisine çağrı yapmak da bir kişisel veri işleme faaliyetidir. Bu sebeple:

  • Çağrı yaptığınız TC kimlik numarasını log'da maskeleyin
  • Yanıtı kısa süreli cache'leyin (yukarıda anlattık)
  • Aydınlatma metninde "NVİ MERNİS servisi ile doğrulama yapılır" maddesi olmalı
  • Hukuki sebep genelde "sözleşmenin kurulması"

Detaylı rehber: KVKK uyumlu kimlik doğrulama.

Sonuç

NVİ MERNİS servisi, Türkiye'de "gerçek TC kimlik doğrulaması" yapabilmenin resmi ve ücretsiz kanalıdır. SOAP 1.1 ile çalışır, TCKimlikNoDogrula method'u dörtlü eşleşmeyi true/false ile döndürür. Production kullanımında rate limit, timeout, retry ve cache stratejileri şarttır; kötü yönetilen bir integration hem performans hem güvenilirlik problemi yaratır.

Anlık deneme için TC Doğrulayıcı ve toplu akışlar için Toplu Doğrulama.