OTP(일회용 비밀번호) 인증 시스템은 비밀번호가 유출된 경우에도 보안 조치를 강화하는 데 중요한 역할을 합니다. 이러한 시스템은 한 번만 액세스 권한을 부여하는 고유 코드를 생성하여 사용자가 여러 개의 비밀번호를 외울 필요가 없습니다. 또한 무단 침입에 대한 추가적인 보호 계층을 제공하는 동시에 피싱 사기의 희생양이 될 가능성을 최소화합니다.

파이썬을 활용한 일회용 비밀번호(OTP) 인증 프로세스를 개발하려면 지정된 모바일 장치로 OTP를 생성하고 전송하는 시스템을 구현하여 2분이라는 제한된 시간 동안 활성 상태를 유지하도록 할 수 있습니다. 또한, 시스템 내에 메커니즘을 통합하면 잘못된 OTP를 연속으로 세 번 입력하면 사용자 계정을 잠그는 방식으로 보안을 보장할 수 있습니다.

Tkinter, Twilio 및 랜덤 모듈 설치

Tkinter를 활용하면 데스크톱 애플리케이션을 위한 그래픽 사용자 인터페이스를 만들 수 있으며, 푸시 버튼, 텍스트 필드, 스크롤 가능한 영역과 같은 다양한 사용자 지정 위젯을 제공하여 개발을 용이하게 할 수 있습니다.

Twilio 모듈을 사용하면 문자 메시지, 멀티미디어 메시징, 음성 통화 및 인증을 포함한 고급 커뮤니케이션 기능을 애플리케이션 프레임워크 내에서 직접 원활하게 통합할 수 있습니다. 이는 번호 할당, 사용자 지정 가능한 메시지 형식, 통화 아카이빙과 같은 최첨단 기능을 갖춘 강력한 클라우드 기반 아키텍처를 통해 이루어집니다.

Twilio 및 Tkinter 라이브러리를 시스템에 통합하려면 터미널 환경에서 다음 명령을 실행하세요:

 pip install twilio tk 

Random 모듈은 의사 난수 생성을 용이하게 하는 Python 프로그래밍 언어의 본질적인 구성 요소입니다. 이 다목적 모듈을 통해 사용자는 필요에 따라 임의의 정수, 부동 소수점 값 및 기타 데이터 유형을 생성할 수 있습니다. 또한 목록에서 무작위 요소를 선택하고, 셔플을 통해 목록 순서를 조작하고, 주사위 굴리기, 무작위로 생성된 비밀번호 등 다양한 시뮬레이션을 생성할 수 있습니다.

Twilio API 생성 및 전화번호 받기

모바일 장치로 일회용 비밀번호(OTP)를 전송하는 데 Twilio를 활용하려면 Twilio 전화번호 외에 인증 자격 증명을 받아야 합니다. 이를 위해서는 다음 단계를 따르세요:

⭐ Twilio 계정에 가입하고 Twilio 콘솔 을 방문하세요.

⭐ 아래로 스크롤하여 전화 번호 받기 버튼을 클릭합니다. 생성된 전화번호를 복사합니다.

⭐ 아래로 스크롤하여 계정 정보 섹션으로 이동합니다.계정 SID와 인증 토큰을 복사합니다.

애플리케이션 구조 구축하기

Python을 사용하여 일회용 비밀번호(OTP) 인증 시스템을 구축하기 위한 전체 코드베이스는 이 Github 리포지토리에서 확인할 수 있습니다.

먼저 필요한 모듈을 가져와서 인증 세부 정보를 설정합니다. 그런 다음 API 요청을 위한 게이트웨이로 Twilio 클라이언트를 초기화하고 만료 기간을 2분으로 구성합니다.

`OTPVerification` 클래스의 인스턴스를 생성하려면 먼저 파이썬의 클래스 구문을 사용하여 클래스를 정의해야 합니다. 그런 다음 `__init__()` 메서드를 사용하여 이 코드 스니펫의 뒷부분에서 사용될 `self.window`와 같은 속성에 대한 기본값을 설정할 수 있습니다. 또한 `app.root.title(“OTP 인증”)`을 호출하여 루트 창을 초기화해야 합니다. 이렇게 하면 메인 창의 제목 표시줄 텍스트가 “OTP 확인”으로 설정됩니다. 마지막으로, `self.width`와 `self.height`에 값을 할당하여 애플리케이션의 기본 치수를 확인합니다.

 import tkinter as tk
