Fix #328: Validate Date header in HTTP Signatures

environments/review-front-fast-sc64e2/deployments/22
Eliot Berriot 2018-06-24 19:17:56 +02:00
rodzic 2a7333df6f
commit 9017acdb39
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: DD6965E2476E5C27
4 zmienionych plików z 58 dodań i 4 usunięć

Wyświetl plik

@ -5,6 +5,7 @@ import requests
import requests_http_signature import requests_http_signature
from django.conf import settings from django.conf import settings
from django.utils import timezone from django.utils import timezone
from django.utils.http import http_date
from funkwhale_api.factories import registry from funkwhale_api.factories import registry
@ -39,7 +40,7 @@ class SignedRequestFactory(factory.Factory):
default_headers = { default_headers = {
"User-Agent": "Test", "User-Agent": "Test",
"Host": "test.host", "Host": "test.host",
"Date": "Right now", "Date": http_date(timezone.now().timestamp()),
"Content-Type": "application/activity+json", "Content-Type": "application/activity+json",
} }
if extracted: if extracted:

Wyświetl plik

@ -1,4 +1,10 @@
import datetime
import logging 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
import requests_http_signature import requests_http_signature
@ -7,8 +13,33 @@ from . import exceptions, utils
logger = logging.getLogger(__name__) 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): def verify(request, public_key):
verify_date(request.headers.get("Date"))
return requests_http_signature.HTTPSignatureAuth.verify( return requests_http_signature.HTTPSignatureAuth.verify(
request, key_resolver=lambda **kwargs: public_key, use_auth_header=False request, key_resolver=lambda **kwargs: public_key, use_auth_header=False
) )

Wyświetl plik

@ -1,4 +1,7 @@
import cryptography.exceptions import cryptography.exceptions
import datetime
from django.utils.http import http_date
from django import forms
import pytest import pytest
from funkwhale_api.federation import keys, signing 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) 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): def test_can_verify_django_request(factories, fake_request):
private_key, public_key = keys.get_key_pair() private_key, public_key = keys.get_key_pair()
signed_request = factories["federation.SignedRequest"]( 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) 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() private_key, public_key = keys.get_key_pair()
signed_request = factories["federation.SignedRequest"]( signed_request = factories["federation.SignedRequest"](
auth__key=private_key, auth__headers=["date"] auth__key=private_key, auth__headers=["date"]
) )
prepared = signed_request.prepare() prepared = signed_request.prepare()
django_request = fake_request.get( 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) signing.verify_django(django_request, public_key)

Wyświetl plik

@ -0,0 +1 @@
Validate Date header in HTTP Signatures (#328)