diff options
| author | Michael Merickel <michael@merickel.org> | 2012-11-04 11:19:41 -0600 |
|---|---|---|
| committer | Michael Merickel <michael@merickel.org> | 2012-11-04 11:19:41 -0600 |
| commit | 19b8207ff1e959669d296407ed112545364a495d (patch) | |
| tree | 02ce76919f96d9f3f74bf1db99e00be3598183c7 | |
| parent | 9149461a45399a9f7f23322daa7c02e1397f9a91 (diff) | |
| download | pyramid-19b8207ff1e959669d296407ed112545364a495d.tar.gz pyramid-19b8207ff1e959669d296407ed112545364a495d.tar.bz2 pyramid-19b8207ff1e959669d296407ed112545364a495d.zip | |
merged SHA512AuthTktAuthenticationPolicy into AuthTktAuthenticationPolicy
AuthTktAuthenticationPolicy now accepts a hashalg parameter and is no
longer deprecated. Docs recommend overriding hashalg and using 'sha512'.
| -rw-r--r-- | CHANGES.txt | 22 | ||||
| -rw-r--r-- | docs/api/authentication.rst | 4 | ||||
| -rw-r--r-- | docs/narr/security.rst | 10 | ||||
| -rw-r--r-- | docs/tutorials/wiki/authorization.rst | 4 | ||||
| -rw-r--r-- | docs/tutorials/wiki/src/authorization/tutorial/__init__.py | 6 | ||||
| -rw-r--r-- | docs/tutorials/wiki/src/tests/tutorial/__init__.py | 6 | ||||
| -rw-r--r-- | docs/tutorials/wiki2/authorization.rst | 4 | ||||
| -rw-r--r-- | docs/tutorials/wiki2/src/authorization/tutorial/__init__.py | 6 | ||||
| -rw-r--r-- | docs/tutorials/wiki2/src/tests/tutorial/__init__.py | 6 | ||||
| -rw-r--r-- | pyramid/authentication.py | 68 | ||||
| -rw-r--r-- | pyramid/tests/test_authentication.py | 82 |
11 files changed, 72 insertions, 146 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 45fc19762..390d3c3e3 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 @@ -43,24 +47,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 |