from tkinter import messagebox
from twilio.rest import Client
import random
import threading
import time

account_sid = "YOUR_ACCOUNT_SID"
auth_token = "YOUR_AUTH_TOKEN"
client = Client(account_sid, auth_token)
expiration_time = 120

class OTPVerification:
    def __init__(self, master):
        self.master = master
        self.master.title('OTP Verification')
        self.master.geometry("600x275")
        self.otp = None
        self.timer_thread = None
        self.resend_timer = None
        self.wrong_attempts = 0
        self.locked = False
        self.stop_timer = False

이 글도 확인해 보세요:  Vite로 React 앱을 설정하는 방법

우아한 문구 버전: 일회용 비밀번호(OTP)와 관련된 고유한 작업을 수행하는 세 개의 버튼이 있는 사용자 인터페이스를 디자인합니다. 각각의 레이블, 클릭 시 실행되는 명령, 폰트 스타일 기본 설정을 설정하세요. pack() 메서드를 사용하여 순서대로 정렬합니다.

         self.label1 = tk.Label(self.master, 
                               text='Enter your mobile number:',
                               font=('Arial', 14))
        self.label1.pack()

        self.mobile_number_entry = tk.Entry(self.master,
                                            width=20,
                                            font=('Arial', 14))
        self.mobile_number_entry.pack()

        self.send_otp_button = tk.Button(self.master,
                                         text='Send OTP',
                                         command=self.send_otp,
                                         font=('Arial', 14))
        self.send_otp_button.pack()

        self.timer_label = tk.Label(self.master,
                                    text='',
                                    font=('Arial', 12, 'bold'))
        self.timer_label.pack()

        self.resend_otp_button = tk.Button(self.master,
                                           text='Resend OTP',
                                           state=tk.DISABLED,
                                           command=self.resend_otp,
                                           font=('Arial', 14))
        self.resend_otp_button.pack()

        self.label2 = tk.Label(self.master,
                               text='Enter OTP sent to your mobile:',
                               font=('Arial', 14))
        self.label2.pack()

        self.otp_entry = tk.Entry(self.master,
                                  width=20,
                                  font=('Arial', 14))
        self.otp_entry.pack()

        self.verify_otp_button = tk.Button(self.master,
                                           text='Verify OTP',
                                           command=self.verify_otp,
                                           font=('Arial', 14))
        self.verify_otp_button.pack()

애플리케이션의 기능 구축

`start_timer()`라는 새로운 메서드를 만들어 `timer_countdown()` 함수를 별도의 스레드에서 실행하여 시스템의 원활하고 효율적인 운영을 보장하는 동시에 시간에 민감한 작업을 관리하기 위한 우아한 솔루션을 제공하세요.

     def start_timer(self):
        self.timer_thread = threading.Thread(target=self.timer_countdown)
        self.timer_thread.start()

다음 코드 스니펫을 프로젝트에 통합하여 카운트다운의 시작부터 끝까지 기간을 추적하는 timer\_countdown() 함수를 구현하세요. 이 함수는 현재 시간을 사용하여 현재 순간과 비교하여 남은 시간을 주기적으로 업데이트하는 진행형 루프를 유지합니다. 또한 지정된 시간(초)에 도달하거나(stop\_timer가 true로 설정됨) 남은 시간이 0 이하로 떨어지면 카운트다운이 만료되었음을 나타내는 함수가 종료됩니다. 마지막으로 카운트다운 프로세스가 완료되기 전에 시간 초과가 발생하는 경우 적절한 오류 메시지를 표시합니다.

