Skip to content

Commit bbe4cf1

Browse files
committed
Test that indexing prevention works as expected.
1 parent c1661b6 commit bbe4cf1

File tree

1 file changed

+288
-0
lines changed

1 file changed

+288
-0
lines changed

test/test_header_indexing.py

Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,3 +311,291 @@ def test_header_tuples_are_decoded_push_promise(self,
311311

312312
assert isinstance(event, h2.events.PushedStreamReceived)
313313
assert_header_blocks_actually_equal(headers, event.headers)
314+
315+
316+
class TestSecureHeaders(object):
317+
"""
318+
Certain headers should always be transformed to their never-indexed form.
319+
"""
320+
example_request_headers = [
321+
(u':authority', u'example.com'),
322+
(u':path', u'/'),
323+
(u':scheme', u'https'),
324+
(u':method', u'GET'),
325+
]
326+
bytes_example_request_headers = [
327+
(b':authority', b'example.com'),
328+
(b':path', b'/'),
329+
(b':scheme', b'https'),
330+
(b':method', b'GET'),
331+
]
332+
possible_auth_headers = [
333+
(u'authorization', u'test'),
334+
(u'Authorization', u'test'),
335+
(u'authorization', u'really long test'),
336+
HeaderTuple(u'authorization', u'test'),
337+
HeaderTuple(u'Authorization', u'test'),
338+
HeaderTuple(u'authorization', u'really long test'),
339+
NeverIndexedHeaderTuple(u'authorization', u'test'),
340+
NeverIndexedHeaderTuple(u'Authorization', u'test'),
341+
NeverIndexedHeaderTuple(u'authorization', u'really long test'),
342+
(b'authorization', b'test'),
343+
(b'Authorization', b'test'),
344+
(b'authorization', b'really long test'),
345+
HeaderTuple(b'authorization', b'test'),
346+
HeaderTuple(b'Authorization', b'test'),
347+
HeaderTuple(b'authorization', b'really long test'),
348+
NeverIndexedHeaderTuple(b'authorization', b'test'),
349+
NeverIndexedHeaderTuple(b'Authorization', b'test'),
350+
NeverIndexedHeaderTuple(b'authorization', b'really long test'),
351+
]
352+
secured_cookie_headers = [
353+
(u'cookie', u'short'),
354+
(u'Cookie', u'short'),
355+
(u'cookie', u'nineteen byte cooki'),
356+
HeaderTuple(u'cookie', u'short'),
357+
HeaderTuple(u'Cookie', u'short'),
358+
HeaderTuple(u'cookie', u'nineteen byte cooki'),
359+
NeverIndexedHeaderTuple(u'cookie', u'short'),
360+
NeverIndexedHeaderTuple(u'Cookie', u'short'),
361+
NeverIndexedHeaderTuple(u'cookie', u'nineteen byte cooki'),
362+
NeverIndexedHeaderTuple(u'cookie', u'longer manually secured cookie'),
363+
(b'cookie', b'short'),
364+
(b'Cookie', b'short'),
365+
(b'cookie', b'nineteen byte cooki'),
366+
HeaderTuple(b'cookie', b'short'),
367+
HeaderTuple(b'Cookie', b'short'),
368+
HeaderTuple(b'cookie', b'nineteen byte cooki'),
369+
NeverIndexedHeaderTuple(b'cookie', b'short'),
370+
NeverIndexedHeaderTuple(b'Cookie', b'short'),
371+
NeverIndexedHeaderTuple(b'cookie', b'nineteen byte cooki'),
372+
NeverIndexedHeaderTuple(b'cookie', b'longer manually secured cookie'),
373+
]
374+
unsecured_cookie_headers = [
375+
(u'cookie', u'twenty byte cookie!!'),
376+
(u'Cookie', u'twenty byte cookie!!'),
377+
(u'cookie', u'substantially longer than 20 byte cookie'),
378+
HeaderTuple(u'cookie', u'twenty byte cookie!!'),
379+
HeaderTuple(u'cookie', u'twenty byte cookie!!'),
380+
HeaderTuple(u'Cookie', u'twenty byte cookie!!'),
381+
(b'cookie', b'twenty byte cookie!!'),
382+
(b'Cookie', b'twenty byte cookie!!'),
383+
(b'cookie', b'substantially longer than 20 byte cookie'),
384+
HeaderTuple(b'cookie', b'twenty byte cookie!!'),
385+
HeaderTuple(b'cookie', b'twenty byte cookie!!'),
386+
HeaderTuple(b'Cookie', b'twenty byte cookie!!'),
387+
]
388+
389+
@pytest.mark.parametrize(
390+
'headers', (example_request_headers, bytes_example_request_headers)
391+
)
392+
@pytest.mark.parametrize('auth_header', possible_auth_headers)
393+
def test_authorization_headers_never_indexed(self,
394+
headers,
395+
auth_header,
396+
frame_factory):
397+
"""
398+
Authorization headers are always forced to be never-indexed, regardless
399+
of their form.
400+
"""
401+
# Regardless of what we send, we expect it to be never indexed.
402+
send_headers = headers + [auth_header]
403+
expected_headers = headers + [
404+
NeverIndexedHeaderTuple(auth_header[0].lower(), auth_header[1])
405+
]
406+
407+
c = h2.connection.H2Connection()
408+
c.initiate_connection()
409+
410+
# Clear the data, then send headers.
411+
c.clear_outbound_data_buffer()
412+
c.send_headers(1, send_headers)
413+
414+
f = frame_factory.build_headers_frame(headers=expected_headers)
415+
assert c.data_to_send() == f.serialize()
416+
417+
@pytest.mark.parametrize(
418+
'headers', (example_request_headers, bytes_example_request_headers)
419+
)
420+
@pytest.mark.parametrize('auth_header', possible_auth_headers)
421+
def test_authorization_headers_never_indexed_push(self,
422+
headers,
423+
auth_header,
424+
frame_factory):
425+
"""
426+
Authorization headers are always forced to be never-indexed, regardless
427+
of their form, when pushed by a server.
428+
"""
429+
# Regardless of what we send, we expect it to be never indexed.
430+
send_headers = headers + [auth_header]
431+
expected_headers = headers + [
432+
NeverIndexedHeaderTuple(auth_header[0].lower(), auth_header[1])
433+
]
434+
435+
c = h2.connection.H2Connection(client_side=False)
436+
c.receive_data(frame_factory.preamble())
437+
438+
# We can use normal headers for the request.
439+
f = frame_factory.build_headers_frame(
440+
self.example_request_headers
441+
)
442+
c.receive_data(f.serialize())
443+
444+
frame_factory.refresh_encoder()
445+
expected_frame = frame_factory.build_push_promise_frame(
446+
stream_id=1,
447+
promised_stream_id=2,
448+
headers=expected_headers,
449+
flags=['END_HEADERS'],
450+
)
451+
452+
c.clear_outbound_data_buffer()
453+
c.push_stream(
454+
stream_id=1,
455+
promised_stream_id=2,
456+
request_headers=send_headers
457+
)
458+
459+
assert c.data_to_send() == expected_frame.serialize()
460+
461+
@pytest.mark.parametrize(
462+
'headers', (example_request_headers, bytes_example_request_headers)
463+
)
464+
@pytest.mark.parametrize('cookie_header', secured_cookie_headers)
465+
def test_short_cookie_headers_never_indexed(self,
466+
headers,
467+
cookie_header,
468+
frame_factory):
469+
"""
470+
Short cookie headers, and cookies provided as NeverIndexedHeaderTuple,
471+
are never indexed.
472+
"""
473+
# Regardless of what we send, we expect it to be never indexed.
474+
send_headers = headers + [cookie_header]
475+
expected_headers = headers + [
476+
NeverIndexedHeaderTuple(cookie_header[0].lower(), cookie_header[1])
477+
]
478+
479+
c = h2.connection.H2Connection()
480+
c.initiate_connection()
481+
482+
# Clear the data, then send headers.
483+
c.clear_outbound_data_buffer()
484+
c.send_headers(1, send_headers)
485+
486+
f = frame_factory.build_headers_frame(headers=expected_headers)
487+
assert c.data_to_send() == f.serialize()
488+
489+
@pytest.mark.parametrize(
490+
'headers', (example_request_headers, bytes_example_request_headers)
491+
)
492+
@pytest.mark.parametrize('cookie_header', secured_cookie_headers)
493+
def test_short_cookie_headers_never_indexed_push(self,
494+
headers,
495+
cookie_header,
496+
frame_factory):
497+
"""
498+
Short cookie headers, and cookies provided as NeverIndexedHeaderTuple,
499+
are never indexed when pushed by servers.
500+
"""
501+
# Regardless of what we send, we expect it to be never indexed.
502+
send_headers = headers + [cookie_header]
503+
expected_headers = headers + [
504+
NeverIndexedHeaderTuple(cookie_header[0].lower(), cookie_header[1])
505+
]
506+
507+
c = h2.connection.H2Connection(client_side=False)
508+
c.receive_data(frame_factory.preamble())
509+
510+
# We can use normal headers for the request.
511+
f = frame_factory.build_headers_frame(
512+
self.example_request_headers
513+
)
514+
c.receive_data(f.serialize())
515+
516+
frame_factory.refresh_encoder()
517+
expected_frame = frame_factory.build_push_promise_frame(
518+
stream_id=1,
519+
promised_stream_id=2,
520+
headers=expected_headers,
521+
flags=['END_HEADERS'],
522+
)
523+
524+
c.clear_outbound_data_buffer()
525+
c.push_stream(
526+
stream_id=1,
527+
promised_stream_id=2,
528+
request_headers=send_headers
529+
)
530+
531+
assert c.data_to_send() == expected_frame.serialize()
532+
533+
@pytest.mark.parametrize(
534+
'headers', (example_request_headers, bytes_example_request_headers)
535+
)
536+
@pytest.mark.parametrize('cookie_header', unsecured_cookie_headers)
537+
def test_long_cookie_headers_can_be_indexed(self,
538+
headers,
539+
cookie_header,
540+
frame_factory):
541+
"""
542+
Longer cookie headers can be indexed.
543+
"""
544+
# Regardless of what we send, we expect it to be indexed.
545+
send_headers = headers + [cookie_header]
546+
expected_headers = headers + [
547+
HeaderTuple(cookie_header[0].lower(), cookie_header[1])
548+
]
549+
550+
c = h2.connection.H2Connection()
551+
c.initiate_connection()
552+
553+
# Clear the data, then send headers.
554+
c.clear_outbound_data_buffer()
555+
c.send_headers(1, send_headers)
556+
557+
f = frame_factory.build_headers_frame(headers=expected_headers)
558+
assert c.data_to_send() == f.serialize()
559+
560+
@pytest.mark.parametrize(
561+
'headers', (example_request_headers, bytes_example_request_headers)
562+
)
563+
@pytest.mark.parametrize('cookie_header', unsecured_cookie_headers)
564+
def test_long_cookie_headers_can_be_indexed_push(self,
565+
headers,
566+
cookie_header,
567+
frame_factory):
568+
"""
569+
Longer cookie headers can be indexed.
570+
"""
571+
# Regardless of what we send, we expect it to be never indexed.
572+
send_headers = headers + [cookie_header]
573+
expected_headers = headers + [
574+
HeaderTuple(cookie_header[0].lower(), cookie_header[1])
575+
]
576+
577+
c = h2.connection.H2Connection(client_side=False)
578+
c.receive_data(frame_factory.preamble())
579+
580+
# We can use normal headers for the request.
581+
f = frame_factory.build_headers_frame(
582+
self.example_request_headers
583+
)
584+
c.receive_data(f.serialize())
585+
586+
frame_factory.refresh_encoder()
587+
expected_frame = frame_factory.build_push_promise_frame(
588+
stream_id=1,
589+
promised_stream_id=2,
590+
headers=expected_headers,
591+
flags=['END_HEADERS'],
592+
)
593+
594+
c.clear_outbound_data_buffer()
595+
c.push_stream(
596+
stream_id=1,
597+
promised_stream_id=2,
598+
request_headers=send_headers
599+
)
600+
601+
assert c.data_to_send() == expected_frame.serialize()

0 commit comments

Comments
 (0)