Praktik terbaik keamanan untuk mssql-django

Artikel ini membahas praktik keamanan untuk aplikasi Django yang terhubung ke SQL Server melalui mssql-django backend. Praktik ini melengkapi fitur keamanan bawaan Django dan model keamanan SQL Server.

Gunakan autentikasi Microsoft Entra alih-alih kata sandi

Autentikasi Microsoft Entra menghapus kata sandi database tersimpan. Gunakan untuk semua koneksi Azure SQL.

DATABASES = {
    "default": {
        "ENGINE": "mssql",
        "NAME": "<your-database>",
        "HOST": "<your-server>.database.windows.net",
        "PORT": "1433",
        "OPTIONS": {
            "driver": "ODBC Driver 18 for SQL Server",
            "extra_params": "Authentication=ActiveDirectoryMsi",
        },
    },
}

Untuk daftar lengkap metode autentikasi, catatan penting saat ini, serta TOKEN contoh penggunaan DefaultAzureCredential dan ManagedIdentityCredential, lihat Autentikasi Microsoft Entra dengan mssql-django.

Mengelola kredensial dengan aman

Saat Anda memerlukan autentikasi SQL, jauhkan kredensial dari kode sumber.

Variabel lingkungan

import os

DATABASES = {
    "default": {
        "ENGINE": "mssql",
        "NAME": os.environ["DB_NAME"],
        "USER": os.environ["DB_USER"],
        "PASSWORD": os.environ["DB_PASSWORD"],
        "HOST": os.environ["DB_HOST"],
        "PORT": os.environ.get("DB_PORT", "1433"),
        "OPTIONS": {
            "driver": "ODBC Driver 18 for SQL Server",
        },
    },
}

Azure Key Vault

Untuk penyebaran produksi, ambil rahasia dari Azure Key Vault:

from azure.identity import DefaultAzureCredential
from azure.keyvault.secrets import SecretClient

credential = DefaultAzureCredential()
client = SecretClient(vault_url="https://<your-vault>.vault.azure.net/", credential=credential)

DATABASES = {
    "default": {
        "ENGINE": "mssql",
        "NAME": client.get_secret("db-name").value,
        "USER": client.get_secret("db-user").value,
        "PASSWORD": client.get_secret("db-password").value,
        "HOST": client.get_secret("db-host").value,
        "PORT": "1433",
        "OPTIONS": {
            "driver": "ODBC Driver 18 for SQL Server",
        },
    },
}

Perhatian

Jangan pernah menerapkan kredensial ke kontrol sumber. Tambahkan .env file ke .gitignore. Gunakan git-secrets atau hook pre-commit untuk memindai komit kredensial yang tidak disengaja.

Menerapkan enkripsi TLS

SQL Server koneksi harus selalu dienkripsi. Driver ODBC mengenkripsi koneksi secara default dimulai dengan Driver ODBC 18:

DATABASES = {
    "default": {
        "ENGINE": "mssql",
        "NAME": "<your-database>",
        "HOST": "<your-server>.database.windows.net",
        "OPTIONS": {
            "driver": "ODBC Driver 18 for SQL Server",
            # Encryption is on by default with Driver 18
        },
    },
}

Jika Anda menggunakan Driver ODBC 17, aktifkan enkripsi secara eksplisit:

"extra_params": "Encrypt=yes"

Perhatian

Gunakan TrustServerCertificate=yes hanya untuk pengembangan lokal dengan sertifikat yang ditandatangani sendiri. Jangan gunakan dalam produksi. Ini menonaktifkan validasi rantai sertifikat dan meningkatkan risiko serangan perantara. Instal sertifikat tepercaya di server dan sambungkan dengan TrustServerCertificate=no.

Menerapkan prinsip hak istimewa terendah

Buat login SQL Server khusus hanya dengan izin yang dibutuhkan aplikasi Anda:

-- Create a login and user for the application
CREATE LOGIN [django_app]
WITH PASSWORD = '<strong-password>';

USE [<your-database>];

CREATE USER [django_app] FOR LOGIN [django_app];

-- Grant minimum required permissions
-- Read and write data
ALTER ROLE db_datareader ADD MEMBER [django_app];
ALTER ROLE db_datawriter ADD MEMBER [django_app];

-- Allow Django to create and alter tables during migrations
GRANT ALTER ON SCHEMA::dbo TO [django_app];
GRANT CREATE TABLE TO [django_app];
GRANT REFERENCES ON SCHEMA::dbo TO [django_app];

Untuk aplikasi yang tidak menjalankan migrasi di lingkungan produksi, hilangkan izin ALTER dan CREATE TABLE:

-- Production application user (read/write only)
ALTER ROLE db_datareader ADD MEMBER [django_app];
ALTER ROLE db_datawriter ADD MEMBER [django_app];

GRANT EXECUTE ON SCHEMA::dbo TO [django_app]; -- If using stored procedures

Jalankan migrasi dari tahap deployment yang terpisah dengan hak akses lebih tinggi:

-- Migration user (used only during deployments)
ALTER ROLE db_ddladmin ADD MEMBER [django_migrations];

Pilih peran yang tepat

Peran database tetap SQL Server diurutkan dari yang hak aksesnya paling rendah hingga yang paling tinggi. Pilih peran dengan hak istimewa paling minimal yang sesuai dengan beban kerja Anda, dan tingkatkan hak akses hanya bila diperlukan:

