Skip to content
This repository was archived by the owner on Apr 13, 2024. It is now read-only.

Commit 78b17af

Browse files
committed
Update input validation for keyId and secret
1 parent 8712e9b commit 78b17af

File tree

5 files changed

+255
-5
lines changed

5 files changed

+255
-5
lines changed

httpsig/sign.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,18 @@ def __init__(self, key_id, secret, algorithm=None, headers=None, sign_header='au
9393
if algorithm is None:
9494
algorithm = DEFAULT_SIGN_ALGORITHM
9595

96+
if not key_id:
97+
raise ValueError("key_id can't be empty")
98+
99+
if len(key_id) > 100000:
100+
raise ValueError("key_id cant be larger than 100000 chars")
101+
102+
if not secret:
103+
raise ValueError("secret can't be empty")
104+
105+
if len(secret) > 100000:
106+
raise ValueError("secret cant be larger than 100000 chars")
107+
96108
super(HeaderSigner, self).__init__(secret=secret, algorithm=algorithm)
97109
self.headers = headers or ['date']
98110
self.signature_template = build_signature_template(

httpsig/tests/test_signature.py

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,3 +106,219 @@ def test_all(self):
106106
params['headers'],
107107
'(request-target) host date content-type digest content-length')
108108
self.assertEqual(params['signature'], 'Ef7MlxLXoBovhil3AlyjtBwAL9g4TN3tibLj7uuNB3CROat/9KaeQ4hW2NiJ+pZ6HQEOx9vYZAyi+7cmIkmJszJCut5kQLAwuX+Ms/mUFvpKlSo9StS2bMXDBNjOh4Auj774GFj4gwjS+3NhFeoqyr/MuN6HsEnkvn6zdgfE2i0=') # noqa: E501
109+
110+
def test_empty_secret(self):
111+
with self.assertRaises(ValueError) as e:
112+
sign.HeaderSigner(key_id='Test', secret='', headers=[
113+
'(request-target)',
114+
'host',
115+
'date',
116+
'content-type',
117+
'digest',
118+
'content-length'
119+
])
120+
self.assertEqual(str(e.exception), "secret can't be empty")
121+
122+
def test_none_secret(self):
123+
with self.assertRaises(ValueError) as e:
124+
sign.HeaderSigner(key_id='Test', secret=None, headers=[
125+
'(request-target)',
126+
'host',
127+
'date',
128+
'content-type',
129+
'digest',
130+
'content-length'
131+
])
132+
self.assertEqual(str(e.exception), "secret can't be empty")
133+
134+
def test_huge_secret(self):
135+
with self.assertRaises(ValueError) as e:
136+
sign.HeaderSigner(key_id='Test', secret='x' * 1000000, headers=[
137+
'(request-target)',
138+
'host',
139+
'date',
140+
'content-type',
141+
'digest',
142+
'content-length'
143+
])
144+
self.assertEqual(str(e.exception), "secret cant be larger than 100000 chars")
145+
146+
def test_empty_key_id(self):
147+
with self.assertRaises(ValueError) as e:
148+
sign.HeaderSigner(key_id='', secret=self.key, headers=[
149+
'(request-target)',
150+
'host',
151+
'date',
152+
'content-type',
153+
'digest',
154+
'content-length'
155+
])
156+
self.assertEqual(str(e.exception), "key_id can't be empty")
157+
158+
def test_none_key_id(self):
159+
with self.assertRaises(ValueError) as e:
160+
sign.HeaderSigner(key_id=None, secret=self.key, headers=[
161+
'(request-target)',
162+
'host',
163+
'date',
164+
'content-type',
165+
'digest',
166+
'content-length'
167+
])
168+
self.assertEqual(str(e.exception), "key_id can't be empty")
169+
170+
def test_huge_key_id(self):
171+
with self.assertRaises(ValueError) as e:
172+
sign.HeaderSigner(key_id='x' * 1000000, secret=self.key, headers=[
173+
'(request-target)',
174+
'host',
175+
'date',
176+
'content-type',
177+
'digest',
178+
'content-length'
179+
])
180+
self.assertEqual(str(e.exception), "key_id cant be larger than 100000 chars")
181+
182+
def test_empty_method(self):
183+
hs = sign.HeaderSigner(key_id='Test', secret=self.key, headers=[
184+
'(request-target)',
185+
'host',
186+
'date',
187+
'content-type',
188+
'digest',
189+
'content-length'
190+
])
191+
unsigned = {
192+
'Host': self.header_host,
193+
'Date': self.header_date,
194+
'Content-Type': self.header_content_type,
195+
'Digest': self.header_digest,
196+
'Content-Length': self.header_content_length,
197+
}
198+
199+
with self.assertRaises(ValueError) as e:
200+
hs.sign(unsigned, method='', path=self.test_path)
201+
self.assertEqual(str(e.exception), 'method and path arguments required when using "(request-target)"')
202+
203+
def test_none_method(self):
204+
hs = sign.HeaderSigner(key_id='Test', secret=self.key, headers=[
205+
'(request-target)',
206+
'host',
207+
'date',
208+
'content-type',
209+
'digest',
210+
'content-length'
211+
])
212+
unsigned = {
213+
'Host': self.header_host,
214+
'Date': self.header_date,
215+
'Content-Type': self.header_content_type,
216+
'Digest': self.header_digest,
217+
'Content-Length': self.header_content_length,
218+
}
219+
220+
with self.assertRaises(ValueError) as e:
221+
hs.sign(unsigned, method=None, path=self.test_path)
222+
self.assertEqual(str(e.exception), 'method and path arguments required when using "(request-target)"')
223+
224+
def test_empty_path(self):
225+
hs = sign.HeaderSigner(key_id='Test', secret=self.key, headers=[
226+
'(request-target)',
227+
'host',
228+
'date',
229+
'content-type',
230+
'digest',
231+
'content-length'
232+
])
233+
unsigned = {
234+
'Host': self.header_host,
235+
'Date': self.header_date,
236+
'Content-Type': self.header_content_type,
237+
'Digest': self.header_digest,
238+
'Content-Length': self.header_content_length,
239+
}
240+
241+
with self.assertRaises(ValueError) as e:
242+
hs.sign(unsigned, method=self.test_method, path='')
243+
self.assertEqual(str(e.exception), 'method and path arguments required when using "(request-target)"')
244+
245+
def test_none_path(self):
246+
hs = sign.HeaderSigner(key_id='Test', secret=self.key, headers=[
247+
'(request-target)',
248+
'host',
249+
'date',
250+
'content-type',
251+
'digest',
252+
'content-length'
253+
])
254+
unsigned = {
255+
'Host': self.header_host,
256+
'Date': self.header_date,
257+
'Content-Type': self.header_content_type,
258+
'Digest': self.header_digest,
259+
'Content-Length': self.header_content_length,
260+
}
261+
262+
with self.assertRaises(ValueError) as e:
263+
hs.sign(unsigned, method=self.test_method, path=None)
264+
self.assertEqual(str(e.exception), 'method and path arguments required when using "(request-target)"')
265+
266+
def test_missing_header_host(self):
267+
hs = sign.HeaderSigner(key_id='Test', secret=self.key, headers=[
268+
'(request-target)',
269+
'host',
270+
'date',
271+
'content-type',
272+
'digest',
273+
'content-length'
274+
])
275+
unsigned = {
276+
'Date': self.header_date,
277+
'Content-Type': self.header_content_type,
278+
'Digest': self.header_digest,
279+
'Content-Length': self.header_content_length,
280+
}
281+
282+
with self.assertRaises(ValueError) as e:
283+
hs.sign(unsigned, method=self.test_method, path=self.test_path)
284+
self.assertEqual(str(e.exception), 'missing required header "host"')
285+
286+
def test_missing_header_date(self):
287+
hs = sign.HeaderSigner(key_id='Test', secret=self.key, headers=[
288+
'(request-target)',
289+
'host',
290+
'date',
291+
'content-type',
292+
'digest',
293+
'content-length'
294+
])
295+
unsigned = {
296+
'Host': self.header_host,
297+
'Content-Type': self.header_content_type,
298+
'Digest': self.header_digest,
299+
'Content-Length': self.header_content_length,
300+
}
301+
302+
with self.assertRaises(ValueError) as e:
303+
hs.sign(unsigned, method=self.test_method, path=self.test_path)
304+
self.assertEqual(str(e.exception), 'missing required header "date"')
305+
306+
def test_missing_header_digest(self):
307+
hs = sign.HeaderSigner(key_id='Test', secret=self.key, headers=[
308+
'(request-target)',
309+
'host',
310+
'date',
311+
'content-type',
312+
'digest',
313+
'content-length'
314+
])
315+
unsigned = {
316+
'Host': self.header_host,
317+
'Date': self.header_date,
318+
'Content-Type': self.header_content_type,
319+
'Content-Length': self.header_content_length,
320+
}
321+
322+
with self.assertRaises(ValueError) as e:
323+
hs.sign(unsigned, method=self.test_method, path=self.test_path)
324+
self.assertEqual(str(e.exception), 'missing required header "digest"')

httpsig/tests/test_verify.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,9 @@ def test_incorrect_headers(self):
130130
required_headers=["some-other-header"],
131131
host=HOST, method=METHOD, path=PATH,
132132
sign_header=self.sign_header)
133-
with self.assertRaises(Exception):
133+
with self.assertRaises(ValueError) as e:
134134
hv.verify()
135+
self.assertEqual(str(e.exception), 'some-other-header is a required header(s)')
135136

