Summary
The current connection pool in mssql-python keys only on the sanitized connection string. It does not account for the identity/security context of the user. This means different principals connecting to the same Server/Database can collide in the same pool bucket — a silent privilege escalation risk.
As a stopgap, PR #603 (token_provider=) disables pooling for all access-token connections. This issue tracks the proper fix: extending the pool key to include identity information so pooling can remain enabled safely.
Problem
The native pool (ddbc_bindings) receives (connection_string, pooling_bool, attrs_before) but only the connection string is used as the pool key. The access token lives in attrs_before[SQL_COPT_SS_ACCESS_TOKEN] and is invisible to the pool. Two users authenticating with different tokens to the same server/database get the same pool bucket.
Auth paths affected
| Auth path |
Identity visible to pool? |
Risk |
token_provider= (custom credential) |
❌ No |
Different principals share pool bucket |
Authentication=ActiveDirectoryDefault |
❌ No |
Same — token injected via attrs_before |
Authentication=ActiveDirectoryInteractive |
❌ No |
Same |
Authentication=ActiveDirectoryDeviceCode |
❌ No |
Same |
Authentication=ActiveDirectoryServicePrincipal |
❌ No |
Same (bulk copy path uses raw token) |
| Windows Integrated Auth (NTLM/Kerberos) |
❌ No |
SSPI token negotiated by driver, not exposed to pool key |
| SQL Auth (UID/PWD) |
✅ Yes |
Credentials are in the connection string (pool key) |
Proposed solution
Extend the native pool key to include an identity discriminator alongside the connection string. Options to explore:
-
For token_provider=: Use id(token_provider) (object identity) as a pool key component. Same credential instance → same pool bucket. New instance → new bucket. This aligns with the principle that a new security context = a new provider instance.
-
For built-in Authentication=ActiveDirectory*: Derive a discriminator from the credential type + principal (e.g., hash of auth type + UID if present).
-
For Windows Integrated Auth (NTLM/Kerberos): The SSPI token is negotiated internally by the driver and not exposed to the Python layer. Needs investigation into whether the native layer can extract the principal identity for the pool key.
Cross-driver reference
| Driver |
Pool identity separation? |
How |
| JDBC (mssql-jdbc) |
✅ Yes |
Includes access token in pool key |
| PHP |
✅ Yes |
Separates pools based on access token |
| pyodbc |
❌ No |
Recommends disabling pooling for token auth |
Current workaround
PR #603 disables pooling (self._pooling = False) whenever SQL_COPT_SS_ACCESS_TOKEN is present in attrs_before. This covers token_provider=, built-in Authentication=ActiveDirectory*, and raw attrs_before token paths. Safe but loses pooling benefits.
Additional deliverable
Document which auth paths have identity-aware pooling vs. which don't — either in the docs or a wiki page — so users understand the current limitations.
Related
Summary
The current connection pool in mssql-python keys only on the sanitized connection string. It does not account for the identity/security context of the user. This means different principals connecting to the same Server/Database can collide in the same pool bucket — a silent privilege escalation risk.
As a stopgap, PR #603 (
token_provider=) disables pooling for all access-token connections. This issue tracks the proper fix: extending the pool key to include identity information so pooling can remain enabled safely.Problem
The native pool (
ddbc_bindings) receives(connection_string, pooling_bool, attrs_before)but only the connection string is used as the pool key. The access token lives inattrs_before[SQL_COPT_SS_ACCESS_TOKEN]and is invisible to the pool. Two users authenticating with different tokens to the same server/database get the same pool bucket.Auth paths affected
token_provider=(custom credential)Authentication=ActiveDirectoryDefaultAuthentication=ActiveDirectoryInteractiveAuthentication=ActiveDirectoryDeviceCodeAuthentication=ActiveDirectoryServicePrincipalProposed solution
Extend the native pool key to include an identity discriminator alongside the connection string. Options to explore:
For
token_provider=: Useid(token_provider)(object identity) as a pool key component. Same credential instance → same pool bucket. New instance → new bucket. This aligns with the principle that a new security context = a new provider instance.For built-in
Authentication=ActiveDirectory*: Derive a discriminator from the credential type + principal (e.g., hash of auth type + UID if present).For Windows Integrated Auth (NTLM/Kerberos): The SSPI token is negotiated internally by the driver and not exposed to the Python layer. Needs investigation into whether the native layer can extract the principal identity for the pool key.
Cross-driver reference
Current workaround
PR #603 disables pooling (
self._pooling = False) wheneverSQL_COPT_SS_ACCESS_TOKENis present inattrs_before. This coverstoken_provider=, built-inAuthentication=ActiveDirectory*, and rawattrs_beforetoken paths. Safe but loses pooling benefits.Additional deliverable
Document which auth paths have identity-aware pooling vs. which don't — either in the docs or a wiki page — so users understand the current limitations.
Related
token_provider=implementation (disables pooling as stopgap)token_providerfeature request