summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.txt22
-rw-r--r--docs/api/authentication.rst4
-rw-r--r--docs/narr/security.rst10
-rw-r--r--docs/tutorials/wiki/authorization.rst4
-rw-r--r--docs/tutorials/wiki/src/authorization/tutorial/__init__.py6
-rw-r--r--docs/tutorials/wiki/src/tests/tutorial/__init__.py6
-rw-r--r--docs/tutorials/wiki2/authorization.rst4
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/__init__.py6
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/__init__.py6
-rw-r--r--pyramid/authentication.py68
-rw-r--r--pyramid/tests/test_authentication.py82
11 files changed, 72 insertions, 146 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index a9ce80712..b3733a787 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -4,6 +4,10 @@ Next release
Features
--------
+- ``pyramid.authentication.AuthTktAuthenticationPolicy`` has been updated to
+ support newer hashing algorithms such as ``sha512``. Existing applications
+ should consider updating if possible.
+
- Added an ``effective_principals`` route and view predicate.
- Do not allow the userid returned from the ``authenticated_userid`` or the
@@ -44,24 +48,6 @@ Bug Fixes
attribute of the request. It no longer fails in this case. See
https://github.com/Pylons/pyramid/issues/700
-Deprecations
-------------
-
-- The ``pyramid.authentication.AuthTktAuthenticationPolicy`` authentication
- policy is deprecated in Pyramid 1.4 due to its use of the MD5 hashing
- algorithm, which has known hash collision vulnerabilities. The risk of an
- exploit is low. However, for improved authentication security, use the
- ``pyramid.authentication.SHA512AuthTktAuthenticationPolicy`` instead.
- Cookies generated by the AuthTktAuthenticationPolicy are not compatible with
- cookies generated by the SHA512AuthTktAuthenticationPolicy, however, so
- switching to the latter will imply that all existing users with a valid
- cookie will be required to re-login. The SHA-512 version is not compatible
- with Apache's mod_auth_tkt either, so if you are relying on that
- compatibility, you'll want to stick with the MD5 version.
-
- A deprecation warning is now emitted when the AuthTktAuthenticationPolicy is
- imported.
-
Internals
---------
diff --git a/docs/api/authentication.rst b/docs/api/authentication.rst
index 49ee405eb..19d08618b 100644
--- a/docs/api/authentication.rst
+++ b/docs/api/authentication.rst
@@ -8,10 +8,6 @@ Authentication Policies
.. automodule:: pyramid.authentication
- .. autoclass:: SHA512AuthTktAuthenticationPolicy
- :members:
- :inherited-members:
-
.. autoclass:: AuthTktAuthenticationPolicy
:members:
:inherited-members:
diff --git a/docs/narr/security.rst b/docs/narr/security.rst
index 3c9759e6c..3a94b4f7d 100644
--- a/docs/narr/security.rst
+++ b/docs/narr/security.rst
@@ -90,13 +90,13 @@ For example:
:linenos:
from pyramid.config import Configurator
- from pyramid.authentication import SHA512AuthTktAuthenticationPolicy
+ from pyramid.authentication import AuthTktAuthenticationPolicy
from pyramid.authorization import ACLAuthorizationPolicy
- authentication_policy = SHA512AuthTktAuthenticationPolicy('seekrit')
- authorization_policy = ACLAuthorizationPolicy()
+ authn_policy = AuthTktAuthenticationPolicy('seekrit', hashalg='sha512')
+ authz_policy = ACLAuthorizationPolicy()
config = Configurator()
- config.set_authentication_policy(authentication_policy)
- config.set_authorization_policy(authorization_policy)
+ config.set_authentication_policy(authn_policy)
+ config.set_authorization_policy(authz_policy)
.. note:: the ``authentication_policy`` and ``authorization_policy``
arguments may also be passed to their respective methods mentioned above
diff --git a/docs/tutorials/wiki/authorization.rst b/docs/tutorials/wiki/authorization.rst
index e2c4637d7..24249945a 100644
--- a/docs/tutorials/wiki/authorization.rst
+++ b/docs/tutorials/wiki/authorization.rst
@@ -134,12 +134,12 @@ Now add those policies to the configuration:
(Only the highlighted lines need to be added.)
-We are enabling an ``SHA512AuthTktAuthenticationPolicy``, it is based in an
+We are enabling an ``AuthTktAuthenticationPolicy``, it is based in an
auth ticket that may be included in the request, and an
``ACLAuthorizationPolicy`` that uses an ACL to determine the allow or deny
outcome for a view.
-Note that the :class:`pyramid.authentication.SHA512AuthTktAuthenticationPolicy`
+Note that the :class:`pyramid.authentication.AuthTktAuthenticationPolicy`
constructor accepts two arguments: ``secret`` and ``callback``. ``secret`` is
a string representing an encryption key used by the "authentication ticket"
machinery represented by this policy: it is required. The ``callback`` is the
diff --git a/docs/tutorials/wiki/src/authorization/tutorial/__init__.py b/docs/tutorials/wiki/src/authorization/tutorial/__init__.py
index 4c766fea2..b42e01d03 100644
--- a/docs/tutorials/wiki/src/authorization/tutorial/__init__.py
+++ b/docs/tutorials/wiki/src/authorization/tutorial/__init__.py
@@ -1,7 +1,7 @@
from pyramid.config import Configurator
from pyramid_zodbconn import get_connection
-from pyramid.authentication import SHA512AuthTktAuthenticationPolicy
+from pyramid.authentication import AuthTktAuthenticationPolicy
from pyramid.authorization import ACLAuthorizationPolicy
from .models import appmaker
@@ -14,8 +14,8 @@ def root_factory(request):
def main(global_config, **settings):
""" This function returns a WSGI application.
"""
- authn_policy = SHA512AuthTktAuthenticationPolicy(secret='sosecret',
- callback=groupfinder)
+ authn_policy = AuthTktAuthenticationPolicy(
+ 'sosecret', callback=groupfinder, hashalg='sha512')
authz_policy = ACLAuthorizationPolicy()
config = Configurator(root_factory=root_factory, settings=settings)
config.set_authentication_policy(authn_policy)
diff --git a/docs/tutorials/wiki/src/tests/tutorial/__init__.py b/docs/tutorials/wiki/src/tests/tutorial/__init__.py
index 4c766fea2..b42e01d03 100644
--- a/docs/tutorials/wiki/src/tests/tutorial/__init__.py
+++ b/docs/tutorials/wiki/src/tests/tutorial/__init__.py
@@ -1,7 +1,7 @@
from pyramid.config import Configurator
from pyramid_zodbconn import get_connection
-from pyramid.authentication import SHA512AuthTktAuthenticationPolicy
+from pyramid.authentication import AuthTktAuthenticationPolicy
from pyramid.authorization import ACLAuthorizationPolicy
from .models import appmaker
@@ -14,8 +14,8 @@ def root_factory(request):
def main(global_config, **settings):
""" This function returns a WSGI application.
"""
- authn_policy = SHA512AuthTktAuthenticationPolicy(secret='sosecret',
- callback=groupfinder)
+ authn_policy = AuthTktAuthenticationPolicy(
+ 'sosecret', callback=groupfinder, hashalg='sha512')
authz_policy = ACLAuthorizationPolicy()
config = Configurator(root_factory=root_factory, settings=settings)
config.set_authentication_policy(authn_policy)
diff --git a/docs/tutorials/wiki2/authorization.rst b/docs/tutorials/wiki2/authorization.rst
index e3087dea5..1ddf8c82d 100644
--- a/docs/tutorials/wiki2/authorization.rst
+++ b/docs/tutorials/wiki2/authorization.rst
@@ -151,12 +151,12 @@ Now add those policies to the configuration:
(Only the highlighted lines need to be added.)
-We are enabling an ``SHA512AuthTktAuthenticationPolicy``, it is based in an
+We are enabling an ``AuthTktAuthenticationPolicy``, it is based in an
auth ticket that may be included in the request, and an
``ACLAuthorizationPolicy`` that uses an ACL to determine the allow or deny
outcome for a view.
-Note that the :class:`pyramid.authentication.SHA512AuthTktAuthenticationPolicy`
+Note that the :class:`pyramid.authentication.AuthTktAuthenticationPolicy`
constructor accepts two arguments: ``secret`` and ``callback``. ``secret`` is
a string representing an encryption key used by the "authentication ticket"
machinery represented by this policy: it is required. The ``callback`` is the
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py b/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py
index 585cdf884..76071173a 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py
@@ -1,5 +1,5 @@
from pyramid.config import Configurator
-from pyramid.authentication import SHA512AuthTktAuthenticationPolicy
+from pyramid.authentication import AuthTktAuthenticationPolicy
from pyramid.authorization import ACLAuthorizationPolicy
from sqlalchemy import engine_from_config
@@ -17,8 +17,8 @@ def main(global_config, **settings):
engine = engine_from_config(settings, 'sqlalchemy.')
DBSession.configure(bind=engine)
Base.metadata.bind = engine
- authn_policy = SHA512AuthTktAuthenticationPolicy(
- 'sosecret', callback=groupfinder)
+ authn_policy = AuthTktAuthenticationPolicy(
+ 'sosecret', callback=groupfinder, hashalg='sha512')
authz_policy = ACLAuthorizationPolicy()
config = Configurator(settings=settings,
root_factory='tutorial.models.RootFactory')
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/__init__.py b/docs/tutorials/wiki2/src/tests/tutorial/__init__.py
index 585cdf884..76071173a 100644
--- a/docs/tutorials/wiki2/src/tests/tutorial/__init__.py
+++ b/docs/tutorials/wiki2/src/tests/tutorial/__init__.py
@@ -1,5 +1,5 @@
from pyramid.config import Configurator
-from pyramid.authentication import SHA512AuthTktAuthenticationPolicy
+from pyramid.authentication import AuthTktAuthenticationPolicy
from pyramid.authorization import ACLAuthorizationPolicy
from sqlalchemy import engine_from_config
@@ -17,8 +17,8 @@ def main(global_config, **settings):
engine = engine_from_config(settings, 'sqlalchemy.')
DBSession.configure(bind=engine)
Base.metadata.bind = engine
- authn_policy = SHA512AuthTktAuthenticationPolicy(
- 'sosecret', callback=groupfinder)
+ authn_policy = AuthTktAuthenticationPolicy(
+ 'sosecret', callback=groupfinder, hashalg='sha512')
authz_policy = ACLAuthorizationPolicy()
config = Configurator(settings=settings,
root_factory='tutorial.models.RootFactory')
diff --git a/pyramid/authentication.py b/pyramid/authentication.py
index 2e58c70e7..dbca68a11 100644
--- a/pyramid/authentication.py
+++ b/pyramid/authentication.py
@@ -7,7 +7,6 @@ import datetime
import re
import time as time_mod
-from zope.deprecation import deprecated
from zope.interface import implementer
from pyramid.compat import (
@@ -406,10 +405,18 @@ class RemoteUserAuthenticationPolicy(CallbackAuthenticationPolicy):
be done somewhere else or in a subclass."""
return []
-class BaseAuthTktAuthenticationPolicy(CallbackAuthenticationPolicy):
+@implementer(IAuthenticationPolicy)
+class AuthTktAuthenticationPolicy(CallbackAuthenticationPolicy):
"""A :app:`Pyramid` :term:`authentication policy` which
obtains data from a Pyramid "auth ticket" cookie.
+ .. warning::
+
+ The default hash algorithm used in this policy is MD5 and has known
+ hash collision vulnerabilities. The risk of an exploit is low.
+ However, for improved authentication security, use
+ ``hashalg='sha512'``.
+
Constructor Arguments
``secret``
@@ -498,6 +505,26 @@ class BaseAuthTktAuthenticationPolicy(CallbackAuthenticationPolicy):
wildcard domain.
Optional.
+ ``hashalg``
+
+ Default: ``md5``. Cookies generated by different instances of
+ AuthTktAuthenticationPolicy using different ``hashalg`` options
+ are not compatible. Switching the ``hashalg`` will imply that
+ all existing users with a valid cookie will be required to re-login.
+
+ Any hash algorithm supported by Python's ``hashlib.new()`` function
+ can be used as the ``hashalg``.
+
+ This option is available as of :app:`Pyramid` 1.4. See the warning
+ above for reasons to change ``hashalg`` in your own apps.
+
+ Optional.
+
+ .. note::
+
+ ``sha512`` is recommended for improved security and to maintain
+ compatibility with Apache's ``mod_auth_tkt`` module.
+
``debug``
Default: ``False``. If ``debug`` is ``True``, log messages to the
@@ -508,7 +535,6 @@ class BaseAuthTktAuthenticationPolicy(CallbackAuthenticationPolicy):
Objects of this class implement the interface described by
:class:`pyramid.interfaces.IAuthenticationPolicy`.
"""
- hashalg = ''
def __init__(self,
secret,
@@ -523,6 +549,7 @@ class BaseAuthTktAuthenticationPolicy(CallbackAuthenticationPolicy):
http_only=False,
wild_domain=True,
debug=False,
+ hashalg='md5',
):
self.cookie = AuthTktCookieHelper(
secret,
@@ -535,7 +562,7 @@ class BaseAuthTktAuthenticationPolicy(CallbackAuthenticationPolicy):
http_only=http_only,
path=path,
wild_domain=wild_domain,
- hashalg=self.hashalg,
+ hashalg=hashalg,
)
self.callback = callback
self.debug = debug
@@ -560,39 +587,6 @@ class BaseAuthTktAuthenticationPolicy(CallbackAuthenticationPolicy):
""" A list of headers which will delete appropriate cookies."""
return self.cookie.forget(request)
-@implementer(IAuthenticationPolicy)
-class SHA512AuthTktAuthenticationPolicy(BaseAuthTktAuthenticationPolicy):
- __doc__ = """.. versionadded:: 1.4
-
- """ + BaseAuthTktAuthenticationPolicy.__doc__
- hashalg = 'sha512'
-
-@implementer(IAuthenticationPolicy)
-class AuthTktAuthenticationPolicy(BaseAuthTktAuthenticationPolicy):
- __doc__ = """
- .. warning::
-
- Deprecated in 1.4 due to security concerns,
- use :class:`SHA512AuthTktAuthenticationPolicy` instead.
-
- """ + BaseAuthTktAuthenticationPolicy.__doc__
- hashalg = 'md5'
-
-deprecated(
- 'AuthTktAuthenticationPolicy',
- 'The AuthTktAuthenticationPolicy is deprecated in Pyramid 1.4 '
- 'due to its use of the MD5 hashing algorithm, which has known '
- 'hash collision vulnerabilities. The risk of an exploit is low. '
- 'However, for improved authentication security, use the '
- 'pyramid.authentication.SHA512AuthTktAuthenticationPolicy instead. '
- 'Cookies generated by the AuthTktAuthenticationPolicy are *not* '
- 'compatible with cookies generated by the '
- 'SHA512AuthTktAuthenticationPolicy, however, so switching to the '
- 'latter will imply that all existing users with a valid cookie '
- 'will be required to re-login. The SHA-512 version is not compatible '
- 'with Apache\'s mod_auth_tkt either.'
- )
-
def b64encode(v):
return base64.b64encode(bytes_(v)).strip().replace(b'\n', b'')
diff --git a/pyramid/tests/test_authentication.py b/pyramid/tests/test_authentication.py
index 8b640b860..2d69173fa 100644
--- a/pyramid/tests/test_authentication.py
+++ b/pyramid/tests/test_authentication.py
@@ -431,14 +431,6 @@ class TestRemoteUserAuthenticationPolicy(unittest.TestCase):
self.assertEqual(result, [])
class TestAuthTktAuthenticationPolicy(unittest.TestCase):
- def setUp(self):
- from zope.deprecation import __show__
- __show__.off()
-
- def tearDown(self):
- from zope.deprecation import __show__
- __show__.on()
-
def _getTargetClass(self):
from pyramid.authentication import AuthTktAuthenticationPolicy
return AuthTktAuthenticationPolicy
@@ -448,72 +440,20 @@ class TestAuthTktAuthenticationPolicy(unittest.TestCase):
inst.cookie = DummyCookieHelper(cookieidentity)
return inst
- def test_is_subclass(self):
- from pyramid.authentication import BaseAuthTktAuthenticationPolicy
- inst = self._makeOne(None, None)
- self.assertTrue(isinstance(inst, BaseAuthTktAuthenticationPolicy))
-
- def test_md5(self):
- inst = self._makeOne(None, None)
- self.assertEqual(inst.hashalg, 'md5')
-
- def test_class_implements_IAuthenticationPolicy(self):
- from zope.interface.verify import verifyClass
- from pyramid.interfaces import IAuthenticationPolicy
- verifyClass(IAuthenticationPolicy, self._getTargetClass())
-
- def test_instance_implements_IAuthenticationPolicy(self):
- from zope.interface.verify import verifyObject
- from pyramid.interfaces import IAuthenticationPolicy
- verifyObject(IAuthenticationPolicy, self._makeOne(None, None))
-
-class TestSHA512AuthTktAuthenticationPolicy(unittest.TestCase):
- def _getTargetClass(self):
- from pyramid.authentication import SHA512AuthTktAuthenticationPolicy
- return SHA512AuthTktAuthenticationPolicy
-
- def _makeOne(self, callback, cookieidentity, **kw):
- inst = self._getTargetClass()('secret', callback, **kw)
- inst.cookie = DummyCookieHelper(cookieidentity)
- return inst
-
- def test_is_subclass(self):
- from pyramid.authentication import BaseAuthTktAuthenticationPolicy
- inst = self._makeOne(None, None)
- self.assertTrue(isinstance(inst, BaseAuthTktAuthenticationPolicy))
-
- def test_sha512(self):
- inst = self._makeOne(None, None)
- self.assertEqual(inst.hashalg, 'sha512')
-
- def test_class_implements_IAuthenticationPolicy(self):
- from zope.interface.verify import verifyClass
- from pyramid.interfaces import IAuthenticationPolicy
- verifyClass(IAuthenticationPolicy, self._getTargetClass())
-
- def test_instance_implements_IAuthenticationPolicy(self):
- from zope.interface.verify import verifyObject
- from pyramid.interfaces import IAuthenticationPolicy
- verifyObject(IAuthenticationPolicy, self._makeOne(None, None))
-
-class TestBaseAutkTktAuthenticationPolicy(unittest.TestCase):
- def _getTargetClass(self):
- from pyramid.authentication import BaseAuthTktAuthenticationPolicy
- return BaseAuthTktAuthenticationPolicy
-
- def _makeOne(self, callback, cookieidentity, **kw):
- inst = self._getTargetClass()('secret', callback, **kw)
- inst.cookie = DummyCookieHelper(cookieidentity)
- return inst
-
def test_allargs(self):
# pass all known args
inst = self._getTargetClass()(
'secret', callback=None, cookie_name=None, secure=False,
include_ip=False, timeout=None, reissue_time=None,
+ hashalg='sha512',
)
self.assertEqual(inst.callback, None)
+ def test_hashalg_override(self):
+ # important to ensure hashalg is passed to cookie helper
+ inst = self._getTargetClass()('secret', hashalg='sha512')
+ self.assertEqual(inst.cookie.hashalg, 'sha512')
+
def test_unauthenticated_userid_returns_None(self):
request = DummyRequest({})
policy = self._makeOne(None, None)
@@ -586,6 +526,16 @@ class TestBaseAutkTktAuthenticationPolicy(unittest.TestCase):
result = policy.forget(request)
self.assertEqual(result, [])
+ def test_class_implements_IAuthenticationPolicy(self):
+ from zope.interface.verify import verifyClass
+ from pyramid.interfaces import IAuthenticationPolicy
+ verifyClass(IAuthenticationPolicy, self._getTargetClass())
+
+ def test_instance_implements_IAuthenticationPolicy(self):
+ from zope.interface.verify import verifyObject
+ from pyramid.interfaces import IAuthenticationPolicy
+ verifyObject(IAuthenticationPolicy, self._makeOne(None, None))
+
class TestAuthTktCookieHelper(unittest.TestCase):
def _getTargetClass(self):
from pyramid.authentication import AuthTktCookieHelper