0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Python】OTP(ワンタイムパスコード)を実装する

Posted at

はじめに

今回はpyotpqrcodeを使ってOTP(ワンタイムパスコード)を使えるようにしていきます。

実行環境

  • Python 3.11
  • Windows 10

使用ライブラリ

  • pyotp
  • qrcode

ライブラリのインストール

今回のプログラムではpyotpqrcodeの2つの外部ライブラリを使用します。以下のコマンドでインストールしましょう。どちらのコマンドを使っても大丈夫です。

ライブラリのインストール
pip install pyotp
pip install qrcode

又は

pip install pyotp qrcode

実装

以下のコードを先程作成した.pyファイルにコピペしてください。
実行するとターミナルにシークレットキーが表示されます。
表示されたシークレットキーをOTPアプリに入力するか、生成されるotp_ar.pngのQRコードを読み取って登録!
OTPアプリに表示されたワンタイムパスコードを入力すると検証結果が表示されます。

プログラムをもう一度実行すると新しいシークレットキーが生成され、前回生成したシークレットキーは無効になります。

任意でコード内のexample@domain.comYourAppNameは変更してください。

コード全文
import pyotp
import qrcode

# ユーザーごとのシークレットキーを生成
secret = pyotp.random_base32()
print(f"Your secret key: {secret}")

# ワンタイムパスコードの生成
totp = pyotp.TOTP(secret)

# QRコード生成用のURL
url = totp.provisioning_uri(name="example@domain.com", issuer_name="YourAppName")
print(f"Provisioning URL: {url}")

# QRコードを生成して保存
qrcode.make(url).save("otp_qr.png")
print("QR code saved as otp_qr.png")

# ユーザー入力と検証
user_input = input("Enter the OTP: ")

if totp.verify(user_input):
    print("Login successful!")
else:
    print("Invalid OTP. Please try again.")

おわりに

今回はOTPを実装してみました。
こちらの記事がOTPを実装する際の参考になれば嬉しいです。

おまけ

実際に実装するとなると現在のコードでは毎回シークレットキーが生成されてしまうので、実務的には使いにくいです。
例えば、シークレットキーをtxtやjsonに保存し、それを再利用するのをおすすめします。
適当に作ってみたので置いときます。

おまけ
import pyotp
import qrcode
import os
import tkinter as tk
from PIL import Image, ImageTk
from tkinter import messagebox
import tkinter.simpledialog as simpledialog
import json

security_data_path = "security.json"

if not os.path.exists(security_data_path):
    data = {
        "OTP_secret_key": None
    }

    # JSONファイルへの書き込み
    with open(security_data_path, 'w', encoding='utf-8') as file:
        json.dump(data, file, ensure_ascii=False, indent=4)

# JSONファイルの読み込み
with open(security_data_path, 'r', encoding='utf-8') as file:
    security_data = json.load(file)

# シークレットキーの保存先
SECRET_KEY_FILE = "OTP_secret_key.txt"

if not security_data['OTP_secret_key'] == None:
    # シークレットキーを取得
    secret = security_data['OTP_secret_key']
else :
    secret = pyotp.random_base32()


# ワンタイムパスコードの生成
totp = pyotp.TOTP(secret)

# QRコード生成用のURL
url = totp.provisioning_uri(name="最強セキュリティ", issuer_name="mikan")

# QRコードを生成して保存(初回のみ)
if not security_data['OTP_secret_key'] == secret:
    qrcode.make(url).save("otp_qr.png")
    
    path = "otp_qr.png"
    
    # ボタンが押されたときにループを終了するフラグ
    loop_running = True

    def end_loop():
        # os.remove(path) QRコードを削除
        global loop_running
        loop_running = False
        root.destroy()  # ウィンドウを閉じる

    # メインウィンドウの作成
    root = tk.Tk()
    root.title("QR Code Display with Text")
    root.geometry("500x500")  # ウィンドウサイズ
    
    # ×ボタンを押されたときの処理を設定
    root.protocol("WM_DELETE_WINDOW", end_loop)
    
    # 画像のリサイズと読み込み
    image = Image.open(path)
    image = image.resize((300, 300))  # 幅300px、高さ300pxにリサイズ
    tk_image = ImageTk.PhotoImage(image)

    # Canvasの作成(画像とテキストを配置)
    canvas = tk.Canvas(root, width=500, height=400)
    canvas.pack()

    # テキストを画像の上部に描画(Canvas上部に表示)
    canvas.create_text(250, 30, text="以下のQRコードでOTPアプリに\n設定してください。", fill="black", font=("Arial", 20))

    # 画像をCanvasに描画(テキストの下に画像を配置)
    canvas.create_image(250, 225, image=tk_image)  # テキストから少し下に配置

    # OKボタンの作成
    ok_button = tk.Button(root, text="登録が終わった", command=end_loop)
    ok_button.pack(pady=20)

    # メインループ
    while loop_running:
        root.update()

# 新しく生成したシークレットキーをJSONに保存
if security_data['OTP_secret_key'] == None:
    security_data['OTP_secret_key'] = secret
    with open(security_data_path, 'w', encoding='utf-8') as file:
        json.dump(security_data, file, ensure_ascii=False, indent=4)

# ユーザー入力と検証
user_input = simpledialog.askstring('OTP入力', 'ワンタイムパスコードを入力してください:')

if totp.verify(user_input):
    messagebox.showinfo("成功", "OTP認証に成功しました。")
else:
    messagebox.showinfo("失敗", "OTP認証に失敗しました。")
    os._exit(0)

このコードでは初回実行時にシークレットキーが生成されsecurity.jsonに保存されます。その後生成されたQRコードを表示しています。
二回目以降からはsecurity.jsonに保存されたシークレットキーを使用し検証だけを行います。
それとtkinter使って少し使いやすくしました。

同じシークレットキーを使用する場合にはsecurity.jsonを削除しないでください。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?