この記事では、mssql-django バックエンドを介してSQL Serverに接続する Django アプリケーションのセキュリティプラクティスについて説明します。 これらのプラクティスは、Django の組み込みのセキュリティ機能とSQL Serverのセキュリティ モデルを補完します。
パスワードの代わりにMicrosoft Entra認証を使用する
Microsoft Entra認証では、保存されているデータベース パスワードが削除されます。 すべての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",
},
},
}
認証方法の完全な一覧、現時点での注意事項、および DefaultAzureCredential と ManagedIdentityCredential を使用した TOKEN の例については、mssql-django での Microsoft Entra 認証をご覧ください。
資格情報を安全に管理する
SQL 認証が必要な場合は、資格情報をソース コードから除外します。
環境変数
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
運用環境のデプロイの場合は、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",
},
},
}
注意事項
資格情報をソース管理にコミットしないでください。
.envファイルを.gitignoreに追加します。 誤った資格情報のコミットをスキャンするには、 git-secrets またはコミット前フックを使用します。
TLS 暗号化を適用する
SQL Server接続は常に暗号化する必要があります。 ODBC ドライバーは、ODBC Driver 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
},
},
}
ODBC Driver 17 を使用する場合は、暗号化を明示的に有効にします。
"extra_params": "Encrypt=yes"
注意事項
TrustServerCertificate=yesは、自己署名証明書を使用したローカル開発にのみ使用します。 運用環境では使用しないでください。 証明書チェーンの検証が無効になり、敵対者の中間リスクが増加します。 信頼された証明書をサーバーにインストールし、 TrustServerCertificate=noで接続します。
最小特権の原則を適用する
アプリケーションに必要なアクセス許可のみを使用して、専用のSQL Server ログインを作成します。
-- 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];
運用環境で移行を実行しないアプリケーションの場合は、 ALTER と 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
別の特権を持つデプロイ手順から移行を実行します。
-- Migration user (used only during deployments)
ALTER ROLE db_ddladmin ADD MEMBER [django_migrations];
適切なロールを選択する
SQL Server固定データベース ロールは、最小限から最も特権の高い順に並べ替えられます。 ワークロードを対象とする最小特権ロールを選択し、必要な場合にのみエスカレートします。
| Role | Grants | いつ使用するか |
|---|---|---|
| db_datareader |
SELECT すべてのユーザーテーブルとビューに対して |
読み取り専用のレポートユーザー |
| db_datawriter | すべてのユーザー テーブル上のINSERT、UPDATE、DELETE |
ランタイム アプリケーション ユーザー ( db_datareaderと組み合わせる) |
| db_ddladmin | スキーマ オブジェクトの作成、変更、および削除 | 移行またはデプロイユーザーのみ |
| db_owner | セキュリティを含むすべてのデータベースアクセス許可 | アプリケーションでは避け、DBA 専用としてください |
固定ロールで許可されるよりもきめ細かな制御を行う場合は、カスタム データベース ロールを作成し、アプリで使用する特定のスキーマに対する特定のアクセス許可のみを GRANT します。 すべてのアプリケーション オブジェクトを専用スキーマ (たとえば app) に保持すると、データベース全体の db_datareader ロールや db_datawriter ロールに頼るのではなく、GRANT ... ON SCHEMA::app を使用して権限の範囲を限定できます。
Note
アプリケーション接続には、 sa アカウントや固定データベース ロール db_owner 使用しないでください。 アプリケーションが侵害された場合、攻撃者はデータベースを完全に制御できます。
SQL インジェクションを防止する
Django の ORM は、すべてのクエリを自動的にパラメーター化します。 SQL インジェクションは、生の SQL を使用する場合にのみリスクになります。
安全な ORM クエリ
# Django parameterizes these automatically
users = User.objects.filter(email=user_input)
products = Product.objects.filter(price__lte=max_price)
安全: パラメーター化された生 SQL
from django.db import connection
with connection.cursor() as cursor:
cursor.execute(
"SELECT * FROM products WHERE category = %s AND price < %s",
[category, max_price],
)
安全でない: 生の SQL での文字列の書式設定
# 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)
Extra と RawSQL
Django の extra() と RawSQL() は生の SQL フラグメントを受け入れます。 常にパラメーターを使用します。
# 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
文字列の書式設定で extra() や RawSQL() を使用しないでください。 これらは、ORM の自動パラメーター化をバイパスします。
Django セキュリティ ミドルウェアを構成する
Django の組み込みのセキュリティ ミドルウェアを有効にして、Web レイヤーを保護します。 これらはデータベース固有ではありませんが、データベースに接続するアプリケーションを保護します。
# 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
データベース アクセスの監査
SQL Server監査を有効にして、Django アプリケーションからのデータベース操作を追跡します。
-- 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);
Azure SQL Databaseの場合は、Azure ポータルまたはAzure CLIを使用して監査を有効にします。
az sql db audit-policy update --resource-group <rg> --server <server> \
--name <database> --state Enabled \
--storage-account <storage-account>
Always Encrypted を使用して機密性の高い列をセキュリティで保護する
SSN、クレジット カード番号、給与データなどの機密データの列レベルの暗号化には、Always Encrypted を使用します。 ODBC ドライバーは、暗号化と復号化を透過的に処理します。
DATABASES = {
"default": {
"ENGINE": "mssql",
"NAME": "<your-database>",
"OPTIONS": {
"driver": "ODBC Driver 18 for SQL Server",
"extra_params": "ColumnEncryption=Enabled",
},
},
}
Azure Key Vaultを使用したキー管理を含む詳細なセットアップについては、mssql-django を使用した Always Encrypted に関する記事を参照してください。
セキュリティ チェックリスト
| カテゴリ | 演習 | 優先度 |
|---|---|---|
| Authentication | Azure SQL Microsoft Entra認証を使用します。 | 高 |
| 資格情報 | 環境変数またはAzure Key Vaultにシークレットを格納します。 | 高 |
| 暗号化 | ODBC Driver 18 (既定では暗号化オン) または Encrypt=yesを使用します。 |
高 |
| 注入 | ORM クエリまたはパラメーター化された生 SQL を使用します。 SQL を文字列形式にしない。 | 高 |
| 最小特権 | 必要最小限のアクセス許可を持つ専用ログインを作成します。 | 高 |
| TLS | 運用環境では TrustServerCertificate=yes を使用しないでください。 |
高 |
| Django |
SECURE_SSL_REDIRECT、セキュリティで保護された Cookie、HSTS を有効にします。 |
中程度 |
| Auditing | SQL Server監査またはAzure SQL監査を有効にします。 | 中程度 |
| 列の暗号化 | 機密性の高い列には Always Encrypted を使用します。 | 低 |
| 接続 |
CONN_MAX_AGEとCONN_HEALTH_CHECKSを設定して、古い接続を防ぎます。 |
低 |