136137
def test_extra_auth_headers(self):
137138
HOST = "example.com"
@@ -166,6 +167,21 @@ def test_extra_auth_headers(self):
166167
required_headers=['date', '(request-target)'])
167168
self.assertTrue(hv.verify())
168169

170+
def test_empty_secret(self):
171+
with self.assertRaises(ValueError) as e:
172+
HeaderVerifier(secret='', headers={})
173+
self.assertEqual(str(e.exception), 'secret cant be empty')
174+
175+
def test_none_secret(self):
176+
with self.assertRaises(ValueError) as e:
177+
HeaderVerifier(secret=None, headers={})
178+
self.assertEqual(str(e.exception), 'secret cant be empty')
179+
180+
def test_huge_secret(self):
181+
with self.assertRaises(ValueError) as e:
182+
HeaderVerifier(secret='x' * 1000000, headers={})
183+
self.assertEqual(str(e.exception), 'secret cant be larger than 100000 chars')
184+
169185

170186
class TestVerifyHMACSHA256(TestVerifyHMACSHA1):
171187

httpsig/utils.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def generate_message(required_headers, headers, host=None, method=None,
6464
h = h.lower()
6565
if h == '(request-target)':
6666
if not method or not path:
67-
raise Exception('method and path arguments required when ' +
67+
raise ValueError('method and path arguments required when ' +
6868
'using "(request-target)"')
6969
signable_list.append('%s: %s %s' % (h, method.lower(), path))
7070

@@ -76,11 +76,11 @@ def generate_message(required_headers, headers, host=None, method=None,
7676
if 'host' in headers:
7777
host = headers[h]
7878
else:
79-
raise Exception('missing required header "%s"' % h)
79+
raise ValueError('missing required header "%s"' % h)
8080
signable_list.append('%s: %s' % (h, host))
8181
else:
8282
if h not in headers:
83-
raise Exception('missing required header "%s"' % h)
83+
raise ValueError('missing required header "%s"' % h)
8484

8585
signable_list.append('%s: %s' % (h, headers[h]))
8686

httpsig/verify.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ def __init__(self, headers, secret, required_headers=None, method=None,
6767
:param sign_header: Optional. The header where the signature is.
6868
Default is 'authorization'.
6969
"""
70+
if not secret:
71+
raise ValueError("secret cant be empty")
72+
73+
if len(secret) > 100000:
74+
raise ValueError("secret cant be larger than 100000 chars")
75+
7076
required_headers = required_headers or ['date']
7177
self.headers = CaseInsensitiveDict(headers)
7278

@@ -101,7 +107,7 @@ def verify(self):
101107
if len(set(self.required_headers) - set(auth_headers)) > 0:
102108
error_headers = ', '.join(
103109
set(self.required_headers) - set(auth_headers))
104-
raise Exception(
110+
raise ValueError(
105111
'{} is a required header(s)'.format(error_headers))
106112

107113
signing_str = generate_message(

0 commit comments

Comments
 (0)