安全性を高めるためにGoogle Authenticatorを使って2段階認証の実装を、
Python3.6 + Django2.0で行ってみた。
以下のURLにあるソースコードを見ながら実装した。
https://github.com/shinsaka/googleauthenticator_demo
尚、以下に記載しているコードは2段階認証の主な処理の部分だけの紹介であり、Djangoを使って、簡単なアプリケーションを作成した経験者向けである。
また、当ソースコードにおいて、意見があればコメントしていただきたい。
当ソースコードをまねして実装して、何かしら問題があったとしても、責任を負いません。
自己責任でお願いします。
login.html(一部抜粋)
<form method="post" action="{% url 'manager:login' %}">
{% csrf_token %}
<table>
<tr>
<td>ログインID</td>
<td><input type="text" name="username" required id="id_username" value="{{username}}"/></td>
</tr>
<tr>
<td>パスワード</td>
<td><input type="password" name="password" required id="id_password" value="{{password}}"/></td>
</tr>
{% if twoAuth == 1 %}
<tr>
<td>トークン</td>
<td><input type="password" name="auth_token" required id="id_auth_token" /></td>
</tr>
{% endif %}
</table>
<input type="hidden" name="twoAuth" id="id_twoAuth" value="{{twoAuth}}" />
<input type="submit" value="login" />
</form>
行った実装について、
ログインIDとパスワードを入力し、ログインボタンを押す。
2段階認証が設定してない場合は、そのままログイン。
2段階認証が設定済みであれば、トークンの入力項目が表示され、2段階認証のコードが求められる。
twoAuth.html(一部抜粋)
<form method="post" action="{% url 'manager:twoAuth' %}">
{% csrf_token %}
{% if request.session.secret == 'registried' %}
<div>
<p>2段階認証は設定済みです。</p>
<p>解除すると、なりすまし等の不正アクセスが発生するおそれがあります。</p>
<input type="submit" value="解除" />
</div>
{% else %}
<p style="color: red;">{{ msg }}</p>
<div>
<p>1. 下記のQRコードを読取り、Google認証システムへ登録してください。</p>
<img src="data:image/png;base64,{{ request.session.img }}" alt="qrcode" width="200"/>
</div>
<div>
<p>2. Google認証システムに表示されているコードを入力してください。</p>
<input type="text" name="token" required id="id_token" />
</div>
<div>
<input type="hidden" name="code" id="id_code" value="{{code}}" />
<input type="submit" value="設定" />
</div>
{% endif %}
</from>
こちらは2段階認証の設定処理。
QRコードを読み取り、コードを入力して設定する方法とした。
もちろん解除もできるようにした。
model.py
class TwoAuth(models.Model): username = models.CharField(max_length=100) secret = models.CharField(max_length=64)
本来Djangoには、ログインユーザを管理するためのUserというmodelを使って認証を行うことができるが、User.usernameと特定のコードだけを持つTwoAuthを作成した。
(もっといい方法があるけど、知恵不足である。)
view.py ( 2段階認証設定側)
def CMStwoAuth(request):
if not request.user.is_authenticated:
return HttpResponseRedirect('/cmsadmin/logout')
if request.method == 'POST':
if request.session['secret'] != 'registried':
token = request.POST['token']
inputToken = ' '.join([token[:3], token[3:]])
value = str(utils.get_token(request.session['secret'])).zfill(6)
checkToken = ' '.join([value[:3], value[3:]])
if inputToken != checkToken:
return render(request, 'manager/twoAuth.html', {'msg': u'トークンが一致しません。'})
data = TwoAuth()
data.username = request.user.username
data.secret = request.session['secret']
data.save()
request.session['secret'] = 'registried'
else:
data = TwoAuth.objects.filter(username=request.user.username).delete()
secret = utils.get_secret()
request.session['secret'] = secret
request.session['img'] = utils.get_image_b64(utils.get_auth_url(request.user.username, secret))
else:
try:
data = TwoAuth.objects.filter(username=request.user.username)
except TwoAuth.DoesNotExist:
data = TwoAuth()
if data is None or len(data) == 0:
secret = utils.get_secret()
request.session['secret'] = secret
request.session['img'] = utils.get_image_b64(utils.get_auth_url(request.user.username, secret))
else:
request.session['secret'] = 'registried'
return render(request, 'manager/twoAuth.html', {'msg': ''})
TwoAuthでレコードが存在しない場合は2段階認証が設定されていないため、
新たなsecretコードとQRコードをセッションに保持して画面を表示する。
2段階認証を設定したら、そのsecretコードをTwoAuthに保管する。
解除する場合は、TwoAuth内のsecretを持つレコードを削除する。
view.py (ログイン処理)
def loginAuth(request, username, password):
user = authenticate(request, username = username, password = password)
if user is not None:
login(request, user)
return user
else:
return None
def CMSLogin(request):
if request.method == 'POST':
username = request.POST['username']
password = request.POST['password']
print(request.POST['twoAuth'])
if request.POST['twoAuth'] == '0':
try:
data = TwoAuth.objects.filter(username=username)
if data is not None and len(data) != 0:
return render(request, 'manager/login.html', { 'msg': '', 'twoAuth': 1, 'username': username, 'password': password })
else:
user = loginAuth(request, username, password)
if user is not None:
return render(request, 'manager/toppage.html', {'user': user })
else:
return render(request, 'manager/login.html', { 'msg': u'ユーザID、あるいはパスワードが一致しません。', 'twoAuth': 0, 'username': '', 'password': ''})
except TwoAuth.DoesNotExist:
user = loginAuth(request, username, password)
if user is not None:
return render(request, 'manager/toppage.html', {'user': user })
else:
return render(request, 'manager/login.html', { 'msg': u'ユーザID、あるいはパスワードが一致しません。', 'twoAuth': 0, 'username': '', 'password': ''})
else:
try:
data = TwoAuth.objects.filter(username=username)
token = request.POST['auth_token']
inputToken = ' '.join([token[:3], token[3:]])
value = str(utils.get_token(data[0].secret)).zfill(6)
checkToken = ' '.join([value[:3], value[3:]])
if inputToken == checkToken:
user = loginAuth(request, username, password)
if user is not None:
return render(request, 'manager/toppage.html', {'user': user })
else:
return render(request, 'manager/login.html', { 'msg': u'ユーザID、あるいはパスワードが一致しません。', 'twoAuth': 0, 'username': '', 'password': ''})
else:
return render(request, 'manager/login.html', { 'msg': u'ユーザID、あるいはパスワードが一致しません。', 'twoAuth': 0, 'username': '', 'password': ''})
except TwoAuth.DoesNotExist:
return render(request, 'manager/login.html', { 'msg': u'ユーザID、あるいはパスワードが一致しません。', 'twoAuth': 0, 'username': '', 'password': ''})
else:
return render(request, 'manager/login.html', { 'msg': '', 'twoAuth': 0, 'username': '', 'password': '' })
ログイン処理が、それなりに汚いコードになってしまった。
(もう少し手直ししたいところだが、残念なことにエレガントに仕上げる知恵がない。)
Comment
2024年11月2日16:50 info@bmwmir.ru
管理者がコメントの内容を確認中・・・
2024年7月31日2:12 GEAVOUS@mailnest.xyz
管理者がコメントの内容を確認中・・・
2024年1月13日3:56 Dopchoolf@hmaill.xyz
管理者がコメントの内容を確認中・・・