Peranan Grants Kapan digunakan
db_datareader SELECT pada semua tabel dan tampilan milik pengguna Pengguna laporan hanya baca
db_datawriter INSERT, UPDATE, DELETE pada semua tabel pengguna Pengguna aplikasi runtime (gabungkan dengan db_datareader)
db_ddladmin Membuat, mengubah, dan menghilangkan objek skema Khusus pengguna migrasi atau penerapan
db_owner Semua izin database, termasuk keamanan Hindari untuk aplikasi; khusus untuk DBA

Untuk kontrol yang lebih terperinci daripada yang dimungkinkan oleh peran tetap, buat peran database kustom dan GRANT hanya hak akses tertentu pada skema tertentu yang digunakan oleh aplikasi Anda. Menyimpan semua objek aplikasi dalam skema khusus (misalnya, app) memungkinkan Anda membatasi grant dengan GRANT ... ON SCHEMA::app alih-alih mengandalkan peran db_datareader dan db_datawriter tingkat database.

Note

Jangan gunakan akun sa atau peran database tetap db_owner untuk koneksi aplikasi. Jika aplikasi disusupi, penyerang mendapatkan kontrol database penuh.

Mencegah injeksi SQL

ORM Django secara otomatis memparametrisasi semua kueri. Injeksi SQL hanya berisiko ketika Anda menggunakan SQL mentah:

Aman: Kueri ORM

# Django parameterizes these automatically
users = User.objects.filter(email=user_input)
products = Product.objects.filter(price__lte=max_price)

Aman: SQL mentah yang diparameterkan

from django.db import connection

with connection.cursor() as cursor:
    cursor.execute(
        "SELECT * FROM products WHERE category = %s AND price < %s",
        [category, max_price],
    )

Tidak aman: Pemformatan string dalam SQL mentah

# NEVER do this - vulnerable to SQL injection
cursor.execute(f"SELECT * FROM products WHERE category = '{category}'")
cursor.execute("SELECT * FROM products WHERE category = '%s'" % category)

Ekstra dan RawSQL

Django dan extra()RawSQL() menerima fragmen SQL mentah. Selalu gunakan parameter:

# Safe - parameterized
Product.objects.extra(where=["category = %s"], params=[category])

from django.db.models.expressions import RawSQL
Product.objects.annotate(
    discount=RawSQL("price * %s", [discount_rate])
)

Important

Jangan pernah menggunakan extra() atau RawSQL() dengan pemformatan string. Ini melewati parameterisasi otomatis ORM.

Mengonfigurasi middleware keamanan Django

Aktifkan middleware keamanan bawaan Django untuk melindungi lapisan web. Meskipun ini tidak khusus untuk basis data, ini melindungi aplikasi yang terhubung ke basis data Anda:

# settings.py

# HTTPS enforcement
SECURE_SSL_REDIRECT = True
SECURE_HSTS_SECONDS = 31536000  # 1 year
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True

# Cookie security
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SESSION_COOKIE_HTTPONLY = True

# Content security
SECURE_CONTENT_TYPE_NOSNIFF = True

Mengaudit akses database

Aktifkan audit SQL Server untuk melacak operasi database dari aplikasi Django Anda:

-- Create a server audit (Azure SQL uses Azure SQL Auditing instead)
CREATE SERVER AUDIT [DjangoAudit]
TO FILE (FILEPATH = 'C:\Audits\')
WITH (ON_FAILURE = CONTINUE);

ALTER SERVER AUDIT [DjangoAudit] WITH (STATE = ON);

-- Create a database audit specification
USE [<your-database>];

CREATE DATABASE AUDIT SPECIFICATION [DjangoDbAudit]
FOR SERVER AUDIT [DjangoAudit]
ADD (SELECT, INSERT, UPDATE, DELETE ON SCHEMA::dbo BY [django_app])
WITH (STATE = ON);

Untuk Azure SQL Database, aktifkan audit melalui portal Azure atau Azure CLI:

az sql db audit-policy update --resource-group <rg> --server <server> \
    --name <database> --state Enabled \
    --storage-account <storage-account>

Mengamankan kolom sensitif dengan Always Encrypted

Untuk enkripsi tingkat kolom data sensitif seperti SSN, nomor kartu kredit, atau data gaji, gunakan Always Encrypted. Driver ODBC menangani enkripsi dan dekripsi secara transparan:

DATABASES = {
    "default": {
        "ENGINE": "mssql",
        "NAME": "<your-database>",
        "OPTIONS": {
            "driver": "ODBC Driver 18 for SQL Server",
            "extra_params": "ColumnEncryption=Enabled",
        },
    },
}

Untuk penyiapan terperinci termasuk manajemen kunci dengan Azure Key Vault, lihat Always Encrypted dengan mssql-django.

Daftar periksa keamanan

Kategori Praktik Prioritas
Authentication Gunakan autentikasi Microsoft Entra untuk Azure SQL. High
Credentials Simpan rahasia dalam variabel lingkungan atau Azure Key Vault. High
Encryption Gunakan Driver ODBC 18 (enkripsi aktif secara default) atau Encrypt=yes. High
Injeksi Gunakan kueri ORM atau SQL mentah berparameter. Jangan pernah memformat SQL sebagai string. High
Hak istimewa terkecil Buat login khusus dengan izin minimum yang diperlukan. High
TLS Jangan gunakan TrustServerCertificate=yes dalam produksi. High
Django Aktifkan SECURE_SSL_REDIRECT, amankan cookie, HSTS. Medium
Auditing Aktifkan audit SQL Server atau Audit Azure SQL. Medium
Enkripsi kolom Gunakan Always Encrypted untuk kolom yang sangat sensitif. Low
Koneksi Atur CONN_MAX_AGE dan CONN_HEALTH_CHECKS untuk mencegah koneksi kedaluarsa. Low