Skip to content

Commit f31fa72

Browse files
enchance signature
1 parent 8f24789 commit f31fa72

File tree

2 files changed

+47
-26
lines changed

2 files changed

+47
-26
lines changed

hubspot/utils/signature.py

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,25 @@
22
import hmac
33
import hashlib
44

5-
from datetime import datetime
5+
from datetime import datetime, UTC, timedelta
66

77
from hubspot.exceptions import InvalidSignatureVersionError, InvalidSignatureTimestampError
88

99

1010
class Signature:
11-
MAX_ALLOWED_TIMESTAMP = 3000
11+
MAX_ALLOWED_TIMESTAMP = 300
12+
13+
@staticmethod
14+
def _is_timestamp_valid(timestamp: str) -> bool:
15+
if timestamp is None:
16+
return False
17+
try:
18+
timestamp_float = float(timestamp)
19+
request_time = datetime.fromtimestamp(timestamp_float // 1000, tz=UTC)
20+
current_time = datetime.now(UTC)
21+
return current_time - request_time < timedelta(seconds=Signature.MAX_ALLOWED_TIMESTAMP)
22+
except (ValueError, OverflowError):
23+
return False
1224

1325
@staticmethod
1426
def is_valid(
@@ -18,12 +30,12 @@ def is_valid(
1830
http_uri: str = None,
1931
http_method: str = "POST",
2032
signature_version: str = "v2",
21-
timestamp: float = None
33+
timestamp: str = None
2234
) -> bool:
2335
if signature_version == "v3":
24-
current_time = datetime.now()
25-
if timestamp is None or current_time.timestamp() - timestamp > Signature.MAX_ALLOWED_TIMESTAMP:
36+
if timestamp is None or not Signature._is_timestamp_valid(timestamp):
2637
raise InvalidSignatureTimestampError(timestamp=timestamp)
38+
2739
hashed_signature = Signature.get_signature(
2840
client_secret,
2941
request_body,
@@ -33,7 +45,7 @@ def is_valid(
3345
timestamp
3446
)
3547

36-
return signature == hashed_signature
48+
return hmac.compare_digest(hashed_signature, signature)
3749

3850
@staticmethod
3951
def get_signature(
@@ -42,7 +54,7 @@ def get_signature(
4254
signature_version: str,
4355
http_uri: str = None,
4456
http_method: str = "POST",
45-
timestamp: float = None,
57+
timestamp: str = None
4658
) -> str:
4759
if signature_version == "v1":
4860
source_string = f"{client_secret}{request_body}"
@@ -54,10 +66,10 @@ def get_signature(
5466
source_string = f"{http_method}{http_uri}{request_body}{timestamp}"
5567
hashed_signature = base64.b64encode(
5668
hmac.new(
57-
client_secret.encode("utf-8"),
58-
msg=source_string.encode("utf-8"),
59-
digestmod=hashlib.sha256
60-
).digest()
69+
client_secret.encode("utf-8"),
70+
msg=source_string.encode("utf-8"),
71+
digestmod=hashlib.sha256
72+
).digest()
6173
).decode()
6274
return hashed_signature
6375
else:

tests/spec/utils/test_signature.py

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import pytest
2-
32
from datetime import datetime
4-
53
from hubspot.exceptions import InvalidSignatureVersionError, InvalidSignatureTimestampError
64
from hubspot.utils.signature import Signature
75

@@ -11,7 +9,7 @@
119
"request_body": "{'example_field':'example_value'}",
1210
"url": "https://www.example.com/webhook_uri",
1311
"http_method": "POST",
14-
"timestamp": 15000000,
12+
"timestamp": str(int(datetime.now().timestamp() * 1000)),
1513
}
1614

1715

@@ -48,10 +46,12 @@ def test_get_signature__v2():
4846

4947
def test_get_signature__v3():
5048
data = {
51-
"signature": "HPW73RUtKmcYoEDADG0s6MmGFWUzWJKAW07r8RDgcQw=",
52-
"signature_version": "v3"
53-
}
49+
"signature": "K36dawei4A+QBNolUOqo7s91KQDWQ5MXZ/QufNYuk/Y=",
50+
"signature_version": "v3",
51+
}
52+
5453
data.update(TEST_DATA)
54+
data["timestamp"] = "1693657560000"
5555

5656
signature = Signature.get_signature(
5757
data["client_secret"],
@@ -66,13 +66,11 @@ def test_get_signature__v3():
6666

6767

6868
def test_get_signature__wrong_version():
69-
7069
with pytest.raises(InvalidSignatureVersionError):
7170
Signature.get_signature(
7271
TEST_DATA["client_secret"],
7372
TEST_DATA["request_body"],
7473
"wrong_signature_version"
75-
7674
)
7775

7876

@@ -84,7 +82,6 @@ def test_is_valid__v1():
8482
TEST_DATA["client_secret"],
8583
TEST_DATA["request_body"],
8684
signature_version="v1"
87-
8885
)
8986

9087
assert result
@@ -120,13 +117,12 @@ def test_is_valid__v2_get_method():
120117

121118

122119
def test_is_valid__v3():
123-
timestamp = datetime.now().timestamp()
124120
signature = Signature.get_signature(
125121
TEST_DATA["client_secret"],
126122
TEST_DATA["request_body"],
127123
signature_version="v3",
128124
http_uri=TEST_DATA["url"],
129-
timestamp=timestamp
125+
timestamp=TEST_DATA["timestamp"]
130126
)
131127

132128
result = Signature.is_valid(
@@ -135,7 +131,7 @@ def test_is_valid__v3():
135131
TEST_DATA["request_body"],
136132
signature_version="v3",
137133
http_uri=TEST_DATA["url"],
138-
timestamp=timestamp
134+
timestamp=TEST_DATA["timestamp"]
139135
)
140136

141137
assert result
@@ -161,13 +157,14 @@ def test_is_valid__none_timestamp():
161157

162158

163159
def test_is_valid__expired_timestamp():
164-
timestamp = datetime.now().timestamp()
160+
expired_timestamp = str(int((datetime.now().timestamp() - Signature.MAX_ALLOWED_TIMESTAMP - 1) * 1000))
161+
165162
signature = Signature.get_signature(
166163
TEST_DATA["client_secret"],
167164
TEST_DATA["request_body"],
168165
signature_version="v3",
169166
http_uri=TEST_DATA["url"],
170-
timestamp=timestamp
167+
timestamp=expired_timestamp
171168
)
172169

173170
with pytest.raises(InvalidSignatureTimestampError):
@@ -177,5 +174,17 @@ def test_is_valid__expired_timestamp():
177174
TEST_DATA["request_body"],
178175
signature_version="v3",
179176
http_uri=TEST_DATA["url"],
180-
timestamp=timestamp - Signature.MAX_ALLOWED_TIMESTAMP
177+
timestamp=expired_timestamp
181178
)
179+
180+
181+
def test_is_timestamp_valid__valid_timestamp():
182+
current_timestamp = str(int(datetime.now().timestamp() * 1000))
183+
184+
assert Signature._is_timestamp_valid(current_timestamp) is True
185+
186+
187+
def test_is_timestamp_valid__expired_timestamp():
188+
expired_timestamp = str(int((datetime.now().timestamp() - Signature.MAX_ALLOWED_TIMESTAMP - 10) * 1000))
189+
190+
assert Signature._is_timestamp_valid(expired_timestamp) is False

0 commit comments

Comments
 (0)