[PYTHON] Django + redis Geben Sie eine dedizierte URL aus, die 1 Stunde lang gültig ist

Umgebung

python:2.7 Django:1.6 pip install redis

Zeitpunkt, zu dem Sie eine dedizierte URL ausgeben möchten

Wenn es um dedizierte Seiten geht, sind Login + Cookies bei Konten üblich. Manchmal möchten Sie jedoch für jeden Benutzer eine dedizierte URL eingeben, ohne sich anzumelden. Beispielsweise stellen wir Benutzern kein Konto zur Verfügung, da es üblich ist, zum Zeitpunkt der Erstregistrierung oder für aktuelle Smartphone-Spiele keine Anmelde-ID für E-Mails auszugeben.

Mögliche Angriffe und Gegenmaßnahmen

Ich habe Angst vor Wörterbuchangriffen und Round-Robin-Angriffen, daher sollte es in Ordnung sein, wenn Sie Maßnahmen dagegen ergreifen

  1. Als Gegenmaßnahme gegen Wörterbuchangriffe muss die URL-Zeichenfolge eine verfügbare Zeichenfolge sein, die nicht aus der Benutzer-ID abgeleitet werden kann.
  2. Fügen Sie eine Prüfsumme in die in 1 ausgegebene Einweg-Zeichenfolge ein, um Gegenmaßnahmen gegen Round-Robin-Angriffe zu ergreifen

↓ Es ist wahrscheinlich schneller, den Code zu lesen

m.py


from uuid import uuid4

# 1.Ausstellung eines Einwegausweises
_uuid4 = str(uuid4())
print 'uuid4:', _uuid4

# 2.Daumen zur Überprüfung der Einweg-ID
uuid4_char_list = [ord(_char) for _char in _uuid4]
print 'uuid4_char_list:', uuid4_char_list
checksum = sum(uuid4_char_list)
print 'checksum:', checksum

# 3.URL-Ausgabe
print "http://hoge/page?token={}%20cs={}".format(_uuid4, checksum)

Ausführungsergebnis


> python m.py 
uuid4: 6da25bb0-5d9c-4c1e-becc-51e3d5078fe4
uuid4_char_list: [54, 100, 97, 50, 53, 98, 98, 48, 45, 53, 100, 57, 99, 45, 52, 99, 49, 101, 45, 98, 101, 99, 99, 45, 53, 49, 101, 51, 100, 53, 48, 55, 56, 102, 101, 52]
checksum: 2606
http://hoge/page?token=6da25bb0-5d9c-4c1e-becc-51e3d5078fe4%20cs=2606

Wenn Sie diesen Mechanismus aus Sicherheitsgründen überprüfen ...

Bei der Überprüfung wird oft gesagt: "Ist es nicht gefährlich, als Prüfsumme analysiert zu werden? 』
Ich werde die Logik korrigieren, indem ich den Ort ertrage, an dem ich sagen möchte, dass es keinen seltsamen Hacker gibt, der unser entvölkertes System analysiert.

m.py


# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals

import binascii
from uuid import uuid4


def checksum_base64_crc(_str):
    """
Invertiert die eingegebene Zeichenfolge, base64 codiert sie und gibt die crc32-Prüfsumme zurück
    :param _str: str
    :rtype : int
    """
    #Umkehren
    _str_r = _str[::-1]

    #base64 codieren und crc32-Prüfsumme nehmen
    return binascii.crc32(binascii.b2a_base64(_str_r))


# 1.Ausstellung eines Einwegausweises
_uuid4 = str(uuid4())
print 'uuid4:', _uuid4

# 2.Daumen zur Überprüfung der Einweg-ID
checksum = checksum_base64_crc(_uuid4)
print 'checksum:', checksum

# 3.URL-Ausgabe
print "http://hoge/page?token={}%20cs={}".format(_uuid4, checksum)

Ausführungsergebnis 2


>python m.py 
uuid4: 6a1d87e0-0518-4aa0-a2ca-cced091f254b
checksum: -2147023629
http://hoge/page?token=6a1d87e0-0518-4aa0-a2ca-cced091f254b%20cs=-2147023629

Von redis ausgegebene URLs verwalten.

Wenn zu viele Token ausgegeben werden, befürchte ich, dass die DB wächst. Es ist praktisch, Daten zu verwalten, die mit der Zeit verschwinden und keine Probleme mit Redis haben. Da die Daten mit der Zeit verschwinden, ist es später einfacher, wenn Sie die URL als Gegenmaßnahme gegen Anfragen im Protokoll beibehalten.

Fertiges Produkt

token_manager.py


# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
import binascii
from redis import Redis
from uuid import uuid4


