Neler yeni

Foruma hoş geldin 👋, Ziyaretçi

Forum içeriğine ve tüm hizmetlerimize erişim sağlamak için foruma kayıt olmalı ya da giriş yapmalısınız. Foruma üye olmak tamamen ücretsizdir.

  • Merhaba Değerli Ziyaretçimiz, ForumaGel ailesi seni bekliyor! 🌟 Aramıza katılarak güçlü ve samimi topluluğumuzun bir parçası olabilirsin. Burada her üye değerli, her katkı kıymetli. Şimdi üye ol, bizimle birlikte gelişmenin ve keyifli sohbetlerin tadını çıkar! Sevgi ve Saygılarla, ForumaGel Yönetimi ❤️
Yan Yana Banner
Yan Yana Banner
Katılım
7 Nis 2025
Konular
367
Mesajlar
780
Çözümler
1
Tepkime puanı
121
Puan
93
Konum
İstanbul
Web sitesi
forumagel.com
Hazır Kod Bankası - Konu 24: Web API Güvenliği Temelleri (Authentication ve Authorization)




Hazır Kod Bankası Serimizin Yirmi Dördüncü Konusu: Web API Güvenliği Temelleri!

Merhaba arkadaşlar! Flask ile veritabanı destekli bir REST API oluşturduk (Konu 23). Ürün ekleyebiliyor, listeleyebiliyor, güncelleyebiliyor ve silebiliyoruz. Ancak bu API şu an herkesin erişimine açık durumda. Bu, hassas verilere sahip uygulamalar için kabul edilemez bir durumdur. Uygulamalarımızda, sadece kimliği doğrulanmış kullanıcıların belirli verilere erişmesi veya belirli işlemleri yapmasına izin verilmesi gerekir. İşte burada API Güvenliği devreye girer.

API güvenliğinin en temel iki kavramı şunlardır:
  • Kimlik Doğrulama (Authentication - AuthN): İsteği yapanın kim olduğunu doğrulama sürecidir. Kullanıcının iddia ettiği kişi olup olmadığını kontrol eder. (Örn: Kullanıcı adı ve şifre kontrolü, API anahtarı doğrulama, token kontrolü). "Sen kimsin?" sorusuna yanıt arar.
  • Yetkilendirme (Authorization - AuthZ): Kimliği doğrulanmış bir kullanıcının (veya sistemin) belirli bir kaynağa erişme veya belirli bir işlemi yapma izninin olup olmadığını belirleme sürecidir. "Bu işlemi yapmaya iznin var mı?" sorusuna yanıt arar. Yetkilendirme, Kimlik Doğrulama başarılı olduktan sonra yapılır.
Bu konuda bu iki kavram arasındaki farkı öğrenecek ve Python Flask API'mizde en basit şekilleriyle nasıl uygulayabileceğimize dair örnekler göreceğiz.

Ön Gereksinim: Flask API (Konu 22) ve Flask-SQLAlchemy ile veritabanı entegrasyonu (Konu 23) konuları anlaşılmış olmalı. Konu 23'teki `User` veya `Product` gibi bir modeliniz ve veritabanı kurulumunuz olmalı. API test araçları (Postman/Insomnia/curl) hazır olmalı.

2. Kimlik Doğrulama (Authentication - AuthN) Temelleri[/B]

Kimlik doğrulama, API'ye istek yapan kişinin iddia ettiği kişi olup olmadığını kontrol etmektir. Birçok yöntemi vardır: API Anahtarları (API Keys), Kullanıcı Adı/Şifre, Token Tabanlı Yöntemler (JWT, OAuth vb.). Basit bir örnek olarak API Anahtarı kontrolünü ele alalım.

Konu 23'teki `User` modelimize basit bir `api_key` alanı ekleyelim.

Python:
# models.py (veya app.py içindeki model tanımınız)
# from app import db # Eğer ayrı dosyadaysa

# class User(db.Model):
#     id = db.Column(db.Integer, primary_key=True)
#     username = db.Column(db.String(20), unique=True, nullable=False)
#     email = db.Column(db.String(120), unique=True, nullable=False)
    # Yeni eklenecek alan:
#     api_key = db.Column(db.String(50), unique=True, nullable=True) # API Anahtarı


# Tablonuzu güncelledikten sonra db.create_all()'ı tekrar çalıştırarak (veya migration yaparak)
# bu alanı veritabanına eklemelisiniz.

