python:2.7 Django:1.6 pip install redis
When it comes to dedicated pages, login + cookies are common with accounts, but there are times when you want to issue a dedicated URL for each user without logging in. For example, we do not issue an account to the user because it is common not to issue a login ID for emails at the time of initial registration or for recent smartphone games.
I'm afraid of dictionary attacks and brute force attacks, so it should be okay if you take measures against it
↓ It's probably faster to read the code
m.py
from uuid import uuid4
# 1.Issuance of disposable ID
_uuid4 = str(uuid4())
print 'uuid4:', _uuid4
# 2.Disposable ID checksum
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 issuance
print "http://hoge/page?token={}%20cs={}".format(_uuid4, checksum)
Execution result
> 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
When reviewing, it is often said: "Isn't it dangerous to be analyzed as a checksum? 』
I will fix the logic by enduring the place where I want to say that there is no strange hacker who analyzes our depopulated system.
m.py
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
import binascii
from uuid import uuid4
def checksum_base64_crc(_str):
"""
Inverts the entered string, base64 encodes it, and returns a crc32 checksum
:param _str: str
:rtype : int
"""
#Invert
_str_r = _str[::-1]
#Base64 encode and take crc32 checksum
return binascii.crc32(binascii.b2a_base64(_str_r))
# 1.Issuance of disposable ID
_uuid4 = str(uuid4())
print 'uuid4:', _uuid4
# 2.Disposable ID checksum
checksum = checksum_base64_crc(_uuid4)
print 'checksum:', checksum
# 3.URL issuance
print "http://hoge/page?token={}%20cs={}".format(_uuid4, checksum)
Execution result 2
>python m.py
uuid4: 6a1d87e0-0518-4aa0-a2ca-cced091f254b
checksum: -2147023629
http://hoge/page?token=6a1d87e0-0518-4aa0-a2ca-cced091f254b%20cs=-2147023629
If too many tokens are issued, there is a fear of DB bloat. It is convenient to manage data that disappears over time and has no problem with redis. Since the data will disappear over time, it will be easier later if you keep the URL issued as a countermeasure against inquiries in the log.
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):
"""
Inverts the entered string, base64 encodes it, and returns a crc32 checksum
:param _str: str
:rtype : int
"""
#Invert
_str_r = _str[::-1]
#Base64 encode and take crc32 checksum
return binascii.crc32(binascii.b2a_base64(_str_r))
class IncorrectCheckSumError(Exception):
#Wrong checksum
pass
class TokenExpiredError(Exception):
#Token expired
pass
class TokenRepository(object):
"""
Token for URL is stored and managed in Redis for a certain period of time
"""
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):
"""
Return the associated value
:param token:
:return: str
"""
value = self.client.get(self.get_key(token))
return str(value)
def exist(self, token):
"""
Check if the token exists in the repository
:param token: unicode or string
:rtype: bool
"""
return bool(self.get(token))
class TokenManager(object):
@classmethod
def validate(cls, token, check_sum):
"""
Check if the token is correct
:param token: unicode or str
:param check_sum: int
:rtype : bool
"""
token = str(token)
check_sum = int(check_sum)
#Check if the token is correct with a checksum
if checksum_base64_crc(token) != check_sum:
raise IncorrectCheckSumError
user_id = TokenRepository().get(token)
return bool(user_id)
@classmethod
def generate(cls, user_id):
"""
Generate token and checksum
:param user_id:
:return: str, int
"""
#Generate
token = str(uuid4())
#Generated token and user_Save id linked to redis
TokenRepository().set(token, user_id)
return token, checksum_base64_crc(token)
@classmethod
def get_user_id_from_token(cls, token, check_sum):
"""
from token to user_Subtract id
: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.Issue a disposable URL
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.Road test
assert TokenManager.get_user_id_from_token(_token, check_sum) == str(user_id)
View on Django side
# -*- 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)
#from token to user_Subtract id
error_message = None
message = None
try:
user_id = TokenManager.get_user_id_from_token(token, check_sum)
message = "Your user_id is{}is".format(user_id)
except IncorrectCheckSumError:
error_message = "Illegal parameter"
except TokenExpiredError:
error_message = "Page expired"
if error_message:
message = error_message
return HttpResponse(message,
mimetype='text/html; charset=UTF-8',
*args, **kwargs)
URL issuance
>python m.py
http://hogehoge.com/hoge?token=e359b20e-4c60-48da-9294-2ea9fcca0a6c&cs=-2066385284
■ Access with a browser Normal system
■ Access with a browser Abnormal system
Recommended Posts