安全性を高めるために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
管理者がコメントの内容を確認中・・・