# Test için bir kullanıcıya manuel olarak API anahtarı ekleyebilirsiniz
# with app.app_context(): # app objesi tanımlı olmalı
#     test_user = User.query.filter_by(username='TestUser').first()
#     if not test_user:
#         test_user = User(username='TestUser', email='[email protected]', api_key='cokgizlisifre123')
#         db.session.add(test_user)
#         db.session.commit()
#         print("TestUser eklendi/güncellendi API key ile.")
Şimdi bu API anahtarını kontrol eden bir mekanizma oluşturalım. API Anahtarı genellikle HTTP başlıklarında (`Header`) gönderilir (örn: `X-API-Key: cokgizlisifre123`).

Python:
from flask import Flask, jsonify, request
from flask_sqlalchemy import SQLAlchemy
import os

# ... (app ve db objelerinin tanımlanması, model tanımı - User sınıfı api_key ile) ...

# Basit API Key Doğrulama Fonksiyonu
def authenticate_by_api_key():
    # İstek başlıklarından 'X-API-Key' değerini al
    api_key = request.headers.get('X-API-Key')

    # Eğer başlıkta API anahtarı yoksa veya boşsa
    if not api_key:
        return None # Kimlik doğrulama başarısız

    # Veritabanında bu API anahtarına sahip bir kullanıcı ara
    # app objesi uygulama bağlamında olmalı ki db'ye erişebilsin
    # Bunu ya fonksiyona 'app'i parametre olarak göndererek ya da bağlam içinde çağırarak yaparsınız
    # Basitlik için bu örnekte db objesinin global olarak erişilebilir olduğunu varsayıyoruz
    user = User.query.filter_by(api_key=api_key).first()

    return user # Kullanıcı bulunursa User objesini, bulunamazsa None döner

# Kimlik Doğrulama gerektiren bir Route örneği
@app.route('/secure_data', methods=['GET'])
def get_secure_data():
    # Kimlik doğrulama fonksiyonunu çağır
    authenticated_user = authenticate_by_api_key()

    # Eğer kullanıcı kimliği doğrulanamazsa
    if not authenticated_user:
        # 401 Unauthorized (Yetkisiz) hatası döndür
        # Kullanıcı kimliğini kanıtlamalıdır.
        return jsonify({'message': 'Kimlik Doğrulama Başarısız. API Anahtarı gerekli.'}), 401

    # Kimlik doğrulama başarılıysa, kullanıcıya özel veya gizli veriyi döndür
    return jsonify({
        'message': 'Gizli Veri!',
        'data': 'Sadece yetkili kullanıcılar görebilir.',
        'user': authenticated_user.username
    })

# ... (Diğer route'larınız ve app.run() kısmı) ...
if __name__ == '__main__':
    # ... db.create_all() ve test_user ekleme kodları ...
    app.run(debug=True)
Açıklama: `authenticate_by_api_key()` fonksiyonu gelen isteğin başlığından `X-API-Key`'i alır ve veritabanında böyle bir kullanıcı arar. Varsa kullanıcı objesini, yoksa `None` döner. `/secure_data` route'u, bu fonksiyonu çağırır. Fonksiyon `None` dönerse, isteği `401 Unauthorized` status kodu ile reddederiz. Bu, kullanıcıya "Kim olduğunu kanıtla!" mesajı verir. Bu sadece temel bir örnektir, gerçek sistemlerde API anahtarları daha güvenli saklanmalı, HTTPS kullanılmalı ve şifreler hashlenmelidir.

3. Yetkilendirme (Authorization - AuthZ) Temelleri[/B]

Yetkilendirme, kimliği doğrulanmış kullanıcının belirli bir kaynağa erişme veya belirli bir işlemi yapma izninin olup olmadığını kontrol etmektir. Genellikle kullanıcılara roller (admin, moderatör, normal kullanıcı) veya özel izinler atanır.

Konu 23'teki `User` modeline basit bir `role` alanı ekleyelim.

Python:
# models.py (veya app.py içindeki model tanımınız)
# ... (id, username, email, api_key alanları) ...
# Yeni eklenecek alan:
# class User(db.Model):
#     ...
#     role = db.Column(db.String(20), nullable=False, default='user') # Kullanıcı rolü ('user', 'admin' vb.)

