summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.txt20
-rw-r--r--docs/api/authentication.rst2
-rw-r--r--docs/narr/security.rst4
-rw-r--r--docs/tutorials/wiki/authorization.rst18
-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.rst18
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/__init__.py4
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/__init__.py4
-rw-r--r--pyramid/authentication.py40
-rw-r--r--pyramid/tests/test_authentication.py12
11 files changed, 86 insertions, 48 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 8d5a00e77..45fc19762 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -43,6 +43,24 @@ 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
---------
@@ -50,7 +68,7 @@ Internals
move ``CyclicDependencyError`` from ``pyramid.config.util`` to
``pyramid.exceptions``, rename ``Singleton`` to ``Sentinel`` and move from
``pyramid.config.util`` to ``pyramid.config.util``; this is in an effort to
- move that stuff that may be an API one day out of ``pyramid.config.util,
+ move that stuff that may be an API one day out of ``pyramid.config.util``,
because that package should never be imported from non-Pyramid code.
TopologicalSorter is still not an API, but may become one.
diff --git a/docs/api/authentication.rst b/docs/api/authentication.rst
index e6ee5658b..49ee405eb 100644
--- a/docs/api/authentication.rst
+++ b/docs/api/authentication.rst
@@ -9,6 +9,8 @@ Authentication Policies
.. automodule:: pyramid.authentication
.. autoclass:: SHA512AuthTktAuthenticationPolicy
+ :members:
+ :inherited-members:
.. autoclass:: AuthTktAuthenticationPolicy
:members:
diff --git a/docs/narr/security.rst b/docs/narr/security.rst
index 07ec0f21e..3c9759e6c 100644
--- a/docs/narr/security.rst
+++ b/docs/narr/security.rst
@@ -90,9 +90,9 @@ For example:
:linenos:
from pyramid.config import Configurator
- from pyramid.authentication import AuthTktAuthenticationPolicy
+ from pyramid.authentication import SHA512AuthTktAuthenticationPolicy
from pyramid.authorization import ACLAuthorizationPolicy
- authentication_policy = AuthTktAuthenticationPolicy('seekrit')
+ authentication_policy = SHA512AuthTktAuthenticationPolicy('seekrit')
authorization_policy = ACLAuthorizationPolicy()
config = Configurator()
config.set_authentication_policy(authentication_policy)
diff --git a/docs/tutorials/wiki/authorization.rst b/docs/tutorials/wiki/authorization.rst
index 9e0bf0f09..e2c4637d7 100644
--- a/docs/tutorials/wiki/authorization.rst
+++ b/docs/tutorials/wiki/authorization.rst
@@ -134,15 +134,15 @@ Now add those policies to the configuration:
(Only the highlighted lines need to be added.)
-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.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
+We are enabling an ``SHA512AuthTktAuthenticationPolicy``, 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`
+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
``groupfinder()`` function that we created before.
Add permission declarations
diff --git a/docs/tutorials/wiki/src/authorization/tutorial/__init__.py b/docs/tutorials/wiki/src/authorization/tutorial/__init__.py
index 6989145d8..4c766fea2 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 AuthTktAuthenticationPolicy
+from pyramid.authentication import SHA512AuthTktAuthenticationPolicy
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 = AuthTktAuthenticationPolicy(secret='sosecret',
- callback=groupfinder)
+ authn_policy = SHA512AuthTktAuthenticationPolicy(secret='sosecret',
+ callback=groupfinder)
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 6989145d8..4c766fea2 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 AuthTktAuthenticationPolicy
+from pyramid.authentication import SHA512AuthTktAuthenticationPolicy
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 = AuthTktAuthenticationPolicy(secret='sosecret',
- callback=groupfinder)
+ authn_policy = SHA512AuthTktAuthenticationPolicy(secret='sosecret',
+ callback=groupfinder)
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 6b2d44410..e3087dea5 100644
--- a/docs/tutorials/wiki2/authorization.rst
+++ b/docs/tutorials/wiki2/authorization.rst
@@ -151,15 +151,15 @@ Now add those policies to the configuration:
(Only the highlighted lines need to be added.)
-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.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
+We are enabling an ``SHA512AuthTktAuthenticationPolicy``, 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`
+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
``groupfinder()`` function that we created before.
Add permission declarations
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py b/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py
index 8922a3cc0..585cdf884 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 AuthTktAuthenticationPolicy
+from pyramid.authentication import SHA512AuthTktAuthenticationPolicy
from pyramid.authorization import ACLAuthorizationPolicy
from sqlalchemy import engine_from_config
@@ -17,7 +17,7 @@ def main(global_config, **settings):
engine = engine_from_config(settings, 'sqlalchemy.')
DBSession.configure(bind=engine)
Base.metadata.bind = engine
- authn_policy = AuthTktAuthenticationPolicy(
+ authn_policy = SHA512AuthTktAuthenticationPolicy(
'sosecret', callback=groupfinder)
authz_policy = ACLAuthorizationPolicy()
config = Configurator(settings=settings,
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/__init__.py b/docs/tutorials/wiki2/src/tests/tutorial/__init__.py
index 8922a3cc0..585cdf884 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 AuthTktAuthenticationPolicy
+from pyramid.authentication import SHA512AuthTktAuthenticationPolicy
from pyramid.authorization import ACLAuthorizationPolicy
from sqlalchemy import engine_from_config
@@ -17,7 +17,7 @@ def main(global_config, **settings):
engine = engine_from_config(settings, 'sqlalchemy.')
DBSession.configure(bind=engine)
Base.metadata.bind = engine
- authn_policy = AuthTktAuthenticationPolicy(
+ authn_policy = SHA512AuthTktAuthenticationPolicy(
'sosecret', callback=groupfinder)
authz_policy = ACLAuthorizationPolicy()
config = Configurator(settings=settings,
diff --git a/pyramid/authentication.py b/pyramid/authentication.py
index 7e7ee4dfb..2e58c70e7 100644
--- a/pyramid/authentication.py
+++ b/pyramid/authentication.py
@@ -6,8 +6,8 @@ import base64
import datetime
import re
import time as time_mod
-import warnings
+from zope.deprecation import deprecated
from zope.interface import implementer
from pyramid.compat import (
@@ -407,7 +407,7 @@ class RemoteUserAuthenticationPolicy(CallbackAuthenticationPolicy):
return []
class BaseAuthTktAuthenticationPolicy(CallbackAuthenticationPolicy):
- """ A :app:`Pyramid` :term:`authentication policy` which
+ """A :app:`Pyramid` :term:`authentication policy` which
obtains data from a Pyramid "auth ticket" cookie.
Constructor Arguments
@@ -562,8 +562,8 @@ class BaseAuthTktAuthenticationPolicy(CallbackAuthenticationPolicy):
@implementer(IAuthenticationPolicy)
class SHA512AuthTktAuthenticationPolicy(BaseAuthTktAuthenticationPolicy):
- __doc__ = """
- .. versionadded:: 1.4
+ __doc__ = """.. versionadded:: 1.4
+
""" + BaseAuthTktAuthenticationPolicy.__doc__
hashalg = 'sha512'
@@ -578,13 +578,20 @@ class AuthTktAuthenticationPolicy(BaseAuthTktAuthenticationPolicy):
""" + BaseAuthTktAuthenticationPolicy.__doc__
hashalg = 'md5'
- def __init__(self, *a, **kw):
- warnings.warn('Deprecated due to the usage of md5, '
- 'hash function known to have collisions. '
- 'Use SHA512AuthTktAuthenticationPolicy instead.',
- DeprecationWarning,
- stacklevel=2)
- super(AuthTktAuthenticationPolicy, self).__init__(*a, **kw)
+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'')
@@ -654,7 +661,7 @@ class BadTicket(Exception):
Exception.__init__(self, msg)
# this function licensed under the MIT license (stolen from Paste)
-def parse_ticket(secret, ticket, ip, hashalg):
+def parse_ticket(secret, ticket, ip, hashalg='md5'):
"""
Parse the ticket, returning (timestamp, userid, tokens, user_data).
@@ -694,7 +701,8 @@ def parse_ticket(secret, ticket, ip, hashalg):
return (timestamp, userid, tokens, user_data)
# this function licensed under the MIT license (stolen from Paste)
-def calculate_digest(ip, timestamp, secret, userid, tokens, user_data, hashalg):
+def calculate_digest(ip, timestamp, secret, userid, tokens, user_data,
+ hashalg='md5'):
secret = bytes_(secret, 'utf-8')
userid = bytes_(userid, 'utf-8')
tokens = bytes_(tokens, 'utf-8')
@@ -749,7 +757,8 @@ class AuthTktCookieHelper(object):
def __init__(self, secret, cookie_name='auth_tkt', secure=False,
include_ip=False, timeout=None, reissue_time=None,
- max_age=None, http_only=False, path="/", wild_domain=True, hashalg='md5'):
+ max_age=None, http_only=False, path="/", wild_domain=True,
+ hashalg='md5'):
self.secret = secret
self.cookie_name = cookie_name
self.include_ip = include_ip
@@ -945,7 +954,8 @@ class AuthTktCookieHelper(object):
user_data=user_data,
cookie_name=self.cookie_name,
secure=self.secure,
- hashalg=self.hashalg)
+ hashalg=self.hashalg
+ )
cookie_value = ticket.cookie_value()
return self._get_cookies(environ, cookie_value, max_age)
diff --git a/pyramid/tests/test_authentication.py b/pyramid/tests/test_authentication.py
index b1a88b915..8b640b860 100644
--- a/pyramid/tests/test_authentication.py
+++ b/pyramid/tests/test_authentication.py
@@ -430,7 +430,15 @@ class TestRemoteUserAuthenticationPolicy(unittest.TestCase):
result = policy.forget(request)
self.assertEqual(result, [])
-class TestAutkTktAuthenticationPolicy(unittest.TestCase):
+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
@@ -459,7 +467,7 @@ class TestAutkTktAuthenticationPolicy(unittest.TestCase):
from pyramid.interfaces import IAuthenticationPolicy
verifyObject(IAuthenticationPolicy, self._makeOne(None, None))
-class TestSHA512AutkTktAuthenticationPolicy(unittest.TestCase):
+class TestSHA512AuthTktAuthenticationPolicy(unittest.TestCase):
def _getTargetClass(self):
from pyramid.authentication import SHA512AuthTktAuthenticationPolicy
return SHA512AuthTktAuthenticationPolicy