La autenticación en django funciona a través de dos middleware dependientes, SessionMiddleware y AuthenticationMiddleware. Básicamente, cuando el usuario hace un login exitoso, se guardan tres valores en la sesión:
-
_auth_user_id. PK del objeto User
-
_auth_user_backend.Dotted path del backend de autenticación. Por defecto: django.contrib.auth.backends.ModelBackend
-
_auth_user_hash. Hash de la contraseña del usuario
El hash es la representación hexadecimal (string) de un hasheado con sal de la contraseña del usuario:
| def get_session_auth_hash(self):
"""
Return an HMAC of the password field.
"""
key_salt = "django.contrib.auth.models.AbstractBaseUser.get_session_auth_hash"
return salted_hmac(key_salt, self.password).hexdigest()
|
En todos los requests AuthenticationMiddleware es el encargado de establecer cuál es el usuario request.user. Para ello llama a la función get_user del módulo django.contrib.auth y lo que hace es comprobar que la contraseña hasheada guardada en la sesión es la misma que se deriva de hacer el mismo cálculo sobre la guardada en la base de datos. Para ello usa la función constant_time_compare del módulo django.utils.crypto. Se usa esta función para que el tiempo que tome la comparación sea constante y se minimice la posibilidad de un timing attack. Si la comparación no es correcta establece como valor de request.user AnonymousUser.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | # django/contrib/auth/middleware.py
def get_user(request):
if not hasattr(request, '_cached_user'):
request._cached_user = auth.get_user(request)
return request._cached_user
class AuthenticationMiddleware(MiddlewareMixin):
def process_request(self, request):
assert hasattr(request, 'session'), (
"The Django authentication middleware requires session middleware "
"to be installed. Edit your MIDDLEWARE%s setting to insert "
"'django.contrib.sessions.middleware.SessionMiddleware' before "
"'django.contrib.auth.middleware.AuthenticationMiddleware'."
) % ("_CLASSES" if settings.MIDDLEWARE is None else "")
request.user = SimpleLazyObject(lambda: get_user(request))
|
Ver tb:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 | # django/contrib/auth/init.py
def get_user(request):
"""
Return the user model instance associated with the given request session.
If no user is retrieved, return an instance of `AnonymousUser`.
"""
from .models import AnonymousUser
user = None
try:
user_id = _get_user_session_key(request)
backend_path = request.session[BACKEND_SESSION_KEY]
except KeyError:
pass
else:
if backend_path in settings.AUTHENTICATION_BACKENDS:
backend = load_backend(backend_path)
user = backend.get_user(user_id)
# Verify the session
if hasattr(user, 'get_session_auth_hash'):
session_hash = request.session.get(HASH_SESSION_KEY)
session_hash_verified = session_hash and constant_time_compare(
session_hash,
user.get_session_auth_hash()
)
if not session_hash_verified:
request.session.flush()
user = None
return user or AnonymousUser()
|