이 문서에서는 백 엔드를 통해 SQL Server 연결하는 Django 애플리케이션에 mssql-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",
},
},
}
인증 방법, 현재 주의 사항 및 TOKEN 예제 DefaultAzureCredentialManagedIdentityCredential의 전체 목록은 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",
},
},
}
Caution
소스 제어에 자격 증명을 커밋하지 않습니다.
.gitignore에 .env 파일 추가 실수로 자격 증명이 커밋되는 것을 검사하려면 git-secrets 또는 pre-commit 훅을 사용하세요.
TLS 암호화 적용
SQL Server 연결은 항상 암호화되어야 합니다. ODBC 드라이버는 기본적으로 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
},
},
}
ODBC 드라이버 17을 사용하는 경우 암호화를 명시적으로 사용하도록 설정합니다.
"extra_params": "Encrypt=yes"
Caution
자체 서명된 인증서를 사용하여 로컬 개발에만 사용합니다 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 고정 데이터베이스 역할은 최소에서 대부분의 권한으로 정렬됩니다. 워크로드를 포괄하는 최소 권한 역할을 선택하고 필요한 경우에만 에스컬레이션합니다.
| 역할 | 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을 사용해 권한 부여 범위를 지정할 수 있습니다.
메모
애플리케이션 연결에 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의 기본 제공 보안 미들웨어를 사용하도록 설정하여 웹 계층을 보호합니다. 이러한 항목은 데이터베이스와 관련이 없지만 데이터베이스에 연결하는 애플리케이션을 보호합니다.
# 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 인증을 사용합니다. | 높음 |
| Credentials | 환경 변수 또는 Azure Key Vault 비밀을 저장합니다. | 높음 |
| Encryption | ODBC 드라이버 18(기본적으로 암호화) 또는 Encrypt=yes. |
높음 |
| 주입 | ORM 쿼리 또는 매개 변수가 있는 원시 SQL을 사용합니다. 문자열 형식 SQL을 지정하지 않습니다. | 높음 |
| 최소 권한 | 필요한 최소 권한으로 전용 로그인을 만듭니다. | 높음 |
| TLS | 프로덕션 환경에서는 TrustServerCertificate=yes을 사용하지 마세요. |
높음 |
| Django |
SECURE_SSL_REDIRECT, 보안 쿠키, HSTS를 활성화합니다. |
중간 |
| Auditing | SQL Server 감사 또는 Azure SQL 감사를 사용하도록 설정합니다. | 중간 |
| 열 암호화 | 매우 중요한 열에 Always Encrypted를 사용합니다. | 낮음 |
| Connection | 오래된 연결을 방지하려면 CONN_MAX_AGE 및 CONN_HEALTH_CHECKS을 설정하세요. |
낮음 |