# Test kullanıcılarına rol atayabilirsiniz
# with app.app_context():
#    test_user = User.query.filter_by(username='TestUser').first()
#    if test_user:
#        test_user.role = 'admin' # Test kullanıcısına admin rolü ver
#        db.session.commit()
#    normal_user = User.query.filter_by(username='NormalUser').first()
#    if not normal_user:
#         normal_user = User(username='NormalUser', email='[email protected]', api_key='normalsifre456', role='user')
#         db.session.add(normal_user)
#         db.session.commit()
Şimdi hem kimlik doğrulama gerektiren hem de sadece 'admin' rolüne sahip kullanıcıların erişebileceği bir route oluşturalım.

Python:
from flask import Flask, jsonify, request
from flask_sqlalchemy import SQLAlchemy
import os

# ... (app, db, User model - api_key ve role alanları ile) ...

# Kimlik Doğrulama Fonksiyonu (Yukarıdaki ile aynı)
def authenticate_by_api_key():
    api_key = request.headers.get('X-API-Key')
    if not api_key:
        return None
    user = User.query.filter_by(api_key=api_key).first()
    return user

# Hem Kimlik Doğrulama hem Yetkilendirme gerektiren Route
@app.route('/admin_panel', methods=['GET'])
def get_admin_panel_data():
    # ADIM 1: Kimlik Doğrulama
    authenticated_user = authenticate_by_api_key()

    if not authenticated_user:
        return jsonify({'message': 'Kimlik Doğrulama Başarısız. API Anahtarı gerekli.'}), 401 # 401 Yetkisiz

    # ADIM 2: Yetkilendirme (Kullanıcının admin rolü var mı?)
    if authenticated_user.role != 'admin':
        # 403 Forbidden (Erişim Engellendi) hatası döndür
        # Kullanıcının kimliği doğru ama bu kaynağa/işleme izni yok.
        return jsonify({'message': 'Erişim Engellendi. Yönetici yetkisi gerekli.'}), 403

    # Kimlik doğrulama ve Yetkilendirme başarılıysa, admin verisini döndür
    return jsonify({
        'message': 'Admin Paneli Verisi!',
        'data': 'Çok gizli admin bilgileri...',
        'user': authenticated_user.username,
        'role': authenticated_user.role
    })

# ... (Diğer route'larınız ve app.run() kısmı) ...
if __name__ == '__main__':
    # ... db.create_all() ve test kullanıcıları ekleme/güncelleme kodları ...
    app.run(debug=True)
Açıklama: `/admin_panel` route'u önce `authenticate_by_api_key()` ile kimliği doğrular. Başarısız olursa 401 döner. Başarılı olursa, kullanıcının `role` özelliğini kontrol eder. Rol 'admin' değilse, 403 hatası döner. Bu, kullanıcı kimliği doğrulanmış olsa bile, o kaynağa veya işleme yetkisinin olmadığını belirtir.

4. Tam Örnek (Basit API Key Auth + Rol Kontrolü)[/B]

Konu 23'teki API'nizi bu güvenlik katmanlarıyla genişletebilirsiniz. Örneğin, ürün ekleme (POST), güncelleme (PUT) ve silme (DELETE) endpoint'lerinin sadece admin rolüne sahip kullanıcılar tarafından erişilebilir olmasını isteyebilirsiniz.

Python:
from flask import Flask, jsonify, request
from flask_sqlalchemy import SQLAlchemy
import os

# ... (app ve db objelerinin tanımlanması) ...

# User Modeli (api_key ve role alanları eklenmiş)
class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(20), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)
    api_key = db.Column(db.String(50), unique=True, nullable=True) # API Anahtarı
    role = db.Column(db.String(20), nullable=False, default='user') # Kullanıcı rolü

    def __repr__(self):
        return f"User('{self.username}', '{self.email}', '{self.role}')"

