Kaynağa Gözat

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 yıl önce
ebeveyn
işleme
10b5e47e9a

+ 1
- 1
src/oscar/apps/customer/app.py Dosyayı Görüntüle

96
             url(r'^orders/$',
96
             url(r'^orders/$',
97
                 login_required(self.order_history_view.as_view()),
97
                 login_required(self.order_history_view.as_view()),
98
                 name='order-list'),
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
                 self.anon_order_detail_view.as_view(), name='anon-order'),
100
                 self.anon_order_detail_view.as_view(), name='anon-order'),
101
             url(r'^orders/(?P<order_number>[\w-]*)/$',
101
             url(r'^orders/(?P<order_number>[\w-]*)/$',
102
                 login_required(self.order_detail_view.as_view()),
102
                 login_required(self.order_detail_view.as_view()),

+ 1
- 1
src/oscar/apps/customer/views.py Dosyayı Görüntüle

604
         # Check URL hash matches that for order to prevent spoof attacks
604
         # Check URL hash matches that for order to prevent spoof attacks
605
         order = get_object_or_404(self.model, user=None,
605
         order = get_object_or_404(self.model, user=None,
606
                                   number=self.kwargs['order_number'])
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
             raise http.Http404()
608
             raise http.Http404()
609
         return order
609
         return order
610
 
610
 

+ 17
- 4
src/oscar/apps/order/abstract_models.py Dosyayı Görüntüle

1
-import hashlib
2
 from collections import OrderedDict
1
 from collections import OrderedDict
3
 from decimal import Decimal as D
2
 from decimal import Decimal as D
4
 
3
 
5
 from django.conf import settings
4
 from django.conf import settings
5
+from django.core.signing import BadSignature, Signer
6
 from django.db import models
6
 from django.db import models
7
 from django.db.models import Sum
7
 from django.db.models import Sum
8
 from django.utils import timezone
8
 from django.utils import timezone
9
+from django.utils.crypto import constant_time_compare
9
 from django.utils.encoding import python_2_unicode_compatible
10
 from django.utils.encoding import python_2_unicode_compatible
10
 from django.utils.timezone import now
11
 from django.utils.timezone import now
11
 from django.utils.translation import ugettext_lazy as _
12
 from django.utils.translation import ugettext_lazy as _
299
         return u"#%s" % (self.number,)
300
         return u"#%s" % (self.number,)
300
 
301
 
301
     def verification_hash(self):
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
     @property
319
     @property
307
     def email(self):
320
     def email(self):

+ 22
- 1
tests/integration/order/test_models.py Dosyayı Görüntüle

1
 from datetime import timedelta, datetime
1
 from datetime import timedelta, datetime
2
 from decimal import Decimal as D
2
 from decimal import Decimal as D
3
 
3
 
4
-from django.test import TestCase
4
+from django.test import TestCase, override_settings
5
 from django.utils import timezone
5
 from django.utils import timezone
6
 from django.utils.translation import ugettext_lazy as _
6
 from django.utils.translation import ugettext_lazy as _
7
 import mock
7
 import mock
391
         # Set second line to returned
391
         # Set second line to returned
392
         event_2.line_quantities.create(line=line_2, quantity=1)
392
         event_2.line_quantities.create(line=line_2, quantity=1)
393
         self.assertEqual(order.shipping_status, _('Returned'))
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'))

Loading…
İptal
Kaydet