Kommentar
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Säkra dina mssql-python-applikationer genom att följa dessa bästa praxis för autentisering, parameteriserade frågor och dataskydd.
Börja med lösenordslös autentisering när du kan. Behandla lokala .env filer och SQL-lösenord som tillfälliga utvecklingshjälpmedel och flytta hemligheter till hanterade identiteter eller en hemlig lagring innan koden når en delad miljö.
Autentiseringssäkerhet
Använd Microsoft Entra-autentisering istället för SQL-autentisering
Microsoft Entra-autentisering eliminerar lagrade lösenord och stödjer hanterade identiteter. Föredrar det framför SQL-autentisering i alla miljöer.
För arbetsbelastningar som finns i Azure använder du en hanterad identitet med ActiveDirectoryMSI. Den behöver inga lagrade hemligheter och ansluter utan att gå igenom en legitimationskedja:
import mssql_python
def connect_with_managed_identity():
return mssql_python.connect(
"Server=<server>.database.windows.net;"
"Database=<database>;"
"Authentication=ActiveDirectoryMSI;"
"Encrypt=yes;"
)
För lokal utveckling, använd ActiveDirectoryDefault, som automatiskt plockar upp din Azure CLI eller andra utvecklaruppgifter. Undvik det i produktion, eftersom DefaultAzureCredential provar varje autentiseringsuppgiftsprovider i tur och ordning vid den första anslutningen, vilket ger extra latens som arbetsbelastningar i produktion inte behöver:
import mssql_python
def connect_with_default_credential():
return mssql_python.connect(
"Server=<server>.database.windows.net;"
"Database=<database>;"
"Authentication=ActiveDirectoryDefault;"
"Encrypt=yes;"
)
Undvik: SQL-autentisering lagrar uppgifter i kod/konfiguration och är sårbar för läckor.
conn = mssql_python.connect("Server=...;UID=user;PWD=password")
Hårdkoda aldrig inloggningsuppgifter
Använd miljövariabler endast för lokal utveckling. I delade miljöer föredrar man lösenordslös autentisering. När ett gammalt SQL-autentiseringsflöde är oundvikligt, hämta hemligheten vid körning från en hemlig lagring istället för att kontrollera en fullständig reťazec pripojenia i versionskontroll.
Följande metod hårdkodar inloggningsuppgifter och bör aldrig användas:
conn_str = "Server=<server>;UID=<login>;PWD=<password>"
För lokal utveckling läser du autentiseringsuppgifter från miljövariabler:
import os
conn_str = (
f"Server={os.environ['DB_SERVER']};"
f"Database={os.environ['DB_NAME']};"
)
För delade miljöer som fortfarande behöver en hemlighet, hämta den vid körning från Azure Key Vault:
from azure.keyvault.secrets import SecretClient
from azure.identity import DefaultAzureCredential
def get_connection_string():
credential = DefaultAzureCredential()
secret_client = SecretClient(
vault_url="https://myvault.vault.azure.net/",
credential=credential
)
return secret_client.get_secret("db-connection-string").value
SQL-injektionsprevention
Använd alltid parametriserade frågor
Parameteriserade frågor förhindrar SQL-injektion genom att separera användarinmatning från frågestrukturen. Använd alltid parametrar för användarinmatning.
Följande strängformaterade fråga är sårbar för SQL-injektion. Bygg aldrig frågor på det här sättet:
user_input = "'; DROP TABLE Person.Person; --"
cursor.execute(f"SELECT * FROM Person.Person WHERE FirstName = '{user_input}'")
Följande parameteriserade fråga är säker, eftersom drivrutinen skickar värdet separat från frågetexten:
user_input = "'; DROP TABLE Person.Person; --"
cursor.execute(
"SELECT * FROM Person.Person WHERE FirstName = %(name)s",
{"name": user_input}
)
Parametrisera alla frågekomponenter
Du kan inte direkt parametrisera tabell- och kolumnnamn. Att interpolera dem från användarinput gör din app sårbar för SQL-injektion:
table = user_input
cursor.execute(f"SELECT * FROM {table}")
Validera istället dynamiska identifierare mot en tillåtningslista av tillåtna värden och parametrisera de återstående värdena.
ALLOWED_TABLES = {"Person.Person", "Production.Product", "Sales.SalesOrderHeader"}
def query_table(cursor, table_name: str, conditions: dict):
"""Query with validated table name."""
if table_name not in ALLOWED_TABLES:
raise ValueError(f"Invalid table: {table_name}")
# Table name is safe, parameters are parameterized
where_clauses = [f"{k} = %({k})s" for k in conditions.keys()]
query = f"SELECT * FROM {table_name} WHERE {' AND '.join(where_clauses)}"
cursor.execute(query, conditions)
return cursor.fetchall()
Validera och sanera indata
När du bygger dynamisk SQL med identifierare, validera varje värde mot ett strikt mönster innan du använder det:
import re
def validate_identifier(value: str) -> bool:
"""Validate SQL identifier (table/column name)."""
# Only allow alphanumeric and underscore
return bool(re.match(r'^[a-zA-Z_][a-zA-Z0-9_]*$', value))
def safe_order_by(cursor, table: str, order_column: str, direction: str):
"""Order by with validation."""
if not validate_identifier(order_column):
raise ValueError(f"Invalid column name: {order_column}")
if direction.upper() not in ("ASC", "DESC"):
raise ValueError(f"Invalid direction: {direction}")
cursor.execute(f"""
SELECT * FROM {table}
ORDER BY {order_column} {direction.upper()}
""")
Använd lagrade procedurer för komplexa operationer
Lagrade procedurer minskar SQL-ytan som exponeras för applikationskoden:
employee_id = 5
cursor.execute("""
EXECUTE dbo.uspGetEmployeeManagers @BusinessEntityID = %(id)s
""", {"id": employee_id})
Anslutningssäkerhet
Kräver kryptering
Kryptera alltid anslutningar. Azure SQL upprätthåller kryptering som standard. För lokal SQL Server, sätt Encrypt=yes uttryckligen:
conn = mssql_python.connect(
"Server=<server>.database.windows.net;"
"Database=<database>;"
"Encrypt=yes;"
"TrustServerCertificate=no"
)
Använd TDS 8.0 strikt läge för högsta säkerhet
TDS 8.0 tillhandahåller:
- TLS 1.3 från anslutningsstart
- Certifikatvalidering krävs
- Ingen fallback till äldre protokoll
conn = mssql_python.connect(
"Server=tcp:<server>.database.windows.net,1433;"
"Database=<database>;"
"Encrypt=strict"
)
Validera servercertifikat
Validera alltid servercertifikatet i produktion för att förhindra attacker från angriparen i mitten. Ange inte TrustServerCertificate=yes, eftersom det kringgår valideringen. Ställ TrustServerCertificate=no istället in att validera mot CA-certifikaten och sätt HostNameInCertificate att verifiera värdnamnet:
conn = mssql_python.connect(
"Server=<server>;"
"Database=<database>;"
"Encrypt=yes;"
"TrustServerCertificate=no;"
"HostNameInCertificate=<server>.domain.com"
)
Dataskydd
Skydda känslig data på servernivå
Always Encrypted går för närvarande inte att konfigurera via mssql-python reťazec pripojenia-nyckelord. Om du behöver Always Encrypted, använd pyodbc med ODBC-drivrutinen för SQL Server, som stödjer det. Även om de inte ger samma skyddsnivå kan du använda SQL Server-funktioner som dynamisk datamaskering och radsäkerhet för att skydda känsliga kolumner.
employee_id = 1
cursor.execute("""
SELECT NationalIDNumber, LoginID
FROM HumanResources.Employee
WHERE BusinessEntityID = %(id)s
""", {"id": employee_id})
row = cursor.fetchone()
Skydda data under överföring
- Använd
Encrypt=yesi anslutningssträngar. - Använd VPN eller privata endpoints för lokala anslutningar.
- Använd Azure Private Link för Azure SQL.
Princip om minsta privilegium
Använd minimala databasbehörigheter
Applikationskonton bör ha minimala behörigheter. Använd inte sa eller db_owner för applikationsanslutningar.
-
Skrivskyddad rapportering:
GRANT SELECT ON SCHEMA::dbo TO ReportingApp; -
Specifik tabellåtkomst:
GRANT SELECT, INSERT, UPDATE ON Orders TO OrderProcessor; -
Endast lagrad procedur:
GRANT EXECUTE ON ProcessOrder TO OrderProcessor;
Använd olika konton för olika operationer
Säkerhetskopiera varje privilegienivå med sin egen identitet så att en läsväg inte kan utföra skrivningar. Detta exempel använder två användartilldelade hanterade identiteter, en beviljad skrivbar åtkomst och en beviljad skrivåtkomst, vald med klient-ID:
readonly_client_id = os.environ["READONLY_IDENTITY_CLIENT_ID"]
readwrite_client_id = os.environ["READWRITE_IDENTITY_CLIENT_ID"]
def get_readonly_connection():
"""Connection for read-only operations."""
return mssql_python.connect(
f"Server={server};Database={db};"
f"Authentication=ActiveDirectoryMSI;UID={readonly_client_id};"
f"Encrypt=yes;ApplicationIntent=ReadOnly;"
)
def get_readwrite_connection():
"""Connection for write operations."""
return mssql_python.connect(
f"Server={server};Database={db};"
f"Authentication=ActiveDirectoryMSI;UID={readwrite_client_id};"
f"Encrypt=yes;"
)
Håll hemligheter för programroller utanför källkodshanteringen
Lösenord för applikationsroller är fortfarande hemligheter. Lagra dem i ett valv eller en hemlighetsinjicerad miljövariabel och rotera dem med samma omsorg som med andra inloggningsuppgifter.
def execute_with_role(cursor, role: str, query: str, params: dict):
"""Execute query with specific application role."""
# Activate application role
cursor.execute(
"EXECUTE sp_setapprole @rolename = %(role)s, @password = %(pwd)s",
{"role": role, "pwd": os.environ[f"ROLE_{role.upper()}_PWD"]}
)
try:
cursor.execute(query, params)
return cursor.fetchall()
finally:
# Reset to original context
cursor.execute("EXECUTE sp_unsetapprole")
Granskning och loggning
Logga säkerhetshändelser
Logga inte känslig data, men logga säkerhetsrelevanta händelser som misslyckade anslutningar, behörighetsfel och misstänkta frågor.
import logging
logger = logging.getLogger("db_security")
def secure_connect(connection_string: str):
"""Connect with security logging."""
logger.info("Attempting database connection")
try:
conn = mssql_python.connect(connection_string)
logger.info("Database connection established")
return conn
except mssql_python.OperationalError as e:
logger.warning(f"Database connection failed: {type(e).__name__}")
raise
Granska känsliga operationer
Konfigurera databasgranskning för känsliga tabeller och operationer. Du kan också implementera applikationsnivågranskning för kritiska åtgärder.
def audit_data_access(cursor, user_id: str, action: str, resource: str):
"""Log data access for audit trail."""
cursor.execute("""
INSERT INTO AuditLog (UserID, Action, Resource, Timestamp, IPAddress)
VALUES (%(user)s, %(action)s, %(resource)s, GETUTCDATE(), %(ip)s)
""", {
"user": user_id,
"action": action,
"resource": resource,
"ip": get_client_ip()
})
Logga aldrig känslig data
Inkludera inte parametervärden i loggmeddelanden. Logga operationen, inte datan.
Undvik – läcker värdet in i loggen:
nid = "295847284"
logger.debug(f"Query: SELECT * FROM HumanResources.Employee WHERE NationalIDNumber = '{nid}'")
Rekommenderat – loggar avsikt utan att exponera värden:
logger.debug("Executing employee lookup query")
nid = "295847284"
cursor.execute("SELECT * FROM HumanResources.Employee WHERE NationalIDNumber = %(nid)s", {"nid": nid})
Felhantering
Avslöja inte interna detaljer
Logga detaljerade fel internt men returnera generiska meddelanden till användarna för att undvika läcka av databasstrukturen:
def safe_query(cursor, query: str, params: dict):
"""Execute query with safe error handling."""
try:
cursor.execute(query, params)
return cursor.fetchall()
except mssql_python.ProgrammingError as e:
# Log full error internally
logging.error(f"Query error: {e}")
# Return generic error to user
raise UserFacingError("An error occurred processing your request")
except mssql_python.IntegrityError:
raise UserFacingError("Invalid data provided")
Sanera felmeddelanden
Kartlägg databasens undantag för användarvänliga meddelanden som inte avslöjar implementeringsdetaljer:
class UserFacingError(Exception):
"""Exception safe to show to users."""
pass
def handle_database_error(error: Exception) -> str:
"""Convert database errors to safe user messages."""
if isinstance(error, mssql_python.IntegrityError):
if "UNIQUE" in str(error):
return "A record with this value already exists"
if "FOREIGN KEY" in str(error):
return "Referenced item not found"
return "An error occurred. Please try again later."
Checklista för säkerhet
Checklista för anslutningssäkerhet
- [ ] Använd Microsoft Entra-autentisering när det är möjligt.
- [ ] Aktivera kryptering (
Encrypt=yes). - [ ] Validera servercertifikat.
- [ ] Förvara inloggningsuppgifter i ett säkert valv.
- [ ] Behåll
.env-filer enbart lokalt, och tillför hemligheter via målplattformen i delade miljöer. - [ ] Använd TDS 8.0 strict mode för Azure SQL.
Söksäkerhet
- [ ] Använd alltid parametriserade frågor.
- [ ] Validera dynamiska identifierare.
- [ ] Använd lagrade procedurer för komplex logik.
- [ ] Begränsa storleken på frågeresultat.
Datasäkerhet
- [ ] Använd servernivå-dataskydd (maskering, radnivåsäkerhet).
- [ ] Använd säkerhet på radnivå där det är lämpligt.
- [ ] Maskera känslig data i loggar.
Åtkomstkontroll
- [ ] Använd principen om minsta privilegium.
- [ ] Separata läs-/skrivkonton.
- [ ] Granska regelbundet tillstånd.
- [ ] Implementera tidsgränser för anslutningar.
Övervakning
- [ ] Logga säkerhetshändelser.
- [ ] Övervaka för avvikelser.
- [ ] Sätt upp varningar vid fel.
- [ ] Genomför regelbundna säkerhetskontroller.