Skip to content

Commit 46c6b5c

Browse files
SNOW-1825610: Initial OCSP Deprecation (#2165)
1 parent d84edc8 commit 46c6b5c

File tree

6 files changed

+151
-40
lines changed

6 files changed

+151
-40
lines changed

DESCRIPTION.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Source code is also available at: https://github.com/snowflakedb/snowflake-conne
2020
- Fixed a bug where file permission check happened on Windows.
2121
- Added support for File types.
2222
- Added `unsafe_file_write` connection parameter that restores the previous behaviour of saving files downloaded with GET with 644 permissions.
23+
- Deprecated `insecure_mode` connection property and replaced it with `disable_ocsp_checks` with the same behavior as the former property.
2324

2425
- v3.13.2(January 29, 2025)
2526
- Changed not to use scoped temporary objects.

src/snowflake/connector/connection.py

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ def _get_private_bytes_from_file(
204204
# add the new client type to the server to support these features.
205205
"internal_application_name": (CLIENT_NAME, (type(None), str)),
206206
"internal_application_version": (CLIENT_VERSION, (type(None), str)),
207-
"insecure_mode": (False, bool), # Error security fix requirement
207+
"disable_ocsp_checks": (False, bool),
208208
"ocsp_fail_open": (True, bool), # fail open on ocsp issues, default true
209209
"inject_client_pause": (0, int), # snowflake internal
210210
"session_parameters": (None, (type(None), dict)), # snowflake session parameters
@@ -329,8 +329,10 @@ class SnowflakeConnection:
329329
Use connect(..) to get the object.
330330
331331
Attributes:
332-
insecure_mode: Whether or not the connection is in insecure mode. Insecure mode means that the connection
333-
validates the TLS certificate but doesn't check revocation status.
332+
insecure_mode (deprecated): Whether or not the connection is in OCSP disabled mode. It means that the connection
333+
validates the TLS certificate but doesn't check revocation status with OCSP provider.
334+
disable_ocsp_checks: Whether or not the connection is in OCSP disabled mode. It means that the connection
335+
validates the TLS certificate but doesn't check revocation status with OCSP provider.
334336
ocsp_fail_open: Whether or not the connection is in fail open mode. Fail open mode decides if TLS certificates
335337
continue to be validated. Revoked certificates are blocked. Any other exceptions are disregarded.
336338
session_id: The session ID of the connection.
@@ -441,6 +443,25 @@ def __init__(
441443
elif "streamlit" in sys.modules:
442444
kwargs["application"] = "streamlit"
443445

446+
if "insecure_mode" in kwargs:
447+
warn_message = "The 'insecure_mode' connection property is deprecated. Please use 'disable_ocsp_checks' instead"
448+
warnings.warn(
449+
warn_message,
450+
DeprecationWarning,
451+
stacklevel=2,
452+
)
453+
454+
if (
455+
"disable_ocsp_checks" in kwargs
456+
and kwargs["disable_ocsp_checks"] != kwargs["insecure_mode"]
457+
):
458+
logger.warning(
459+
"The values for 'disable_ocsp_checks' and 'insecure_mode' differ. "
460+
"Using the value of 'disable_ocsp_checks."
461+
)
462+
else:
463+
self._disable_ocsp_checks = kwargs["insecure_mode"]
464+
444465
self.converter = None
445466
self.query_context_cache: QueryContextCache | None = None
446467
self.query_context_cache_size = 5
@@ -472,19 +493,23 @@ def __init__(
472493
# check SNOW-1218851 for long term improvement plan to refactor ocsp code
473494
atexit.register(self._close_at_exit)
474495

496+
# Deprecated
475497
@property
476498
def insecure_mode(self) -> bool:
477-
return self._insecure_mode
499+
return self._disable_ocsp_checks
500+
501+
@property
502+
def disable_ocsp_checks(self) -> bool:
503+
return self._disable_ocsp_checks
478504

479505
@property
480506
def ocsp_fail_open(self) -> bool:
481507
return self._ocsp_fail_open
482508

483509
def _ocsp_mode(self) -> OCSPMode:
484-
"""OCSP mode. INSEC
485-
URE, FAIL_OPEN or FAIL_CLOSED."""
486-
if self.insecure_mode:
487-
return OCSPMode.INSECURE
510+
"""OCSP mode. DISABLE_OCSP_CHECKS, FAIL_OPEN or FAIL_CLOSED."""
511+
if self.disable_ocsp_checks:
512+
return OCSPMode.DISABLE_OCSP_CHECKS
488513
elif self.ocsp_fail_open:
489514
return OCSPMode.FAIL_OPEN
490515
else:
@@ -1312,7 +1337,7 @@ def __config(self, **kwargs):
13121337
)
13131338

13141339
if self.ocsp_fail_open:
1315-
logger.info(
1340+
logger.debug(
13161341
"This connection is in OCSP Fail Open Mode. "
13171342
"TLS Certificates would be checked for validity "
13181343
"and revocation status. Any other Certificate "
@@ -1321,12 +1346,10 @@ def __config(self, **kwargs):
13211346
"connectivity."
13221347
)
13231348

1324-
if self.insecure_mode:
1325-
logger.info(
1326-
"THIS CONNECTION IS IN INSECURE MODE. IT "
1327-
"MEANS THE CERTIFICATE WILL BE VALIDATED BUT THE "
1328-
"CERTIFICATE REVOCATION STATUS WILL NOT BE "
1329-
"CHECKED."
1349+
if self.disable_ocsp_checks:
1350+
logger.debug(
1351+
"This connection runs with disabled OCSP checks. "
1352+
"Revocation status of the certificate will not be checked against OCSP Responder."
13301353
)
13311354

13321355
def cmd_query(

src/snowflake/connector/constants.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,12 +357,14 @@ class OCSPMode(Enum):
357357
FAIL_OPEN: A response indicating a revoked certificate results in a failed connection. A response with any
358358
other certificate errors or statuses allows the connection to occur, but denotes the message in the logs
359359
at the WARNING level with the relevant details in JSON format.
360-
INSECURE: The connection will occur anyway.
360+
INSECURE (deprecated): The connection will occur anyway.
361+
DISABLE_OCSP_CHECKS: The OCSP check will not happen. If the certificate is valid then connection will occur.
361362
"""
362363

363364
FAIL_CLOSED = "FAIL_CLOSED"
364365
FAIL_OPEN = "FAIL_OPEN"
365366
INSECURE = "INSECURE"
367+
DISABLE_OCSP_CHECKS = "DISABLE_OCSP_CHECKS"
366368

367369

368370
@unique

src/snowflake/connector/ocsp_snowflake.py

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ def __init__(self) -> None:
331331
self.cache_enabled = False
332332
self.cache_hit = False
333333
self.fail_open = False
334-
self.insecure_mode = False
334+
self.disable_ocsp_checks = False
335335

336336
def set_event_sub_type(self, event_sub_type: str) -> None:
337337
"""
@@ -380,8 +380,12 @@ def set_cache_hit(self, cache_hit) -> None:
380380
def set_fail_open(self, fail_open) -> None:
381381
self.fail_open = fail_open
382382

383+
# Deprecated
383384
def set_insecure_mode(self, insecure_mode) -> None:
384-
self.insecure_mode = insecure_mode
385+
self.disable_ocsp_checks = insecure_mode
386+
387+
def set_disable_ocsp_checks(self, disable_ocsp_checks) -> None:
388+
self.disable_ocsp_checks = disable_ocsp_checks
385389

386390
def generate_telemetry_data(
387391
self, event_type: str, urgent: bool = False
@@ -396,7 +400,7 @@ def generate_telemetry_data(
396400
TelemetryField.KEY_OOB_OCSP_REQUEST_BASE64.value: self.ocsp_req,
397401
TelemetryField.KEY_OOB_OCSP_RESPONDER_URL.value: self.ocsp_url,
398402
TelemetryField.KEY_OOB_ERROR_MESSAGE.value: self.error_msg,
399-
TelemetryField.KEY_OOB_INSECURE_MODE.value: self.insecure_mode,
403+
TelemetryField.KEY_OOB_INSECURE_MODE.value: self.disable_ocsp_checks,
400404
TelemetryField.KEY_OOB_FAIL_OPEN.value: self.fail_open,
401405
TelemetryField.KEY_OOB_CACHE_ENABLED.value: self.cache_enabled,
402406
TelemetryField.KEY_OOB_CACHE_HIT.value: self.cache_hit,
@@ -1091,7 +1095,7 @@ def validate_certfile(self, cert_filename, no_exception: bool = False):
10911095
cert_map = {}
10921096
telemetry_data = OCSPTelemetryData()
10931097
telemetry_data.set_cache_enabled(self.OCSP_CACHE_SERVER.CACHE_SERVER_ENABLED)
1094-
telemetry_data.set_insecure_mode(False)
1098+
telemetry_data.set_disable_ocsp_checks(False)
10951099
telemetry_data.set_sfc_peer_host(cert_filename)
10961100
telemetry_data.set_fail_open(self.is_enabled_fail_open())
10971101
try:
@@ -1137,7 +1141,7 @@ def validate(
11371141

11381142
telemetry_data = OCSPTelemetryData()
11391143
telemetry_data.set_cache_enabled(self.OCSP_CACHE_SERVER.CACHE_SERVER_ENABLED)
1140-
telemetry_data.set_insecure_mode(False)
1144+
telemetry_data.set_disable_ocsp_checks(False)
11411145
telemetry_data.set_sfc_peer_host(hostname)
11421146
telemetry_data.set_fail_open(self.is_enabled_fail_open())
11431147

@@ -1224,15 +1228,10 @@ def is_enabled_fail_open(self) -> bool:
12241228
return self.FAIL_OPEN
12251229

12261230
@staticmethod
1227-
def print_fail_open_warning(ocsp_log) -> None:
1228-
static_warning = (
1229-
"WARNING!!! Using fail-open to connect. Driver is connecting to an "
1230-
"HTTPS endpoint without OCSP based Certificate Revocation checking "
1231-
"as it could not obtain a valid OCSP Response to use from the CA OCSP "
1232-
"responder. Details:"
1233-
)
1234-
ocsp_warning = f"{static_warning} \n {ocsp_log}"
1235-
logger.warning(ocsp_warning)
1231+
def print_fail_open_debug(ocsp_log) -> None:
1232+
static_debug = "OCSP responder didn't respond correctly. Assuming certificate is not revoked. Details: "
1233+
ocsp_debug = f"{static_debug} \n {ocsp_log}"
1234+
logger.debug(ocsp_debug)
12361235

12371236
def validate_by_direct_connection(
12381237
self,
@@ -1320,7 +1319,7 @@ def verify_fail_open(self, ex_obj, telemetry_data):
13201319
)
13211320
return ex_obj
13221321
else:
1323-
SnowflakeOCSP.print_fail_open_warning(
1322+
SnowflakeOCSP.print_fail_open_debug(
13241323
telemetry_data.generate_telemetry_data("RevocationCheckFailure")
13251324
)
13261325
return None

src/snowflake/connector/ssl_wrap_socket.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ def ssl_wrap_socket_with_ocsp(*args: Any, **kwargs: Any) -> WrappedSocket:
8181
FEATURE_OCSP_MODE.name,
8282
FEATURE_OCSP_RESPONSE_CACHE_FILE_NAME,
8383
)
84-
if FEATURE_OCSP_MODE != OCSPMode.INSECURE:
84+
if FEATURE_OCSP_MODE != OCSPMode.DISABLE_OCSP_CHECKS:
8585
from .ocsp_asn1crypto import SnowflakeOCSPAsn1Crypto as SFOCSP
8686

8787
v = SFOCSP(
@@ -98,11 +98,9 @@ def ssl_wrap_socket_with_ocsp(*args: Any, **kwargs: Any) -> WrappedSocket:
9898
errno=ER_OCSP_RESPONSE_CERT_STATUS_REVOKED,
9999
)
100100
else:
101-
log.info(
102-
"THIS CONNECTION IS IN INSECURE "
103-
"MODE. IT MEANS THE CERTIFICATE WILL BE "
104-
"VALIDATED BUT THE CERTIFICATE REVOCATION "
105-
"STATUS WILL NOT BE CHECKED."
101+
log.debug(
102+
"This connection does not perform OCSP checks. "
103+
"Revocation status of the certificate will not be checked against OCSP Responder."
106104
)
107105

108106
return ret

test/integ/test_connection.py

Lines changed: 91 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ def test_bogus(db_parameters):
325325
host=db_parameters["host"],
326326
port=db_parameters["port"],
327327
login_timeout=5,
328-
insecure_mode=True,
328+
disable_ocsp_checks=True,
329329
)
330330

331331
with pytest.raises(DatabaseError):
@@ -1371,9 +1371,9 @@ def test_server_session_keep_alive(conn_cnx):
13711371

13721372

13731373
@pytest.mark.skipolddriver
1374-
def test_ocsp_mode_insecure(conn_cnx, is_public_test, caplog):
1374+
def test_ocsp_mode_disable_ocsp_checks(conn_cnx, is_public_test, caplog):
13751375
caplog.set_level(logging.DEBUG, "snowflake.connector.ocsp_snowflake")
1376-
with conn_cnx(insecure_mode=True) as conn, conn.cursor() as cur:
1376+
with conn_cnx(disable_ocsp_checks=True) as conn, conn.cursor() as cur:
13771377
assert cur.execute("select 1").fetchall() == [(1,)]
13781378
assert "snowflake.connector.ocsp_snowflake" not in caplog.text
13791379
caplog.clear()
@@ -1382,10 +1382,98 @@ def test_ocsp_mode_insecure(conn_cnx, is_public_test, caplog):
13821382
assert cur.execute("select 1").fetchall() == [(1,)]
13831383
if is_public_test:
13841384
assert "snowflake.connector.ocsp_snowflake" in caplog.text
1385+
assert "This connection does not perform OCSP checks." not in caplog.text
1386+
else:
1387+
assert "snowflake.connector.ocsp_snowflake" not in caplog.text
1388+
1389+
1390+
@pytest.mark.skipolddriver
1391+
def test_ocsp_mode_insecure_mode(conn_cnx, is_public_test, caplog):
1392+
caplog.set_level(logging.DEBUG, "snowflake.connector.ocsp_snowflake")
1393+
with conn_cnx(insecure_mode=True) as conn, conn.cursor() as cur:
1394+
assert cur.execute("select 1").fetchall() == [(1,)]
1395+
if is_public_test:
1396+
assert "snowflake.connector.ocsp_snowflake" not in caplog.text
1397+
assert "This connection does not perform OCSP checks." in caplog.text
1398+
else:
1399+
assert "snowflake.connector.ocsp_snowflake" not in caplog.text
1400+
1401+
1402+
@pytest.mark.skipolddriver
1403+
def test_ocsp_mode_insecure_mode_and_disable_ocsp_checks_match(
1404+
conn_cnx, is_public_test, caplog
1405+
):
1406+
caplog.set_level(logging.DEBUG, "snowflake.connector.ocsp_snowflake")
1407+
with conn_cnx(
1408+
insecure_mode=True, disable_ocsp_checks=True
1409+
) as conn, conn.cursor() as cur:
1410+
assert cur.execute("select 1").fetchall() == [(1,)]
1411+
if is_public_test:
1412+
assert "snowflake.connector.ocsp_snowflake" not in caplog.text
1413+
assert (
1414+
"The values for 'disable_ocsp_checks' and 'insecure_mode' differ. "
1415+
"Using the value of 'disable_ocsp_checks."
1416+
) not in caplog.text
1417+
assert "This connection does not perform OCSP checks." in caplog.text
1418+
else:
1419+
assert "snowflake.connector.ocsp_snowflake" not in caplog.text
1420+
1421+
1422+
@pytest.mark.skipolddriver
1423+
def test_ocsp_mode_insecure_mode_and_disable_ocsp_checks_mismatch_ocsp_disabled(
1424+
conn_cnx, is_public_test, caplog
1425+
):
1426+
caplog.set_level(logging.DEBUG, "snowflake.connector.ocsp_snowflake")
1427+
with conn_cnx(
1428+
insecure_mode=False, disable_ocsp_checks=True
1429+
) as conn, conn.cursor() as cur:
1430+
assert cur.execute("select 1").fetchall() == [(1,)]
1431+
if is_public_test:
1432+
assert "snowflake.connector.ocsp_snowflake" not in caplog.text
1433+
assert (
1434+
"The values for 'disable_ocsp_checks' and 'insecure_mode' differ. "
1435+
"Using the value of 'disable_ocsp_checks."
1436+
) in caplog.text
1437+
assert "This connection does not perform OCSP checks." in caplog.text
1438+
else:
1439+
assert "snowflake.connector.ocsp_snowflake" not in caplog.text
1440+
1441+
1442+
@pytest.mark.skipolddriver
1443+
def test_ocsp_mode_insecure_mode_and_disable_ocsp_checks_mismatch_ocsp_enabled(
1444+
conn_cnx, is_public_test, caplog
1445+
):
1446+
caplog.set_level(logging.DEBUG, "snowflake.connector.ocsp_snowflake")
1447+
with conn_cnx(
1448+
insecure_mode=True, disable_ocsp_checks=False
1449+
) as conn, conn.cursor() as cur:
1450+
assert cur.execute("select 1").fetchall() == [(1,)]
1451+
if is_public_test:
1452+
assert "snowflake.connector.ocsp_snowflake" in caplog.text
1453+
assert (
1454+
"The values for 'disable_ocsp_checks' and 'insecure_mode' differ. "
1455+
"Using the value of 'disable_ocsp_checks."
1456+
) in caplog.text
1457+
assert "This connection does not perform OCSP checks." not in caplog.text
13851458
else:
13861459
assert "snowflake.connector.ocsp_snowflake" not in caplog.text
13871460

13881461

1462+
@pytest.mark.skipolddriver
1463+
def test_ocsp_mode_insecure_mode_deprecation_warning(conn_cnx):
1464+
with warnings.catch_warnings(record=True) as w:
1465+
warnings.simplefilter("ignore")
1466+
warnings.filterwarnings(
1467+
"always", category=DeprecationWarning, message=".*insecure_mode"
1468+
)
1469+
with conn_cnx(insecure_mode=True):
1470+
assert len(w) == 1
1471+
assert issubclass(w[0].category, DeprecationWarning)
1472+
assert "The 'insecure_mode' connection property is deprecated." in str(
1473+
w[0].message
1474+
)
1475+
1476+
13891477
@pytest.mark.skipolddriver
13901478
def test_connection_atexit_close(conn_cnx):
13911479
"""Basic Connection test without schema."""

0 commit comments

Comments
 (0)