def checksum_base64_crc(_str):
    """
Invertiert die eingegebene Zeichenfolge, base64 codiert sie und gibt die crc32-Prüfsumme zurück
    :param _str: str
    :rtype : int
    """
    #Umkehren
    _str_r = _str[::-1]

    #base64 codieren und crc32-Prüfsumme nehmen
    return binascii.crc32(binascii.b2a_base64(_str_r))


class IncorrectCheckSumError(Exception):
    #Falsche Prüfsumme
    pass


class TokenExpiredError(Exception):
    #Token abgelaufen
    pass


class TokenRepository(object):
    """
Das Token für die URL wird für einen bestimmten Zeitraum in Redis gespeichert und verwaltet
    """
    EXPIRE_SEC = 3600
    _KEY_BASE = "project_name/url/disposable/{}"
    _cli = None

    @property
    def client(self):
        """
        :rtype : Redis
        """
        if self._cli is None:
            self._cli = Redis(host='localhost', port=6379, db=10)
            return self._cli
        return self._cli

    def get_key(self, token):
        return self._KEY_BASE.format(str(token))

    def set(self, token, value):
        self.client.setex(self.get_key(token), value, self.EXPIRE_SEC)
        return

    def get(self, token):
        """
Gibt den zugehörigen Wert zurück
        :param token:
        :return: str
        """
        value = self.client.get(self.get_key(token))
        return str(value)

    def exist(self, token):
        """
Überprüfen Sie, ob das Token im Repository vorhanden ist
        :param token: unicode or string
        :rtype: bool
        """
        return bool(self.get(token))


class TokenManager(object):
    @classmethod
    def validate(cls, token, check_sum):
        """
Überprüfen Sie, ob das Token korrekt ist
        :param token: unicode or str
        :param check_sum: int
        :rtype : bool
        """
        token = str(token)
        check_sum = int(check_sum)

        #Überprüfen Sie, ob das Token mit der Prüfsumme korrekt ist
        if checksum_base64_crc(token) != check_sum:
            raise IncorrectCheckSumError

        user_id = TokenRepository().get(token)
        return bool(user_id)

    @classmethod
    def generate(cls, user_id):
        """
Token und Prüfsumme generieren
        :param user_id:
        :return: str, int
        """
        #Generieren
        token = str(uuid4())

        #Token und Benutzer generiert_Speichern Sie die ID, indem Sie sie mit redis verknüpfen
        TokenRepository().set(token, user_id)

        return token, checksum_base64_crc(token)

    @classmethod
    def get_user_id_from_token(cls, token, check_sum):
        """
vom Token zum Benutzer_ID subtrahieren
        :param token: str or unicode
        :param check_sum: int
        :rtype: str
        """
        token = str(token)
        if not cls.validate(token, check_sum):
            raise TokenExpiredError
        return TokenRepository().get(token)


# 1.Geben Sie eine Einweg-URL ein
url_base = "http://hogehoge.com/hoge?token={}&cs={}"
user_id = "1111222333"
_token, check_sum = TokenManager.generate(user_id)
url = url_base.format(_token, str(check_sum))
print url

# 2.Straßentest
assert TokenManager.get_user_id_from_token(_token, check_sum) == str(user_id)

Blick auf die Django Seite


# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from django.http import HttpResponse
from django.views.generic import View


class ExclusiveView(View):
    def get(self, request, *args, **kwargs):
        # HTTP GET
        token = request.GET.get("token", None)
        check_sum = request.GET.get("cs", None)

        #vom Token zum Benutzer_ID subtrahieren
        error_message = None
        message = None
        try:
            user_id = TokenManager.get_user_id_from_token(token, check_sum)
            message = "Ihr Benutzer_ID ist{}ist".format(user_id)
        except IncorrectCheckSumError:
            error_message = "Unzulässiger Parameter"
        except TokenExpiredError:
            error_message = "Seite abgelaufen"
        if error_message:
            message = error_message

        return HttpResponse(message,
                            mimetype='text/html; charset=UTF-8',
                            *args, **kwargs)

Ausführungsergebnis

URL-Ausgabe


>python m.py 
http://hogehoge.com/hoge?token=e359b20e-4c60-48da-9294-2ea9fcca0a6c&cs=-2066385284

■ Zugriff mit dem Browser Normales System スクリーンショット 2015-10-15 15.20.04.png

■ Zugriff mit dem Browser Anormales System スクリーンショット 2015-10-15 15.20.16.png

Recommended Posts

Django + redis Geben Sie eine dedizierte URL aus, die 1 Stunde lang gültig ist
Erstellen Sie ein Modell für Ihren Django-Zeitplan
Befehle zum Erstellen eines neuen Django-Projekts
Geben Sie eine signierte URL mit AWS SQS aus
Schreiben Sie kurz if-else von Django Template
Erstellen Sie mit Django ein Dashboard für Netzwerkgeräte!