Skip to content

Commit 90af115

Browse files
feat(security): enable cookie secure flag, fix redos, add jwt decode warn (#781)
Co-authored-by: Shuni <251468265+shuni-bot[bot]@users.noreply.github.com> Co-authored-by: Omer Cohen <omercnet@users.noreply.github.com>
1 parent de5697a commit 90af115

7 files changed

Lines changed: 39 additions & 5 deletions

File tree

descope/common.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88
DEFAULT_BASE_URL = DEFAULT_URL_PREFIX + "." + DEFAULT_DOMAIN # pragma: no cover
99
DEFAULT_TIMEOUT_SECONDS = 60
1010

11-
PHONE_REGEX = r"""^(?:(?:\(?(?:00|\+)([1-4]\d\d|[1-9]\d?)\)?)?[\-\.\ \\/]?)?((?:\(?\d{1,}\)?[\-\.\ \\/]?){0,})(?:[\-\.\ \\/]?(?:#|ext\.?|extension|x)[\-\.\ \\/]?(\d+))?$"""
11+
# Simple phone validation to prevent ReDoS (catastrophic backtracking)
12+
# Optional leading +, then digits, spaces, hyphens, parentheses, dots, # for extension
13+
# Requires at least 4 consecutive digits, length 7-25, at most one leading +
14+
PHONE_REGEX = r"""^(?=.*\d{4,})\+?[\d\s\-\(\)\.#xX]{6,24}$"""
1215

1316
SESSION_COOKIE_NAME = "DS"
1417
REFRESH_SESSION_COOKIE_NAME = "DSR"

descope/descope_client.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
import os
4+
import warnings
45
from typing import Iterable
56

67
import requests
@@ -50,6 +51,17 @@ def __init__(
5051
),
5152
)
5253

54+
# Warn about TLS verification bypass
55+
if skip_verify:
56+
warnings.warn(
57+
"⚠️ SECURITY WARNING: TLS certificate verification is DISABLED (skip_verify=True). "
58+
"This makes your application vulnerable to man-in-the-middle attacks. "
59+
"ONLY use this for local development with self-signed certificates. "
60+
"NEVER use skip_verify=True in production environments.",
61+
category=UserWarning,
62+
stacklevel=2,
63+
)
64+
5365
# Auth Initialization
5466
auth_http_client = HTTPClient(
5567
project_id=project_id,

descope/flask/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def set_cookie_on_response(response: Response, token: dict, cookie_data: dict):
3434
expires=cookie_data.get("exp", expire_time),
3535
path=cookie_data.get("path", "/"),
3636
domain=cookie_domain,
37-
secure=False, # True
37+
secure=True, # Cookies must be sent over HTTPS only
3838
httponly=True,
3939
samesite="Strict", # "Strict", "Lax", "None"
4040
)

descope/jwt_common.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import warnings
34
from typing import Callable, Iterable, Optional
45

56
import jwt
@@ -71,9 +72,20 @@ def decode_token_unverified(
7172
) -> dict:
7273
"""Decode a JWT without verifying signature (used when no validator is provided).
7374
75+
WARNING: This function does NOT verify JWT signatures and should NEVER be used
76+
for authentication or authorization decisions. It is only intended for testing
77+
or scenarios where token validation happens elsewhere.
78+
7479
Audience verification is disabled by default since no key is provided.
7580
Returns an empty dict if decoding fails.
7681
"""
82+
warnings.warn(
83+
"decode_token_unverified() does not verify JWT signatures. "
84+
"Do not use this for authentication or authorization. "
85+
"Use proper token validation with signature verification instead.",
86+
UserWarning,
87+
stacklevel=2,
88+
)
7789
try:
7890
return jwt.decode(
7991
token, options={"verify_signature": False, "verify_aud": False}

samples/otp_web_sample_app.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818

1919
PROJECT_ID = ""
2020

21-
# init the DescopeClient
21+
# SECURITY WARNING: skip_verify=True disables TLS certificate verification
22+
# ONLY use this for local development with self-signed certificates
23+
# NEVER use skip_verify=True in production - remove this parameter for production
2224
descope_client = DescopeClient(PROJECT_ID, skip_verify=True)
2325

2426

samples/password_web_sample_app.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414

1515
PROJECT_ID = ""
1616

17-
# init the DescopeClient
17+
# SECURITY WARNING: skip_verify=True disables TLS certificate verification
18+
# ONLY use this for local development with self-signed certificates
19+
# NEVER use skip_verify=True in production - remove this parameter for production
1820
descope_client = DescopeClient(PROJECT_ID, skip_verify=True)
1921

2022

tests/test_jwt_common.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import unittest
2+
import warnings
23

34
from descope.jwt_common import (
45
COOKIE_DATA_NAME,
@@ -58,7 +59,9 @@ def validator(token: str, audience=None):
5859

5960
def test_decode_token_unverified_handles_garbage(self):
6061
# Invalid token strings should not raise and should return empty dict
61-
assert decode_token_unverified("not-a-jwt") == {}
62+
with warnings.catch_warnings():
63+
warnings.simplefilter("ignore", UserWarning)
64+
assert decode_token_unverified("not-a-jwt") == {}
6265

6366

6467
if __name__ == "__main__":

0 commit comments

Comments
 (0)