Просмотр исходного кода

Replace insecure md5 hash generation in Order.verification_hash .

Use django.core.signing instead of our own md5 hash.

Fixes #2667.
master
Samir Shah 7 лет назад
Родитель
Сommit
10b5e47e9a

+ 1
- 1
src/oscar/apps/customer/app.py Просмотреть файл

@@ -96,7 +96,7 @@ class CustomerApplication(Application):
96 96
             url(r'^orders/$',
97 97
                 login_required(self.order_history_view.as_view()),
98 98
                 name='order-list'),
99
-            url(r'^order-status/(?P<order_number>[\w-]*)/(?P<hash>\w+)/$',
99
+            url(r'^order-status/(?P<order_number>[\w-]*)/(?P<hash>[A-z0-9-_=:]+)/$',
100 100
                 self.anon_order_detail_view.as_view(), name='anon-order'),
101 101
             url(r'^orders/(?P<order_number>[\w-]*)/$',
102 102
                 login_required(self.order_detail_view.as_view()),

+ 1
- 1
src/oscar/apps/customer/views.py Просмотреть файл

@@ -604,7 +604,7 @@ class AnonymousOrderDetailView(generic.DetailView):
604 604
         # Check URL hash matches that for order to prevent spoof attacks
605 605
         order = get_object_or_404(self.model, user=None,
606 606
                                   number=self.kwargs['order_number'])
607
-        if self.kwargs['hash'] != order.verification_hash():
607
+        if not order.check_verification_hash(self.kwargs['hash']):
608 608
             raise http.Http404()
609 609
         return order
610 610
 

+ 17
- 4
src/oscar/apps/order/abstract_models.py Просмотреть файл

@@ -1,11 +1,12 @@
1
-import hashlib
2 1
 from collections import OrderedDict
3 2
 from decimal import Decimal as D
4 3
 
5 4
 from django.conf import settings
5
+from django.core.signing import BadSignature, Signer
6 6
 from django.db import models
7 7
 from django.db.models import Sum
8 8
 from django.utils import timezone
9
+from django.utils.crypto import constant_time_compare
9 10
 from django.utils.encoding import python_2_unicode_compatible
10 11
 from django.utils.timezone import now
11 12
 from django.utils.translation import ugettext_lazy as _
@@ -299,9 +300,21 @@ class AbstractOrder(models.Model):
299 300
         return u"#%s" % (self.number,)
300 301
 
301 302
     def verification_hash(self):
302
-        key = '%s%s' % (self.number, settings.SECRET_KEY)
303
-        hash = hashlib.md5(key.encode('utf8'))
304
-        return hash.hexdigest()
303
+        signer = Signer(salt='oscar.apps.order.Order')
304
+        return signer.sign(self.number)
305
+
306
+    def check_verification_hash(self, hash_to_check):
307
+        """
308
+        Checks the received verification hash against this order number.
309
+        Returns False if the verification failed, True otherwise.
310
+        """
311
+        signer = Signer(salt='oscar.apps.order.Order')
312
+        try:
313
+            signed_number = signer.unsign(hash_to_check)
314
+        except BadSignature:
315
+            return False
316
+
317
+        return constant_time_compare(signed_number, self.number)
305 318
 
306 319
     @property
307 320
     def email(self):

+ 22
- 1
tests/integration/order/test_models.py Просмотреть файл

@@ -1,7 +1,7 @@
1 1
 from datetime import timedelta, datetime
2 2
 from decimal import Decimal as D
3 3
 
4
-from django.test import TestCase
4
+from django.test import TestCase, override_settings
5 5
 from django.utils import timezone
6 6
 from django.utils.translation import ugettext_lazy as _
7 7
 import mock
@@ -391,3 +391,24 @@ class OrderTests(TestCase):
391 391
         # Set second line to returned
392 392
         event_2.line_quantities.create(line=line_2, quantity=1)
393 393
         self.assertEqual(order.shipping_status, _('Returned'))
394
+
395
+    @override_settings(SECRET_KEY='order_hash_secret')
396
+    def test_verification_hash_generation(self):
397
+        order = OrderFactory(number='111000')
398
+        self.assertEqual(order.verification_hash(), '111000:UJrZWNPLsq7zf1r17c3v1Q6DUmE')
399
+
400
+    @override_settings(SECRET_KEY='order_hash_secret')
401
+    def test_check_verification_hash_valid(self):
402
+        order = OrderFactory(number='111000')
403
+        self.assertTrue(order.check_verification_hash('111000:UJrZWNPLsq7zf1r17c3v1Q6DUmE'))
404
+
405
+    @override_settings(SECRET_KEY='order_hash_secret')
406
+    def test_check_verification_hash_invalid_signature(self):
407
+        order = OrderFactory(number='111000')
408
+        self.assertFalse(order.check_verification_hash('111000:HKDZWNPLsq7589517c3v1Q6DHKD'))
409
+
410
+    @override_settings(SECRET_KEY='order_hash_secret')
411
+    def test_check_verification_hash_valid_signature_but_wrong_number(self):
412
+        order = OrderFactory(number='111000')
413
+        # Hash is valid, but it is for a different order number
414
+        self.assertFalse(order.check_verification_hash('222000:knvoMB1KAiJu8meWtGce00Y88j4'))

Загрузка…
Отмена
Сохранить