kopia lustrzana https://dev.funkwhale.audio/funkwhale/funkwhale
				
				
				
			Fix #328: Validate Date header in HTTP Signatures
							rodzic
							
								
									2a7333df6f
								
							
						
					
					
						commit
						9017acdb39
					
				|  | @ -5,6 +5,7 @@ import requests | |||
| import requests_http_signature | ||||
| from django.conf import settings | ||||
| from django.utils import timezone | ||||
| from django.utils.http import http_date | ||||
| 
 | ||||
| from funkwhale_api.factories import registry | ||||
| 
 | ||||
|  | @ -39,7 +40,7 @@ class SignedRequestFactory(factory.Factory): | |||
|         default_headers = { | ||||
|             "User-Agent": "Test", | ||||
|             "Host": "test.host", | ||||
|             "Date": "Right now", | ||||
|             "Date": http_date(timezone.now().timestamp()), | ||||
|             "Content-Type": "application/activity+json", | ||||
|         } | ||||
|         if extracted: | ||||
|  |  | |||
|  | @ -1,4 +1,10 @@ | |||
| import datetime | ||||
| import logging | ||||
| import pytz | ||||
| 
 | ||||
| from django import forms | ||||
| from django.utils import timezone | ||||
| from django.utils.http import parse_http_date | ||||
| 
 | ||||
| import requests | ||||
| import requests_http_signature | ||||
|  | @ -7,8 +13,33 @@ from . import exceptions, utils | |||
| 
 | ||||
| logger = logging.getLogger(__name__) | ||||
| 
 | ||||
| #  the request Date should be between now - 30s and now + 30s | ||||
| DATE_HEADER_VALID_FOR = 30 | ||||
| 
 | ||||
| 
 | ||||
| def verify_date(raw_date): | ||||
|     if not raw_date: | ||||
|         raise forms.ValidationError("Missing date header") | ||||
| 
 | ||||
|     try: | ||||
|         ts = parse_http_date(raw_date) | ||||
|     except ValueError as e: | ||||
|         raise forms.ValidationError(str(e)) | ||||
|     dt = datetime.datetime.utcfromtimestamp(ts) | ||||
|     dt = dt.replace(tzinfo=pytz.utc) | ||||
|     delta = datetime.timedelta(seconds=DATE_HEADER_VALID_FOR) | ||||
|     now = timezone.now() | ||||
|     if dt < now - delta or dt > now + delta: | ||||
|         raise forms.ValidationError( | ||||
|             "Request Date is too far in the future or in the past" | ||||
|         ) | ||||
| 
 | ||||
|     return dt | ||||
| 
 | ||||
| 
 | ||||
| def verify(request, public_key): | ||||
|     verify_date(request.headers.get("Date")) | ||||
| 
 | ||||
|     return requests_http_signature.HTTPSignatureAuth.verify( | ||||
|         request, key_resolver=lambda **kwargs: public_key, use_auth_header=False | ||||
|     ) | ||||
|  |  | |||
|  | @ -1,4 +1,7 @@ | |||
| import cryptography.exceptions | ||||
| import datetime | ||||
| from django.utils.http import http_date | ||||
| from django import forms | ||||
| import pytest | ||||
| 
 | ||||
| from funkwhale_api.federation import keys, signing | ||||
|  | @ -36,6 +39,20 @@ def test_verify_fails_with_wrong_key(nodb_factories): | |||
|         signing.verify(prepared_request, wrong_public) | ||||
| 
 | ||||
| 
 | ||||
| def test_verify_fails_with_wrong_date(nodb_factories, now): | ||||
|     too_old = now - datetime.timedelta(seconds=31) | ||||
|     too_old = http_date(too_old.timestamp()) | ||||
|     private, public = nodb_factories["federation.KeyPair"]() | ||||
|     auth = nodb_factories["federation.SignatureAuth"](key=private) | ||||
|     request = nodb_factories["federation.SignedRequest"]( | ||||
|         auth=auth, headers={"Date": too_old} | ||||
|     ) | ||||
|     prepared_request = request.prepare() | ||||
| 
 | ||||
|     with pytest.raises(forms.ValidationError): | ||||
|         signing.verify(prepared_request, public) | ||||
| 
 | ||||
| 
 | ||||
| def test_can_verify_django_request(factories, fake_request): | ||||
|     private_key, public_key = keys.get_key_pair() | ||||
|     signed_request = factories["federation.SignedRequest"]( | ||||
|  | @ -95,14 +112,18 @@ def test_can_verify_django_request_digest_failure(factories, fake_request): | |||
|         signing.verify_django(django_request, public_key) | ||||
| 
 | ||||
| 
 | ||||
| def test_can_verify_django_request_failure(factories, fake_request): | ||||
| def test_can_verify_django_request_failure(factories, fake_request, now): | ||||
|     private_key, public_key = keys.get_key_pair() | ||||
|     signed_request = factories["federation.SignedRequest"]( | ||||
|         auth__key=private_key, auth__headers=["date"] | ||||
|     ) | ||||
|     prepared = signed_request.prepare() | ||||
|     django_request = fake_request.get( | ||||
|         "/", **{"HTTP_DATE": "Wrong", "HTTP_SIGNATURE": prepared.headers["signature"]} | ||||
|         "/", | ||||
|         **{ | ||||
|             "HTTP_DATE": http_date((now + datetime.timedelta(seconds=31)).timestamp()), | ||||
|             "HTTP_SIGNATURE": prepared.headers["signature"], | ||||
|         } | ||||
|     ) | ||||
|     with pytest.raises(cryptography.exceptions.InvalidSignature): | ||||
|     with pytest.raises(forms.ValidationError): | ||||
|         signing.verify_django(django_request, public_key) | ||||
|  |  | |||
|  | @ -0,0 +1 @@ | |||
| Validate Date header in HTTP Signatures (#328) | ||||
		Ładowanie…
	
		Reference in New Issue
	
	 Eliot Berriot
						Eliot Berriot