terça-feira, 8 de janeiro de 2013

Autenticação do Django utilizando old_password do mysql

No mesmo diretório do seu arquivo settings.py crie um arquivo chamado hashers.py com o seguinte conteúdo:

from django.contrib.auth.hashers import BasePasswordHasher, mask_hash

from django.utils.datastructures import SortedDict
from django.utils.crypto import constant_time_compare
from django.utils.translation import ugettext_noop as _

class MySQLOldPasswordHasher(BasePasswordHasher):
    """
    Classe para criptografar as senhas utilizando o modelo da funcao old_password do mysql 4.x
    Class to encrypt the passwords using the model of mysql 4.x  old_password function
    """
    algorithm = "mysql_old_password"

    def salt(self):
        return ''

    def encode(self, password, salt):
        nr = 1345345333
        add = 7
        nr2 = 0x12345671

        for c in (ord(x) for x in password if x not in (' ', '\t')):
            nr^= (((nr & 63)+add)*c)+ (nr << 8) & 0xFFFFFFFF
            nr2= (nr2 + ((nr2 << 8) ^ nr)) & 0xFFFFFFFF
            add= (add + c) & 0xFFFFFFFF

        password = "%08x%08x" % (nr & 0x7FFFFFFF,nr2 & 0x7FFFFFFF)
        return "%s$%s" % (self.algorithm, password)

    def verify(self, password, encoded):
        encoded_2 = self.encode(password, '')
        return constant_time_compare(encoded, encoded_2)

    def safe_summary(self, encoded):
        return SortedDict([
            (_('algorithm'), self.algorithm),
            (_('hash'), mask_hash(encoded, show=3)),
        ])

Adicone esse novo hasher no início da setting PASSWORD_HASHERS no seu arquivo settings.py:

PASSWORD_HASHERS = (
    'app.hashers.MySQLOldPasswordHasher',
    'django.contrib.auth.hashers.BCryptPasswordHasher',
    'django.contrib.auth.hashers.PBKDF2PasswordHasher',
    'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
    'django.contrib.auth.hashers.SHA1PasswordHasher',
    'django.contrib.auth.hashers.MD5PasswordHasher',
    'django.contrib.auth.hashers.CryptPasswordHasher',
)

A função de login fica como de costume:

from django.contrib.auth import authenticate, logout, login as authlogin
from django.core.context_processors import csrf
def logar(request):
 if request.POST:
  c = {}
  c.update(csrf(request))
  r = request.POST

  usuario = r.get('usuario')
  senha = r.get('senha')

  u = authenticate(username=usuario, password=senha)

  if u is not None:
   c = u.get_profile()
   if c:
    if c.ativo:
     if u.is_active:
      authlogin(request, u)
      # USUARIO LOGADO COM SUCESSO
      return HttpResponse('1')
    # USUARIO INATIVO     
    return HttpResponse('-3')
  # USUARIO NAO ENCONTRADO / SENHA INCORRETA
  return HttpResponse('0')
 # REQUISICAO SEM POST
 return HttpResponse('-1')


Da mesma forma pode ser feito com outros hashers. Basta adicioná-los ao arquivo hashers.py e listá-los na ordem desejada no settings como vimos acima.

Uma coisa notada analisando os algoritmos de hash disponível no Django (disponíveis em django.contrib.auth.hashers), o password gerado automático que é salvo no banco possui 3 partes separadas pelo simbolo "$":

  • Algoritmo de criptografia utilizado
  • Número de iterações
  • Hash da senha
No exemplo acima que a função old_password() do mysql não possui iterações, a segunda parte é omitda.



hasta!