Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Este artigo aborda práticas de segurança para aplicações Django que se ligam ao SQL Server através do mssql-django backend. Estas práticas complementam as funcionalidades de segurança integradas da Django e o modelo de segurança do SQL Server.
Use autenticação Microsoft Entra em vez de palavras-passe
A autenticação Microsoft Entra elimina palavras-passe armazenadas na base de dados. Usa-o para todas as ligações SQL do Azure.
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",
},
},
}
Para obter a lista completa dos métodos de autenticação, as limitações atuais e TOKEN exemplos com DefaultAzureCredential e ManagedIdentityCredential, consulte Autenticação do Microsoft Entra com o mssql-django.
Gerir credenciais de forma segura
Quando precisares de autenticação SQL, mantém as credenciais fora do código-fonte.
Variáveis ambientais
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
Para implementações de produção, obtenha segredos do 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
Nunca comprometas credenciais com o controlo de versão. Adicionar .env ficheiros a .gitignore. Utilize git-secrets ou ganchos de pré-commit para detetar commits acidentais com credenciais.
Aplicar encriptação TLS
As ligações ao SQL Server devem estar sempre encriptadas. O driver ODBC encripta as ligações por defeito, começando pelo 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
},
},
}
Se usar o Driver ODBC 17, ative explicitamente a encriptação:
"extra_params": "Encrypt=yes"
Caution
Uso TrustServerCertificate=yes apenas para desenvolvimento local com certificados auto-assinados. Não o uses na produção. Desativa a validação da cadeia de certificados e aumenta o risco de ataque de intermediário. Instale um certificado de confiança no servidor e ligue-se a TrustServerCertificate=no.
Aplicar o princípio do menor privilégio
Crie logins dedicados no SQL Server apenas com as permissões que a sua aplicação necessita:
-- 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];
Para aplicações que não executam migrações em produção, omita as permissões ALTER e 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
Execute migrações a partir de uma etapa de implementação separada e mais privilegiada:
-- Migration user (used only during deployments)
ALTER ROLE db_ddladmin ADD MEMBER [django_migrations];
Escolha o papel certo
Os papéis fixos da base de dados do SQL Server estão ordenados do menos ao mais privilegiado. Escolha o cargo menos privilegiado que cubra a sua carga de trabalho e só escale quando necessário:
| Função | Grants | Quando utilizar |
|---|---|---|
| db_datareader |
SELECT em todas as tabelas e vistas de utilizadores |
Utilizadores que reportam apenas leitura |
| db_datawriter |
INSERT, UPDATE, DELETE em todas as tabelas de utilizador |
Utilizador de aplicação de runtime (combinar com db_datareader) |
| db_ddladmin | Criar, alterar e eliminar objetos de esquema | Migração ou implementação apenas para utilizadores |
| db_owner | Todas as permissões da base de dados, incluindo segurança | Evitar para aplicações; reservar para DBAs |
Para um controlo mais granular do que as funções fixas permitem, crie uma função personalizada da base de dados e GRANT apenas as permissões necessárias no esquema específico que a sua aplicação utiliza. Manter todos os objetos da aplicação num esquema dedicado (por exemplo, app) permite-lhe delimitar as permissões com GRANT ... ON SCHEMA::app em vez de depender das funções db_datareader e db_datawriter ao nível de toda a base de dados.
Note
Não utilize a conta sa nem a função de base de dados fixa db_owner para ligações da aplicação. Se a aplicação for comprometida, um atacante ganha controlo total da base de dados.
Prevenir a injeção SQL
O ORM do Django parametriza automaticamente todas as consultas. A injeção de SQL só é um risco quando se usa SQL bruto:
Seguro: Consultas ORM
# Django parameterizes these automatically
users = User.objects.filter(email=user_input)
products = Product.objects.filter(price__lte=max_price)
Safe: SQL bruto parametrizado
from django.db import connection
with connection.cursor() as cursor:
cursor.execute(
"SELECT * FROM products WHERE category = %s AND price < %s",
[category, max_price],
)
Inseguro: Formatação de strings em SQL bruto
# 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 e RawSQL
Django extra() e RawSQL() aceitam fragmentos SQL brutos. Use sempre parâmetros:
# 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])
)
Importante
Nunca uses extra() ou RawSQL() na formatação de cadeias de caracteres. Estas contornam a parametrização automática do ORM.
Configurar middleware de segurança Django
Ative o middleware de segurança incorporado da Django para proteger a camada web. Embora estes não sejam específicos da base de dados, protegem a aplicação que se liga à sua base de dados:
# 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
Acesso à base de dados de auditoria
Ative a auditoria do SQL Server para acompanhar as operações da base de dados a partir da sua aplicação 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);
Para Base de Dados SQL do Azure, ative a auditoria através do portal Azure ou CLI do Azure:
az sql db audit-policy update --resource-group <rg> --server <server> \
--name <database> --state Enabled \
--storage-account <storage-account>
Proteger colunas sensíveis com Always Encrypted
Para encriptação ao nível de coluna de dados sensíveis como SSNs, números de cartões de crédito ou dados salariais, use o Always Encrypted. O driver ODBC gere a encriptação e a desencriptação de forma transparente:
DATABASES = {
"default": {
"ENGINE": "mssql",
"NAME": "<your-database>",
"OPTIONS": {
"driver": "ODBC Driver 18 for SQL Server",
"extra_params": "ColumnEncryption=Enabled",
},
},
}
Para uma configuração detalhada, incluindo gestão de chaves com Azure Key Vault, veja Always Encrypted with mssql-django.
Lista de verificação de segurança
| Categoria | Practice | Prioridade |
|---|---|---|
| Authentication | Utilize a autenticação do Microsoft Entra para o SQL do Azure. | Alto |
| Credentials | Guardar segredos em variáveis de ambiente ou no Azure Key Vault. | Alto |
| Encryption | Utilize o controlador ODBC 18 (encriptação ativada por predefinição) ou Encrypt=yes. |
Alto |
| Injeção | Utilize consultas ORM ou SQL em bruto parametrizado. Nunca SQL em formato de string. | Alto |
| Privilégio mínimo | Crie logins dedicados com permissões mínimas necessárias. | Alto |
| TLS | Não uses TrustServerCertificate=yes em produção. |
Alto |
| Django | Ativar SECURE_SSL_REDIRECT, proteger cookies, HSTS. |
Medium |
| Auditing | Ative a auditoria do SQL Server ou da auditoria SQL do Azure. | Medium |
| Encriptação de colunas | Use o Always Encrypted para colunas altamente sensíveis. | Low |
| Conexão | Definir CONN_MAX_AGE e CONN_HEALTH_CHECKS para evitar ligações obsoletas. |
Low |