Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 2844ce9

Browse filesBrowse files
fix: Accepting time-zone aware expiration datetime only to prevent hard-to debug issues with invalid signatures (GoogleCloudPlatform#12704)
* Accepting time-zone aware expiration datetime only to prevent hard-to debug issues with invalid signatures. * Applying code review suggestion * Fixing linter issues
1 parent 6f7c2d8 commit 2844ce9
Copy full SHA for 2844ce9

File tree

Expand file treeCollapse file tree

2 files changed

+73
-18
lines changed
Filter options
Expand file treeCollapse file tree

2 files changed

+73
-18
lines changed

‎cdn/snippets.py

Copy file name to clipboardExpand all lines: cdn/snippets.py
+10-10Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
# [START cloudcdn_sign_cookie]
2727
import argparse
2828
import base64
29-
from datetime import datetime
29+
from datetime import datetime, timezone
3030
import hashlib
3131
import hmac
3232
from urllib.parse import parse_qs, urlsplit
@@ -48,7 +48,7 @@ def sign_url(
4848
url: URL to sign.
4949
key_name: name of the signing key.
5050
base64_key: signing key as a base64 encoded string.
51-
expiration_time: expiration time.
51+
expiration_time: expiration time as time-zone aware datetime.
5252
5353
Returns:
5454
Returns the Signed URL appended with the query parameters based on the
@@ -57,7 +57,7 @@ def sign_url(
5757
stripped_url = url.strip()
5858
parsed_url = urlsplit(stripped_url)
5959
query_params = parse_qs(parsed_url.query, keep_blank_values=True)
60-
epoch = datetime.utcfromtimestamp(0)
60+
epoch = datetime.fromtimestamp(0, timezone.utc)
6161
expiration_timestamp = int((expiration_time - epoch).total_seconds())
6262
decoded_key = base64.urlsafe_b64decode(base64_key)
6363

@@ -87,7 +87,7 @@ def sign_url_prefix(
8787
url_prefix: URL prefix to sign.
8888
key_name: name of the signing key.
8989
base64_key: signing key as a base64 encoded string.
90-
expiration_time: expiration time.
90+
expiration_time: expiration time as time-zone aware datetime.
9191
9292
Returns:
9393
Returns the Signed URL appended with the query parameters based on the
@@ -99,7 +99,7 @@ def sign_url_prefix(
9999
encoded_url_prefix = base64.urlsafe_b64encode(
100100
url_prefix.strip().encode("utf-8")
101101
).decode("utf-8")
102-
epoch = datetime.utcfromtimestamp(0)
102+
epoch = datetime.fromtimestamp(0, timezone.utc)
103103
expiration_timestamp = int((expiration_time - epoch).total_seconds())
104104
decoded_key = base64.urlsafe_b64decode(base64_key)
105105

@@ -127,15 +127,15 @@ def sign_cookie(
127127
url_prefix: URL prefix to sign.
128128
key_name: name of the signing key.
129129
base64_key: signing key as a base64 encoded string.
130-
expiration_time: expiration time.
130+
expiration_time: expiration time as time-zone aware datetime.
131131
132132
Returns:
133133
Returns the Cloud-CDN-Cookie value based on the specified configuration.
134134
"""
135135
encoded_url_prefix = base64.urlsafe_b64encode(
136136
url_prefix.strip().encode("utf-8")
137137
).decode("utf-8")
138-
epoch = datetime.utcfromtimestamp(0)
138+
epoch = datetime.fromtimestamp(0, timezone.utc)
139139
expiration_timestamp = int((expiration_time - epoch).total_seconds())
140140
decoded_key = base64.urlsafe_b64decode(base64_key)
141141

@@ -167,7 +167,7 @@ def sign_cookie(
167167
sign_url_parser.add_argument("base64_key", help="The base64 encoded signing key.")
168168
sign_url_parser.add_argument(
169169
"expiration_time",
170-
type=lambda d: datetime.utcfromtimestamp(float(d)),
170+
type=lambda d: datetime.fromtimestamp(float(d), timezone.utc),
171171
help="Expiration time expessed as seconds since the epoch.",
172172
)
173173

@@ -185,7 +185,7 @@ def sign_cookie(
185185
)
186186
sign_url_prefix_parser.add_argument(
187187
"expiration_time",
188-
type=lambda d: datetime.utcfromtimestamp(float(d)),
188+
type=lambda d: datetime.fromtimestamp(float(d), timezone.utc),
189189
help="Expiration time expessed as seconds since the epoch.",
190190
)
191191

@@ -200,7 +200,7 @@ def sign_cookie(
200200
)
201201
sign_cookie_parser.add_argument(
202202
"expiration_time",
203-
type=lambda d: datetime.utcfromtimestamp(float(d)),
203+
type=lambda d: datetime.fromtimestamp(float(d), timezone.utc),
204204
help="Expiration time expressed as seconds since the epoch.",
205205
)
206206

‎cdn/snippets_test.py

Copy file name to clipboardExpand all lines: cdn/snippets_test.py
+63-8Lines changed: 63 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
import datetime
2020

21+
import pytest
22+
2123
import snippets
2224

2325

@@ -27,7 +29,7 @@ def test_sign_url() -> None:
2729
"http://35.186.234.33/index.html",
2830
"my-key",
2931
"nZtRohdNF9m3cKM24IcK4w==",
30-
datetime.datetime.utcfromtimestamp(1549751401),
32+
datetime.datetime.fromtimestamp(1549751401, datetime.timezone.utc),
3133
)
3234
== "http://35.186.234.33/index.html?Expires=1549751401&KeyName=my-key&Signature=CRFqQnVfFyiUyR63OQf-HRUpIwc="
3335
)
@@ -37,7 +39,7 @@ def test_sign_url() -> None:
3739
"http://www.example.com/",
3840
"my-key",
3941
"nZtRohdNF9m3cKM24IcK4w==",
40-
datetime.datetime.utcfromtimestamp(1549751401),
42+
datetime.datetime.fromtimestamp(1549751401, datetime.timezone.utc),
4143
)
4244
== "http://www.example.com/?Expires=1549751401&KeyName=my-key&Signature=OqDUFfHpN5Vxga6r80bhsgxKves="
4345
)
@@ -46,19 +48,36 @@ def test_sign_url() -> None:
4648
"http://www.example.com/some/path?some=query&another=param",
4749
"my-key",
4850
"nZtRohdNF9m3cKM24IcK4w==",
49-
datetime.datetime.utcfromtimestamp(1549751401),
51+
datetime.datetime.fromtimestamp(1549751401, datetime.timezone.utc),
5052
)
5153
== "http://www.example.com/some/path?some=query&another=param&Expires=1549751401&KeyName=my-key&Signature=9Q9TCxSju8-W5nUkk5CuTrun2_o="
5254
)
5355

5456

57+
def test_sign_url_raise_exception_on_naive_expiration_datetime() -> None:
58+
with pytest.raises(TypeError):
59+
snippets.sign_url(
60+
"http://www.example.com/some/path?some=query&another=param",
61+
"my-key",
62+
"nZtRohdNF9m3cKM24IcK4w==",
63+
datetime.datetime.fromtimestamp(1549751401),
64+
)
65+
with pytest.raises(TypeError):
66+
snippets.sign_url(
67+
"http://www.example.com/some/path?some=query&another=param",
68+
"my-key",
69+
"nZtRohdNF9m3cKM24IcK4w==",
70+
datetime.datetime.utcfromtimestamp(1549751401),
71+
)
72+
73+
5574
def test_sign_url_prefix() -> None:
5675
assert snippets.sign_url_prefix(
5776
"http://35.186.234.33/index.html",
5877
"http://35.186.234.33/",
5978
"my-key",
6079
"nZtRohdNF9m3cKM24IcK4w==",
61-
datetime.datetime.utcfromtimestamp(1549751401),
80+
datetime.datetime.fromtimestamp(1549751401, datetime.timezone.utc),
6281
) == (
6382
"http://35.186.234.33/index.html?URLPrefix=aHR0cDovLzM1LjE4Ni4yMzQuMzMv&"
6483
"Expires=1549751401&KeyName=my-key&Signature=j7HYgoQ8dIOVsW3Rw4cpkjWfRMA="
@@ -68,7 +87,7 @@ def test_sign_url_prefix() -> None:
6887
"http://www.example.com/",
6988
"my-key",
7089
"nZtRohdNF9m3cKM24IcK4w==",
71-
datetime.datetime.utcfromtimestamp(1549751401),
90+
datetime.datetime.fromtimestamp(1549751401, datetime.timezone.utc),
7291
) == (
7392
"http://www.example.com/?URLPrefix=aHR0cDovL3d3dy5leGFtcGxlLmNvbS8=&"
7493
"Expires=1549751401&KeyName=my-key&Signature=UdT5nVks6Hh8QFMJI9kmXuXYBk0="
@@ -78,21 +97,40 @@ def test_sign_url_prefix() -> None:
7897
"http://www.example.com/some/",
7998
"my-key",
8099
"nZtRohdNF9m3cKM24IcK4w==",
81-
datetime.datetime.utcfromtimestamp(1549751401),
100+
datetime.datetime.fromtimestamp(1549751401, datetime.timezone.utc),
82101
) == (
83102
"http://www.example.com/some/path?some=query&another=param&"
84103
"URLPrefix=aHR0cDovL3d3dy5leGFtcGxlLmNvbS9zb21lLw==&"
85104
"Expires=1549751401&KeyName=my-key&Signature=3th4ThmpS95I1TAKYyYSCSq3dnQ="
86105
)
87106

88107

108+
def test_sign_url_prefix_raise_exception_on_naive_expiration_datetime() -> None:
109+
with pytest.raises(TypeError):
110+
snippets.sign_url_prefix(
111+
"http://www.example.com/some/path?some=query&another=param",
112+
"http://www.example.com/some/",
113+
"my-key",
114+
"nZtRohdNF9m3cKM24IcK4w==",
115+
datetime.datetime.fromtimestamp(1549751401),
116+
)
117+
with pytest.raises(TypeError):
118+
snippets.sign_url_prefix(
119+
"http://www.example.com/some/path?some=query&another=param",
120+
"http://www.example.com/some/",
121+
"my-key",
122+
"nZtRohdNF9m3cKM24IcK4w==",
123+
datetime.datetime.utcfromtimestamp(1549751401),
124+
)
125+
126+
89127
def test_sign_cookie() -> None:
90128
assert (
91129
snippets.sign_cookie(
92130
"http://35.186.234.33/index.html",
93131
"my-key",
94132
"nZtRohdNF9m3cKM24IcK4w==",
95-
datetime.datetime.utcfromtimestamp(1549751401),
133+
datetime.datetime.fromtimestamp(1549751401, datetime.timezone.utc),
96134
)
97135
== "Cloud-CDN-Cookie=URLPrefix=aHR0cDovLzM1LjE4Ni4yMzQuMzMvaW5kZXguaHRtbA==:Expires=1549751401:KeyName=my-key:Signature=uImwlOBCPs91mlCyG9vyyZRrNWU="
98136
)
@@ -102,7 +140,24 @@ def test_sign_cookie() -> None:
102140
"http://www.example.com/foo/",
103141
"my-key",
104142
"nZtRohdNF9m3cKM24IcK4w==",
105-
datetime.datetime.utcfromtimestamp(1549751401),
143+
datetime.datetime.fromtimestamp(1549751401, datetime.timezone.utc),
106144
)
107145
== "Cloud-CDN-Cookie=URLPrefix=aHR0cDovL3d3dy5leGFtcGxlLmNvbS9mb28v:Expires=1549751401:KeyName=my-key:Signature=Z9uYAu73YHioRScZDxnP-TnS274="
108146
)
147+
148+
149+
def test_sign_cookie_raise_exception_on_naive_expiration_datetime() -> None:
150+
with pytest.raises(TypeError):
151+
snippets.sign_cookie(
152+
"http://www.example.com/foo/",
153+
"my-key",
154+
"nZtRohdNF9m3cKM24IcK4w==",
155+
datetime.datetime.fromtimestamp(1549751401),
156+
)
157+
with pytest.raises(TypeError):
158+
snippets.sign_cookie(
159+
"http://www.example.com/foo/",
160+
"my-key",
161+
"nZtRohdNF9m3cKM24IcK4w==",
162+
datetime.datetime.utcfromtimestamp(1549751401),
163+
)

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.