REST API Kimlik Doğrulama Yöntemleri: API Key, Basic Auth ve Bearer Token

Bir REST API’yi dışarıya açtığınız anda, güvenlik meselesi anında birinci öncelik haline gelir. Kim erişebilir, kim erişemez, hangi istek meşru hangi istek şüpheli? Bütün bu soruların cevabı kimlik doğrulama mekanizmasında yatar. Sysadmin olarak siz de hem kendi geliştirdiğiniz servisleri korumanız hem de üçüncü parti API’lerle entegrasyon yaparken doğru yöntemi seçmeniz gerekiyor. API Key, Basic Auth ve Bearer Token, REST dünyasının en yaygın üç kimlik doğrulama yöntemi. Her birinin kendine has güçlü ve zayıf yanları var, kullanım senaryoları birbirinden farklı. Hadi bunları gerçek dünya örnekleriyle, elle tutulur bir şekilde inceleyelim.

Kimlik Doğrulama ile Yetkilendirme Arasındaki Fark

Konuya girmeden önce bir kavram karmaşasını giderelim. Authentication (kimlik doğrulama) “sen kimsin?” sorusunu yanıtlar. Authorization (yetkilendirme) ise “ne yapabilirsin?” sorusunu. API güvenliğinde çoğunlukla ikisi iç içe geçer ama mekanizmalar farklıdır. Bir API anahtarı size erişim sağlar (authentication), ancak o anahtarın hangi endpoint’lere erişip erişemeyeceği yetkilendirme katmanında belirlenir.

Bu yazıda ağırlıklı olarak authentication tarafına odaklanacağız.

API Key Yöntemi

API Key, en basit ve en yaygın kimlik doğrulama yöntemidir. Sunucu tarafında üretilen benzersiz bir string, istemciye verilir ve her istekte bu anahtar gönderilir. Konsept son derece sade: anahtarın var mı, varsa girebilirsin.

API Key Nasıl Gönderilir?

API anahtarı üç farklı yerde taşınabilir: HTTP header’ında, query string’de veya request body’sinde. Header üzerinden göndermek en güvenli yaklaşımdır.

Header üzerinden gönderim:

curl -X GET "https://api.ornekservis.com/v1/users" 
  -H "X-API-Key: sk_live_abc123xyz789"

Query string üzerinden gönderim (önerilmez ama yaygındır):

curl -X GET "https://api.ornekservis.com/v1/users?api_key=sk_live_abc123xyz789"

Query string yönteminin ciddi bir dezavantajı var: URL’ler sunucu log’larına, tarayıcı geçmişine ve proxy kayıtlarına düşer. API anahtarınız bu şekilde istemeden ifşa olabilir. Mümkün olduğunca header yöntemini kullanın.

Nginx ile API Key Doğrulama

Diyelim ki bir backend servisiniz var ve önüne Nginx koyarak API key kontrolü yapmak istiyorsunuz. Basit ama etkili bir yöntem:

# /etc/nginx/conf.d/api_gateway.conf

