- 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.
Ö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.")
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)
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()
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)
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).
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?
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.