# Product Modeli (Konu 23'ten)
class Product(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    ad = db.Column(db.String(100), nullable=False)
    fiyat = db.Column(db.Float, nullable=False)

    def to_dict(self):
        return {
            'id': self.id,
            'ad': self.ad,
            'fiyat': self.fiyat
        }

# Veritabanı tablolarını oluşturma ve test kullanıcıları ekleme (main bloğunda)
# ... db.create_all() ve User objeleri oluşturup ekleme (api_key ve role ile) ...

# Basit API Key Doğrulama Fonksiyonu (Yukarıdaki ile aynı)
def authenticate_by_api_key():
    api_key = request.headers.get('X-API-Key')
    if not api_key:
        return None
    # app objesi tanımlı olmalı
    user = User.query.filter_by(api_key=api_key).first()
    return user

# Yetkilendirme Kontrol Fonksiyonu (Belirli bir rolü kontrol eder)
def is_authorized(user, required_role):
    return user and user.role == required_role

# Route'lar (Konu 23'teki endpoint'lerin güncellenmiş hali)

# Tüm ürünleri listeleme (Herkes Erişebilir - GET)
@app.route('/products', methods=['GET'])
def get_products():
    products = Product.query.all()
    products_list = [product.to_dict() for product in products]
    return jsonify(products_list)

# Belirli bir ürünü getirme (Herkes Erişebilir - GET)
@app.route('/products/<int:product_id>', methods=['GET'])
def get_product(product_id):
    product = Product.query.get_or_404(product_id)
    return jsonify(product.to_dict())

# Yeni ürün ekleme (Sadece Admin Erişebilir - POST)
@app.route('/products', methods=['POST'])
def create_product():
    # ADIM 1: Kimlik Doğrulama
    authenticated_user = authenticate_by_api_key()
    if not authenticated_user:
        return jsonify({'message': 'Kimlik Doğrulama Başarısız'}), 401

    # ADIM 2: Yetkilendirme (Admin rolü gerekli)
    if not is_authorized(authenticated_user, 'admin'):
        return jsonify({'message': 'Ekleme işlemi için yönetici yetkisi gerekli'}), 403

    # Yetkilendirme başarılıysa işlemi yap
    data = request.get_json()
    if not data or not 'ad' in data or not 'fiyat' in data:
        return jsonify({'message': 'Geçersiz veri sağlandı'}), 400

    new_product = Product(ad=data['ad'], fiyat=data['fiyat'])
    db.session.add(new_product)
    db.session.commit()

    return jsonify({'message': 'Ürün başarıyla eklendi', 'product': new_product.to_dict()}), 201

# Ürün güncelleme (Sadece Admin Erişebilir - PUT)
@app.route('/products/<int:product_id>', methods=['PUT'])
def update_product(product_id):
    # Kimlik Doğrulama
    authenticated_user = authenticate_by_api_key()
    if not authenticated_user:
        return jsonify({'message': 'Kimlik Doğrulama Başarısız'}), 401

    # Yetkilendirme (Admin rolü gerekli)
    if not is_authorized(authenticated_user, 'admin'):
        return jsonify({'message': 'Güncelleme işlemi için yönetici yetkisi gerekli'}), 403

    # Yetkilendirme başarılıysa işlemi yap
    product_to_update = Product.query.get_or_404(product_id)
    data = request.get_json()

    if 'ad' in data:
        product_to_update.ad = data['ad']
    if 'fiyat' in data:
        product_to_update.fiyat = data['fiyat']

    db.session.commit()
    return jsonify({'message': 'Ürün başarıyla güncellendi', 'product': product_to_update.to_dict()})

# Ürün silme (Sadece Admin Erişebilir - DELETE)
@app.route('/products/<int:product_id>', methods=['DELETE'])
def delete_product(product_id):
    # Kimlik Doğrulama
    authenticated_user = authenticate_by_api_key()
    if not authenticated_user:
        return jsonify({'message': 'Kimlik Doğrulama Başarısız'}), 401

    # Yetkilendirme (Admin rolü gerekli)
    if not is_authorized(authenticated_user, 'admin'):
        return jsonify({'message': 'Silme işlemi için yönetici yetkisi gerekli'}), 403

    # Yetkilendirme başarılıysa işlemi yap
    product_to_delete = Product.query.get_or_404(product_id)

    db.session.delete(product_to_delete)
    db.session.commit()

    return jsonify({'message': 'Ürün başarıyla silindi'})

# ... (app.run() kısmı) ...
if __name__ == '__main__':
    with app.app_context():
        db.create_all() # Tabloları oluştur
        # Test kullanıcıları ekle (API key ve rol ile)
        if not User.query.filter_by(username='TestAdmin').first():
             admin_user = User(username='TestAdmin', email='[email protected]', api_key='admin_super_key', role='admin')
             db.session.add(admin_user)
        if not User.query.filter_by(username='TestUser').first():
             normal_user = User(username='TestUser', email='[email protected]', api_key='user_basic_key', role='user')
             db.session.add(normal_user)
        db.session.commit()
        print("Veritabanı ve test kullanıcıları hazır.")

    app.run(debug=True)

Sıra Sizde![/B]

API güvenliğinin temellerini ve Flask'ta basit uygulamasını gördük.

  • Konu 23'teki Flask API projenizi açın. User modelinize `api_key` ve `role` alanlarını ekleyin ve `db.create_all()` veya migration ile veritabanınızı güncelleyin.
  • Kodunuza `authenticate_by_api_key()` ve `is_authorized()` fonksiyonlarını ekleyin.
  • `main` bloğuna veya ayrı bir betikle test kullanıcıları (admin ve user rolünde, kendilerine özel API anahtarları ile) ekleyin.
  • `/products` GET route'unun (tümünü listeleme) hala API key olmadan erişilebilir olduğunu test edin (çünkü bu genellikle herkese açık olabilir).
  • `/products/ID_NUMARASI` GET route'unun (tekini getirme) hala API key olmadan erişilebilir olduğunu test edin.
  • `/products` POST, `/products/ID` PUT ve `/products/ID` DELETE route'larına API Key olmadan istek gönderin (Postman/Insomnia/curl ile). 401 Unauthorized hatası aldığınızı doğrulayın.
  • `/products` POST, `/products/ID` PUT ve `/products/ID` DELETE route'larına geçerli bir normal kullanıcı API anahtarı (`user_basic_key`) ile istek gönderin. 403 Forbidden hatası aldığınızı doğrulayın (çünkü admin yetkisi gerekli).
  • `/products` POST, `/products/ID` PUT ve `/products/ID` DELETE route'larına geçerli bir admin API anahtarı (`admin_super_key`) ile istek gönderin. İşlemlerin (ekleme, güncelleme, silme) başarıyla gerçekleştiğini doğrulayın (200 veya 201 status kodları ve beklenen yanıt).
API Güvenliği kavramları (AuthN vs AuthZ), API anahtarı kontrolü, rol tabanlı yetkilendirme, 401/403 status kodları veya bunları Flask'ta uygulama hakkında aklınıza takılan soruları çekinmeden bu konu altında sorabilirsiniz.

Serinin Geleceği?[/B]

API güvenliğinin temellerini (Kimlik Doğrulama ve Yetkilendirme) gördük ve basit bir API Key + Rol kontrolü uyguladık. Bu, API'leri korumanın ilk adımıdır.

Seriyi buradan sonra nasıl devam ettirelim?

  • API Güvenliğinde daha ileri konular (Token Tabanlı Kimlik Doğrulama - JWT/OAuth giriş, şifre güvenliği - hashing, HTTPS önemi, Rate Limiting)?
  • Flask'ın güvenlik uzantıları (Flask-Login, Flask-Security) veya daha gelişmiş API uzantıları (Flask-RESTful/RESTX)?
  • Client-side JavaScript kullanarak bu güvenli API'yi tüketme (AJAX isteklerinde Header gönderme)?
  • Daha ileri Veri Yapıları ve Algoritma konuları?
  • Başka bir programlama diline giriş (C#, C++ gibi)?
  • Mobil geliştirme temelleri (API tüketimi dahil)?
  • Veya başka önerileriniz mi var?
Geri bildirimleriniz ve önerileriniz serinin geleceğini şekillendirecektir.

Umarım bu konu, API'lerinizi ve uygulamalarınızı daha güvenli hale getirme konusunda sizlere temel bir bakış açısı sunmuştur. Görüşmek üzere!


Bu konu, "Hazır Kod Bankası" serisinin yirmi dördüncü parçasıdır ve "Yazılım Bilgi ve Yeni Başlayanlar İçin" kategorisi altında paylaşılmıştır.
 

Şu an konuyu görüntüleyenler

Tema özelleştirme sistemi

Bu menüden forum temasının bazı alanlarını kendinize özel olarak düzenleye bilirsiniz

Zevkini yansıtan rengi seç

Geniş / Dar görünüm

Temanızı geniş yada dar olarak kullanmak için kullanabileceğiniz bir yapıyı kontrolünü sağlayabilirsiniz.

Izgara görünümlü forum listesi

Forum listesindeki düzeni ızgara yada sıradan listeleme tarzındaki yapının kontrolünü sağlayabilirsiniz.

Resimli ızgara modu

Izgara forum listesinde resimleri açıp/kapatabileceğiniz yapının kontrolünü sağlayabilirsiniz.

Kenar çubuğunu kapat

Kenar çubuğunu kapatarak forumdaki kalabalık görünümde kurtulabilirsiniz.

Sabit kenar çubuğu

Kenar çubuğunu sabitleyerek daha kullanışlı ve erişiminizi kolaylaştırabilirsiniz.

Köşe kıvrımlarını kapat

Blokların köşelerinde bulunan kıvrımları kapatıp/açarak zevkinize göre kullanabilirsiniz.

Geri