‘OTP 다시 보내기’ 버튼을 활성화하고 일회용 비밀번호가 비활성화되었는지 확인한 후 프로세스를 완료하세요. 또는 만료까지 남은 시간(분 및 초)을 확인하고 타이머 레이블에 이 값을 표시한 후 1초 동안 잠시 일시 정지합니다.

     def timer_countdown(self):
        start_time = time.time()
        while True:
            current_time = time.time()
            elapsed_time = current_time - start_time
            remaining_time = expiration_time - elapsed_time
            if self.stop_timer:
                break
            if remaining_time <= 0:
                messagebox.showerror('Error', 'OTP has expired.')
                self.resend_otp_button.config(state=tk.NORMAL)
                self.otp = None
                break
            minutes = int(remaining_time // 60)
            seconds = int(remaining_time % 60)
            timer_label = f'Time Remaining: {minutes:02d}:{seconds:02d}'
            self.timer_label.config(text=timer_label)
            time.sleep(1)

`잠겨 있음` 플래그가 `참`으로 설정된 경우 잘못된 로그인 시도로 인해 계정이 잠겨 있음을 나타내는 적절한 메시지를 표시합니다. 그러나 `잠겨 있음`이 `거짓`이면 데이터베이스에서 등록된 전화번호를 추출하고 사용 가능한 SIM 카드와 비교하여 유효성을 검사한 다음 암호화 알고리즘을 사용하여 고유한 OTP를 생성하여 사용자의 새 일회용 비밀번호(OTP) 생성을 진행합니다. 그런 다음 클라이언트 라이브러리에서 제공하는 메시징 서비스를 활용하여 생성된 OTP를 사용자가 지정한 휴대폰 번호로 전송합니다. OTP가 성공적으로 전송되면 대화 상자에 확인 메시지를 표시하고 카운트다운 타이머를 활성화하고

     def send_otp(self):
        if self.locked:
            messagebox.showinfo('Account Locked', 'Your account is locked. Try again later.')
            return
        mobile_number = self.mobile_number_entry.get()
        if not mobile_number:
            messagebox.showerror('Error', 'Please enter your mobile number.')
            return

        self.otp = random.randint(1000, 9999)
        message = client.messages.create(
            body=f'Your OTP is {self.otp}.',
            from_='TWILIO_MOBILE_NUMBER',
            to=mobile_number
        )
        messagebox.showinfo('OTP Sent', f'OTP has been sent to {mobile_number}.')
        self.start_timer()
        self.send_otp_button.config(state=tk.DISABLED)
        self.resend_otp_button.config(state=tk.DISABLED)
        self.otp_entry.delete(0, tk.END)

를 제외한 인터페이스의 모든 버튼을 비활성화합니다. 제공된 코드 스니펫을 통합하여 Django 프로젝트의 `accounts.py` 파일 내에 `resend_otp()`라는 새 함수를 구현하여 주어진 작업을 수행할 수 있습니다. 이 함수는 사용자가 잠겨 있지 않거나 이미 잠겨 있는 경우 프롬프트에 지정된 필요한 작업을 수행합니다. 이 함수를 구현하는 방법은 다음과 같습니다: ”’python 에서 django.shortcuts import render, 리디렉션 import hashlib from django.contrib.auth.decorators import login_required .models에서 사용자 프로필을 가져옵니다. .forms에서 UserRegisterForm, UserUpdateForm을 가져옵니다. def generate_otp(user): # … (이전과 동일) @login_required def resend_otp(request): if request.method ==

     def resend_otp(self):
        if self.locked:
            messagebox.showinfo('Account Locked', 'Your account is locked. Try again later.')
            return
        mobile_number = self.mobile_number_entry.get()
        if not mobile_number:
            messagebox.showerror('Error', 'Please enter your mobile number.')
            return

        self.otp = random.randint(1000, 9999)
        message = client.messages.create(
            body=f'Your OTP is {self.otp}.',
            from_='TWILIO_MOBILE_NUMBER',
            to=mobile_number
        )
        messagebox.showinfo('OTP Sent', f'New OTP has been sent to {mobile_number}.')
        self.start_timer()
        self.resend_otp_button.config(state=tk.DISABLED)

`verify_otp()` 함수는 사용자가 제공한 일회용 비밀번호(OTP)가 기록에 저장된 비밀번호와 일치하는지 여부를 확인하는 인증 메커니즘 역할을 합니다. 이 프로세스는 사용자에게 OTP를 입력하라는 메시지를 표시하고, 기록에 저장된 것과 비교하고, 그에 따라 성공 또는 실패 메시지를 표시하고, 잘못된 입력이 있는지 확인하고, 여러 번 실패한 후 계정을 잠그는 등 여러 단계로 구성됩니다.

     def verify_otp(self):
        user_otp = self.otp_entry.get()
        if not user_otp:
            messagebox.showerror('Error', 'Please enter OTP.')
            return
        if self.otp is None:
            messagebox.showerror('Error', 'Please generate OTP first.')
            return
        if int(user_otp) == self.otp:
            messagebox.showinfo('Success', 'OTP verified successfully.')
            self.stop_timer = True
            exit()
        else:
            self.wrong_attempts += 1
            if self.wrong_attempts == 3:
                self.lock_account()
            else:
                messagebox.showerror('Error', 'OTP does not match.')

`lock_account()`라는 메서드를 구현하여 계정의 잠금 상태를 true로 설정하고 레이블로 “계정 잠금”을 표시할 수 있습니다. 또한 모든 라벨, 항목 및 버튼을 비활성화하고 진행 중인 모든 타이머를 중지한 후 10분 타이머를 새로 시작해야 합니다.

     def lock_account(self):
        self.locked = True
        self.label1.config(text='Account Locked')
        self.mobile_number_entry.config(state=tk.DISABLED)
        self.send_otp_button.config(state=tk.DISABLED)
        self.timer_label.config(text='')
        self.resend_otp_button.config(state=tk.DISABLED)
        self.label2.config(text='')
        self.otp_entry.config(state=tk.DISABLED)
        self.verify_otp_button.config(state=tk.DISABLED)
        self.stop_timer = True
        countdown_time = 10 * 60
        self.start_countdown(countdown_time)

제공된 코드 스니펫을 플랫폼에서 사용자 계정을 관리하기 위한 우아한 알고리즘에 통합합니다. 구체적으로, 남은 시간에 따라 특정 계정을 잠글지 여부를 결정하는 `start_countdown()`이라는 메서드를 구현하세요. 할당된 시간이 만료되었거나 부족한 경우 계정은 자동으로 재설정되며, 그렇지 않은 경우 시스템은 미리 정의된 콜백 함수를 통해 사용자에게 잠금이 임박했음을 알리고 미리 작업을 완료하도록 권장합니다.

     def start_countdown(self, remaining_time):
        if remaining_time <= 0:
            self.reset_account()
            return

        minutes = int(remaining_time // 60)
        seconds = int(remaining_time % 60)
        timer_label = f'Account Locked. Try again in: {minutes:02d}:{seconds:02d}'
        self.timer_label.config(text=timer_label)
        self.master.after(1000, self.start_countdown, remaining_time - 1)

계정을 초기 상태로 복원하기 위해 “reset\_account”라는 함수를 만들었습니다. 이 함수는 모든 위젯 값과 변수 상태를 기본 설정으로 되돌립니다. 이 함수를 호출하면 애플리케이션이나 프로그램을 변경하거나 문제를 해결한 후 쉽게 원래 구성으로 되돌릴 수 있습니다.

     def reset_account(self):
        self.locked = False
        self.wrong_attempts = 0
        self.label1.config(text='Enter your mobile number:')
        self.mobile_number_entry.config(state=tk.NORMAL)
        self.send_otp_button.config(state=tk.NORMAL)
        self.timer_label.config(text='')
        self.resend_otp_button.config(state=tk.DISABLED)
        self.label2.config(text='Enter OTP sent to your mobile:')
        self.otp_entry.config(state=tk.NORMAL)
        self.verify_otp_button.config(state=tk.NORMAL)
        self.stop_timer = False

Python에서 Tkinter 라이브러리를 사용하여 그래픽 사용자 인터페이스 실행을 시작하려면 몇 가지 단계를 수행해야 합니다. 먼저, `tkinter` 모듈에서 `Tk` 클래스의 객체를 인스턴스화하여 메인 창 또는 “루트”를 만들어야 합니다. 이렇게 하면 GUI 내의 다른 모든 위젯의 기초가 되는 초기 빈 캔버스가 생성됩니다. 다음으로, 화면에 표시되는 모든 요소의 컨테이너 역할을 하는 Tkinter 패키지에서 제공하는 `Frame` 클래스에서 파생된 특정 클래스의 다른 객체를 인스턴스화해야 합니다. 이 프레임이 생성되면 `pack()` 메서드를 사용하여 루트 창에 패킹하여 프레임의 내용이 루트 창 내의 공간을 차지할 수 있습니다. 마지막으로 애플리케이션을 실행하기 위해 `

 if __name__ == '__main__':
    root = tk.Tk()
    otp_verification = OTPVerification(root)
    root.mainloop()

OTP를 이용한 인증 출력 예시

일회용 비밀번호(OTP) 인증 애플리케이션을 실행하면 휴대폰 번호와 해당 국가 코드를 입력하라는 인터페이스가 나타납니다. 입력이 완료되면 ‘OTP 전송’ 탭을 클릭하여 OTP 전송을 시작하세요. OTP가 성공적으로 전송되었음을 알리는 확인 메시지가 표시되고 해당 버튼이 2분 동안 비활성화됩니다. 수신한 OTP를 장치에서 확인하고 만료되기 전에 적시에 입력하시기 바랍니다.

이 글도 확인해 보세요:  Reqwest로 Rust에서 HTTP 요청 만들기

할당된 시간 내에 일회용 비밀번호(OTP)를 올바르게 입력하면 OTP를 성공적으로 확인했음을 확인하는 확인 메시지가 표시됩니다. 그러면 애플리케이션이 종료됩니다. 기한 내에 OTP를 입력하지 않으면 OTP가 만료되었음을 알리는 팝업 메시지가 표시됩니다. 사용자는 “OTP 다시 보내기” 기능을 클릭하여 OTP 재전송을 요청할 수 있으며, 이 경우 모바일 장치로 전송할 수 있는 새로운 OTP가 생성됩니다.

일회용 비밀번호(OTP)를 잘못 입력한 경우, 입력한 OTP가 올바른 값과 일치하지 않음을 알리는 알림이 사용자에게 표시됩니다.

잘못된 일회용 비밀번호(OTP)를 세 번 이상 입력할 경우 모든 양식 필드를 사용할 수 없게 되며, 무단 액세스를 방지하기 위한 보안 조치로 사용자의 계정이 10분 동안 일시적으로 정지됩니다.

Python과 함께 Twilio 사용

Twilio를 사용하면 여러 도메인에서 활용할 수 있는 SMS 메시징 플랫폼을 구축할 수 있습니다. 통합 기능을 통해 사물 인터넷(IoT) 시스템에 원활하게 통합할 수 있어 미리 설정된 임계값을 벗어나거나 무단 침입을 감지하는 등 특정 조건에 따라 문자 메시지를 자동으로 전송할 수 있습니다. 또한 이 플랫폼은 2단계 인증 프로세스 및 보안 로그인 메커니즘을 포함한 강력한 보안 조치의 개발을 지원합니다. 또한 WhatsApp 챗봇 및 자동 약속 알림과 같은 대화형 플랫폼의 생성을 용이하게 합니다.

커뮤니케이션 플랫폼으로서의 기본 기능 외에도 Twilio는 전화번호 확인, 마케팅 캠페인, 설문조사 배포, 고객 피드백 수집과 같은 다양한 기능을 제공합니다. 그러나 Twilio를 사용하여 애플리케이션을 개발할 때는 예상치 못한 비용이 발생하지 않도록 가격 구조를 염두에 두는 것이 중요합니다.

By 김민수

안드로이드, 서버 개발을 시작으로 여러 분야를 넘나들고 있는 풀스택(Full-stack) 개발자입니다. 오픈소스 기술과 혁신에 큰 관심을 가지고 있고, 보다 많은 사람이 기술을 통해 꿈꾸던 일을 실현하도록 돕기를 희망하고 있습니다.