server {
    listen 443 ssl;
    server_name api.ornekservis.com;

    location /v1/ {
        # API key kontrolü
        if ($http_x_api_key != "sk_live_abc123xyz789") {
            return 401 '{"error": "Gecersiz API anahtari"}';
            add_header Content-Type application/json;
        }

        proxy_pass http://localhost:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Tabii gerçek dünyada birden fazla anahtarı yönetmeniz gerekir. Bunun için Nginx’in map modülünü kullanabilirsiniz:

# /etc/nginx/nginx.conf içine http bloğuna ekleyin

map $http_x_api_key $api_client {
    default         "";
    "sk_live_abc123" "musteri_a";
    "sk_live_def456" "musteri_b";
    "sk_live_ghi789" "musteri_c";
}

server {
    listen 443 ssl;
    server_name api.ornekservis.com;

    location /v1/ {
        if ($api_client = "") {
            return 401 '{"error": "Yetkisiz erisim"}';
        }

        # Hangi müşterinin istek yaptığını backend'e ilet
        proxy_set_header X-API-Client $api_client;
        proxy_pass http://localhost:8080;
    }
}

Bu yapıyla hem doğrulama yapıyor hem de hangi müşterinin istek attığını backend’e iletiyorsunuz. Çok güzel bir pattern.

API Key Ne Zaman Kullanılır?

  • Makine-makine iletişiminde (M2M)
  • Dahili mikroservis haberleşmesinde
  • Monitoring ve webhook entegrasyonlarında
  • Basit public API’lerde kullanıcı oturumu gerektirmeyen durumlarda

API Key’in en büyük zayıflığı: anahtar ele geçirilirse doğrudan erişim sağlanır, ek bir koruma katmanı yoktur. Bu yüzden anahtarları düzenli rotate etmek, her istemciye ayrı anahtar vermek ve HTTPS olmadan asla kullanmamak şarttır.

Basic Authentication

Basic Auth, HTTP protokolünün en eski kimlik doğrulama mekanizmasıdır. RFC 7617 ile standartlaştırılmıştır. Kullanıcı adı ve şifre birleştirilip Base64 ile encode edilerek Authorization header’ına eklenir.

Format şu şekildedir: kullaniciadi:sifre birleşimi Base64 encode edilir ve Basic olarak gönderilir.

# Manuel olarak Base64 encode
echo -n "admin:super_gizli_sifre" | base64
# Çıktı: YWRtaW46c3VwZXJfZ2l6bGlfc2lmcmU=

# curl ile Basic Auth kullanımı
curl -X GET "https://api.ornekservis.com/v1/dashboard" 
  -H "Authorization: Basic YWRtaW46c3VwZXJfZ2l6bGlfc2lmcmU="

# Ya da curl'ün -u parametresiyle (daha pratik)
curl -X GET "https://api.ornekservis.com/v1/dashboard" 
  -u "admin:super_gizli_sifre"

Basic Auth’un Kritik Güvenlik Notu

Base64 encoding bir şifreleme değil, sadece bir kodlama yöntemidir. YWRtaW46c3VwZXJfZ2l6bGlfc2lmcmU= değerini alıp base64 -d ile decode ederseniz düz metin olarak admin:super_gizli_sifre elde edersiniz. Bu yüzden Basic Auth kesinlikle HTTPS olmadan kullanılmamalıdır.

# Decode örneği - ne kadar basit olduğunu görmek için
echo "YWRtaW46c3VwZXJfZ2l6bGlfc2lmcmU=" | base64 -d
# Çıktı: admin:super_gizli_sifre

Apache ile Basic Auth Konfigürasyonu

Apache üzerinde bir API endpoint’ini Basic Auth ile korumak:

# Önce htpasswd dosyası oluşturun
htpasswd -c /etc/apache2/.api_credentials api_kullanici
# Şifre soracak, girin

# Mevcut bir dosyaya kullanıcı eklemek için (-c olmadan)
htpasswd /etc/apache2/.api_credentials ikinci_kullanici

# Apache VirtualHost konfigürasyonu
# /etc/apache2/sites-available/api.conf

<VirtualHost *:443>
    ServerName api.ornekservis.com

    <Location /v1/>
        AuthType Basic
        AuthName "API Erisimi - Yetkili Kullanicilara Ozel"
        AuthUserFile /etc/apache2/.api_credentials
        Require valid-user
    </Location>

    ProxyPass /v1/ http://localhost:8080/v1/
    ProxyPassReverse /v1/ http://localhost:8080/v1/
</VirtualHost>

Basic Auth Ne Zaman Kullanılır?

  • Dahili araçlarda ve admin panellerinde
  • CI/CD pipeline’larında (Jenkins, GitLab gibi araçların API erişiminde)
  • Geçici ve test ortamlarında
  • Basit script entegrasyonlarında

Gerçek dünya senaryosunda Basic Auth’u en çok Jenkins API erişiminde, Elasticsearch veya RabbitMQ management API’lerinde görürsünüz. Production’da son kullanıcıya açık bir API için Basic Auth kullanmak artık neredeyse anti-pattern sayılır.

Bearer Token (JWT) Yöntemi

Bearer Token, modern web API’lerinin standart kimlik doğrulama yöntemi haline gelmiştir. RFC 6750 ile tanımlanmıştır ve OAuth 2.0 framework’ünün bir parçasıdır. Adından da anlaşılacağı üzere “taşıyan” (bearer) token’ı sunar, sunucu o token’ı doğrular.

Bearer Token’ın en yaygın implementasyonu JWT (JSON Web Token)‘dır. JWT üç bölümden oluşur ve nokta ile ayrılır:

  • Header: Token tipi ve kullanılan algoritma
  • Payload: Claim’ler, yani token içindeki veriler (kullanıcı id, roller, expire süresi)
  • Signature: Token’ın doğruluğunu kanıtlayan imza

Bearer Token ile İstek Yapmak

# Bearer token ile GET isteği
curl -X GET "https://api.ornekservis.com/v1/profile" 
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiIxMjM0NSIsInJvbGUiOiJhZG1pbiIsImV4cCI6MTcwMDAwMDAwMH0.dummysignature"

# Token alıp kullanan tam bir senaryo (login endpoint'i varsa)
TOKEN=$(curl -s -X POST "https://api.ornekservis.com/auth/login" 
  -H "Content-Type: application/json" 
  -d '{"username": "admin", "password": "sifre123"}' 
  | grep -o '"token":"[^"]*"' | cut -d'"' -f4)

echo "Alinan token: $TOKEN"

# Alınan token ile korunan endpoint'e erişim
curl -X GET "https://api.ornekservis.com/v1/dashboard" 
  -H "Authorization: Bearer $TOKEN"

JWT Token Analizi

JWT token’larını debug etmek için jq ve base64 kombinasyonu işe yarar:

#!/bin/bash
# jwt_decode.sh - JWT token'ı decode eden basit script

TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiIxMjM0NSIsInJvbGUiOiJhZG1pbiIsImV4cCI6MTcwMDAwMDAwMH0.dummysignature"

# Header'ı decode et (ilk parça)
HEADER=$(echo $TOKEN | cut -d'.' -f1)
echo "=== JWT Header ==="
echo $HEADER | base64 -d 2>/dev/null | jq .

# Payload'ı decode et (ikinci parça)
PAYLOAD=$(echo $TOKEN | cut -d'.' -f2)
echo "=== JWT Payload ==="
# Base64 padding sorununu aşmak için
echo $PAYLOAD | awk '{
    n = length($0) % 4
    if (n == 2) print $0 "=="
    else if (n == 3) print $0 "="
    else print $0
}' | base64 -d 2>/dev/null | jq .

# Token expiry kontrolü
EXP=$(echo $PAYLOAD | awk '{
    n = length($0) % 4
    if (n == 2) print $0 "=="
    else if (n == 3) print $0 "="
    else print $0
}' | base64 -d 2>/dev/null | jq -r '.exp')

NOW=$(date +%s)
if [ "$EXP" -gt "$NOW" ]; then
    echo "Token gecerli. Son kullanma: $(date -d @$EXP)"
else
    echo "UYARI: Token suresi dolmus!"
fi

Nginx ile JWT Doğrulama

Nginx’in ticari versiyonu JWT doğrulamayı native destekler, ancak açık kaynak Nginx’te lua-nginx-module veya ngx_http_auth_request_module kullanılabilir. Basit bir auth_request senaryosu:

# /etc/nginx/conf.d/api_jwt.conf

upstream auth_service {
    server localhost:3001;  # Token doğrulayan servis
}

upstream api_backend {
    server localhost:8080;
}

server {
    listen 443 ssl;
    server_name api.ornekservis.com;

    location = /auth/verify {
        internal;
        proxy_pass http://auth_service/verify;
        proxy_pass_request_body off;
        proxy_set_header Content-Length "";
        proxy_set_header X-Original-URI $request_uri;
        proxy_set_header Authorization $http_authorization;
    }

    location /v1/ {
        auth_request /auth/verify;
        auth_request_set $auth_user_id $upstream_http_x_user_id;

        proxy_set_header X-User-ID $auth_user_id;
        proxy_pass http://api_backend;
    }

    # 401 hatalarını özelleştir
    error_page 401 = @unauthorized;
    location @unauthorized {
        default_type application/json;
        return 401 '{"error": "Gecersiz veya suresi dolmus token"}';
    }
}

Token Refresh Mekanizması

JWT’nin önemli bir özelliği kısa ömürlü olmasıdır. Access token 15-60 dakika, refresh token ise günler veya haftalar geçerli olabilir. Bunu script’te yönetmek:

#!/bin/bash
# api_client.sh - Token refresh destekli API istemcisi

ACCESS_TOKEN_FILE="/tmp/api_access_token"
REFRESH_TOKEN_FILE="/tmp/api_refresh_token"
API_BASE="https://api.ornekservis.com"

get_new_token() {
    echo "Yeni token aliniyor..."
    RESPONSE=$(curl -s -X POST "$API_BASE/auth/login" 
        -H "Content-Type: application/json" 
        -d '{"username": "api_user", "password": "gizli_sifre"}')

    echo $RESPONSE | jq -r '.access_token' > $ACCESS_TOKEN_FILE
    echo $RESPONSE | jq -r '.refresh_token' > $REFRESH_TOKEN_FILE
    echo "Token basariyla alindi."
}

refresh_token() {
    REFRESH_TOKEN=$(cat $REFRESH_TOKEN_FILE 2>/dev/null)
    if [ -z "$REFRESH_TOKEN" ]; then
        get_new_token
        return
    fi

    RESPONSE=$(curl -s -X POST "$API_BASE/auth/refresh" 
        -H "Content-Type: application/json" 
        -d "{"refresh_token": "$REFRESH_TOKEN"}")

    NEW_TOKEN=$(echo $RESPONSE | jq -r '.access_token')
    if [ "$NEW_TOKEN" != "null" ] && [ -n "$NEW_TOKEN" ]; then
        echo $NEW_TOKEN > $ACCESS_TOKEN_FILE
        echo "Token yenilendi."
    else
        echo "Refresh basarisiz, yeniden login yapiliyor..."
        get_new_token
    fi
}

api_request() {
    local METHOD=$1
    local ENDPOINT=$2
    local DATA=$3

    ACCESS_TOKEN=$(cat $ACCESS_TOKEN_FILE 2>/dev/null)
    if [ -z "$ACCESS_TOKEN" ]; then
        get_new_token
        ACCESS_TOKEN=$(cat $ACCESS_TOKEN_FILE)
    fi

    RESPONSE=$(curl -s -w "n%{http_code}" -X $METHOD "$API_BASE$ENDPOINT" 
        -H "Authorization: Bearer $ACCESS_TOKEN" 
        -H "Content-Type: application/json" 
        ${DATA:+-d "$DATA"})

    HTTP_CODE=$(echo "$RESPONSE" | tail -1)
    BODY=$(echo "$RESPONSE" | head -n -1)

    if [ "$HTTP_CODE" = "401" ]; then
        echo "Token gecersiz, yenileniyor..."
        refresh_token
        ACCESS_TOKEN=$(cat $ACCESS_TOKEN_FILE)

        BODY=$(curl -s -X $METHOD "$API_BASE$ENDPOINT" 
            -H "Authorization: Bearer $ACCESS_TOKEN" 
            -H "Content-Type: application/json" 
            ${DATA:+-d "$DATA"})
    fi

    echo $BODY
}

# Kullanım örnekleri
api_request GET "/v1/users"
api_request POST "/v1/orders" '{"product_id": "123", "quantity": 2}'

Üç Yöntemin Karşılaştırması

API Key:

  • Kurulum kolaylığı: Çok basit, hızlıca hayata geçirilir
  • Güvenlik seviyesi: Orta, anahtar ele geçirilirse doğrudan erişim
  • Durumsuzluk: Evet, sunucu tarafında oturum bilgisi saklanmaz
  • Expire mekanizması: Genellikle yoktur, manuel rotate gerekir
  • En iyi kullanım senaryosu: M2M iletişim, dahili servisler, webhook’lar

Basic Auth:

  • Kurulum kolaylığı: Çok basit, HTTP standart desteği
  • Güvenlik seviyesi: Düşük-orta, şifre her istekte gönderilir
  • Durumsuzluk: Evet
  • Expire mekanizması: Yoktur
  • En iyi kullanım senaryosu: Dahili araçlar, CI/CD, legacy sistemler

Bearer Token / JWT:

  • Kurulum kolaylığı: Orta-karmaşık, auth sunucusu gerekebilir
  • Güvenlik seviyesi: Yüksek, kısa ömürlü token’lar ile güçlü kontrol
  • Durumsuzluk: Evet (JWT stateless’tır)
  • Expire mekanizması: Yerleşik, refresh mekanizması ile yönetilebilir
  • En iyi kullanım senaryosu: Son kullanıcı uygulamaları, modern API’ler, mikroservisler

Güvenlik En İyi Pratikleri

Hangi yöntemi kullanırsanız kullanın, şu kurallar değişmez:

HTTPS zorunluluğu:

# curl ile SSL sertifikası doğrulama (asla -k kullanmayın production'da)
curl -X GET "https://api.ornekservis.com/v1/data" 
  -H "Authorization: Bearer $TOKEN" 
  --cacert /etc/ssl/certs/ca-certificates.crt

# SSL/TLS versiyonunu belirtmek
curl --tlsv1.2 -X GET "https://api.ornekservis.com/v1/data" 
  -H "Authorization: Bearer $TOKEN"

Rate limiting ile brute force koruması:

  • Her IP için dakikada maksimum istek sayısı belirleyin
  • 401 hatalarını özellikle izleyin, anormal artışlar saldırı işareti olabilir
  • API key’leri environment variable’da saklayın, kod içine gömmeyin
  • Log’larda authorization header’larını maskelleyin

Credential yönetimi için environment variable kullanımı:

# Yanlış: Credential'ı direkt koda yazmak
curl -u "admin:sifre123" https://api.ornekservis.com/v1/data

# Doğru: Environment variable kullanmak
export API_USER="admin"
export API_PASS="sifre123"
curl -u "$API_USER:$API_PASS" https://api.ornekservis.com/v1/data

# Vault veya benzeri bir secret manager'dan almak (HashiCorp Vault örneği)
export API_TOKEN=$(vault kv get -field=token secret/api/production)
curl -H "Authorization: Bearer $API_TOKEN" https://api.ornekservis.com/v1/data

Gerçek Dünya Senaryo Örneği

Diyelim ki bir e-ticaret platformunuz var. Farklı bileşenler farklı auth yöntemleri kullanıyor:

  • Warehouse sistemi ile stok senkronizasyonu: API Key, çünkü iki sistem arası M2M iletişim, kullanıcı kavramı yok
  • Admin paneli: Basic Auth veya Bearer Token, dahili kullanım ve kurumsal LDAP entegrasyonu
  • Mobil uygulama ve web frontend: Bearer Token / JWT, kullanıcı oturumları, token refresh, çoklu cihaz desteği
  • Ödeme gateway webhook’ları: API Key + IP whitelist kombinasyonu, webhook kaynaklarını kısıtlamak için

Her senaryo için en uygun yöntemi seçmek, karmaşıklık ile güvenlik arasındaki dengeyi iyi kurmayı gerektiriyor.

Sonuç

REST API kimlik doğrulaması, tek bir doğru cevabı olmayan bir alan. API Key sadeliği ve hızı ile M2M senaryolarda ideal iken, Basic Auth legacy uyumluluğu gerektiren veya çok basit dahili araçlar için yeterli. Bearer Token ise modern, kullanıcı odaklı ve güvenlik gereksinimlerinin yüksek olduğu senaryoların tercihi.

Sysadmin perspektifinden baktığımda en kritik nokta şu: hangi yöntemi seçerseniz seçin, HTTPS olmadan hiçbirini kullanmayın, credential’larınızı kod içine gömmeyin ve log’larınızda hassas bilgilerin maskelenmesini sağlayın. API güvenliği, deployment sonrası düşünülecek bir şey değil, mimari kararın ilk gününden itibaren planlanması gereken bir unsurdur. Bir güvenlik açığını sonradan yamanın maliyeti, baştan doğru yapmaktan çok daha ağırdır.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir