Привет, Хабр!

Сегодня мы по шагам расскажем, как подключить проверку пользовательских данных с помощью одноразового пароля по SMS. Для этого будем использовать платформу MTC Exolve и фреймворк Flask. Такой метод легко внедрить в свой проект и сделать работу с простыми личными кабинетами удобной и максимально ненапряжной для пользователей.

Увы, многие пользователи не любят запоминать пароли, но им можно предложить вход по SMS

Часто онлайн-сервисы заменяют стандартную пару из логина и пароля на более удобный способ аутентификации — вход через номер телефона и одноразовый SMS-код. Подробно об этом мы говорили в прошлой статье. Уточним, что для повышения уровня защиты от хакерских и фишинговых атак лучше использовать более сложные механики. Цель фишинговых атак — завладеть личной информацией пользователей: паролями, данными банковских карт. Поэтому придется усложнять доступ к номеру телефона и SMS для злоумышленника. Мы же ограничимся основной схемой аутентификации, которая подойдет для редких и разовых входов в простые личные кабинеты без важных персональных данных. В дальнейшем ее можно использовать и как элемент более сложных способов аутентификации.

Для пользователей вход по коду из SMS удобен тем, что не нужно вспоминать логин, пароль или данные паспорта — достаточно ввести номер телефона и получить одноразовый код.

Дополнительно пользователю можно отправить SMS-уведомление о подозрительной активности — например, попытке входа с неизвестных устройств или из других географических локаций. Это значит, что человек сможет быстро среагировать на потенциальные угрозы и принять меры по защите аккаунта.

Ниже мы расскажем, как настроить аутентификацию с помощью SMS API от платформы MTC Exolve и фреймворка Flask. В этом примере мы покажем, как отправить одноразовый код на указанный номер телефона. Для этого понадобятся только API-ключ и номер телефона отправителя, который можно приобрести на нашей платформе.

Пример простой реализации отправки SMS с помощью Flask           

Почему Flask? Он неплох для создания легкого примера, предлагает базовый набор инструментов, помогает сразу начать разработку и подходит для создания микросервисов. Если вы знаете только Python и нужна интеграция вашего проекта с другими сервисами, то как раз этот фреймворк подойдет идеально.

Посмотрим, как реализовать отправку кода на телефон пользователя и верификацию через SMS-код для последующей авторизации на примере Flask-приложения.

Для начала убедитесь, что у вас установлен Flask и библиотека requests: pip install Flask requests

Создание структуры

flasksms/
    /venv
    /templates
        signup.html
        verify.html
	   start.html
	   result.html
   error.html
    app.py
    config.py
    dev.env

Создание шаблонов

В директории templates мы создадим пять шаблонов:

  1. signup.html для ввода номера телефона.

  2. start.html для стартовой страницы.

  3. error.html на случай ввода неверного кода.

  4. result.html для получения страницы с персональными данными.

  5. verify.html для ввода полученного SMS-кода.

Вы можете стилизовать эти шаблоны в соответствии с вашим дизайном.                            

Создание приложения

Файл app.py будет содержать логику Flask-приложения.

Дальше создадим маршруты для стартовой страницы, отправки SMS-кода и верификации кода. Кроме того, добавим словарь generated_codes для хранения сгенерированных кодов.

Получим данные из переменных окружения в файл config.py:

from dotenv import dotenv_values

info_env = dotenv_values('dev.env')

API_KEY = info_env.get('API_KEY')
PHONE_SEND = info_env.get('PHONE_SEND')

Создадим стартовый маршрут в app.py:

from flask import Flask, render_template, request, jsonify
import requests
import random
import string
from config import API_KEY, PHONE_SEND

app = Flask(__name__)

 # Секретный ключ приложения
app.secret_key = API_KEY

# Номер телефона для отправки кода
phone_send = PHONE_SEND

 # Словарь для хранения сгенерированных кодов
generated_codes = {}

@app.route('/', methods=['GET'])
 def start():
  return render_template('start.html')

Стартовый шаблон в html сделаем таким:

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Добро пожаловать</title>
	<style>
		body {
			background-color: white;
			font-family: Arial, sans-serif;
			text-align: center;
		}
		h3 {
			margin-top: 80px;
		}
	</style>
</head>
<body>
	<h3>Добро пожаловать</h3>
	<h5>Простое приложение flask для получения персональных данных с помощью одноразового SMS-кода</h5>
	<p>Для получения персональных данных перейдите по ссылке <a href="http://0.0.0.0:5000/signup">здесь</a>.</p>
	<p>--------------------------------------------------------------</p>
</body>
</html>

Потом создадим маршрут /singup для получения номера телефона:

@app.route('/signup', methods=['GET', 'POST'])
 def signup():
 if request.method == 'POST':
number = request.form['number']

 # Генерируем случайную последовательность из 5 латинских букв
 	code = ''.join(random.choice(string.ascii_letters) for _ in range(5))

 # Отправляем SMS сгенерированным кодом
 	sms_data = {
"number": phone_send,
"destination": number,
"text": code}

headers = {'Authorization': f'Bearer {API_KEY}'}

	response = requests.post(url="https://api.exolve.ru/messaging/v1/SendSMS",
json=sms_data, headers=headers)

	if response.status_code == 200:
 	# Сохраняем сгенерированный код для дальнейшей проверки
 		generated_codes[number] = code
 		return render_template('verify.html', number=number)
 	else:
 		return f"Ошибка при отправке SMS: {response.status_code}"

 return render_template('signup.html')

Переменную number будем забирать из тега input шаблона signup.html:

<!DOCTYPE html>
<html>
<head>
	<title>SMS Signup</title>
	<style>
		.container {
			width: 33.33%;
			margin: 0 auto;
		}
      
	label {
		display: block;
	}
	input[type="text"] {
		width: 70%;
		padding: 10px;
		border-radius: 5px;
		border: 1px solid #ccc;
		margin-bottom: 10px;
	}
	button[type="submit"] {
		background-color: #7FBB28;
		color: #fff;
		border: none;
		padding: 10px 20px;
		border-radius: 5px;
		cursor: pointer;
	}
	button[type="submit"]:hover {
		background-color: #7FСB10;
	}
	</style>
</head>
<body>
	<div class="container">
		<h1>Доступ к данным по SMS</h1>
		<form method="POST" action="/signup">
			<label for="number">Номер мобильного:</label>
			<input type="text" name="number" placeholder="+7 --- --- -- --" required>
			<button type="submit">Получить код</button>
		</form>
	</div>
</body>
</html>

Командой requests.post(url="https://api.exolve.ru/messaging/v1/SendSMS", json=sms_data, headers=headers) отсылаем запрос на отправку SMS по номеру, введенному в форму. В заголовках headers указываем тип авторизации и API-ключ приложения.

Если отправка SMS прошла успешно, перебрасываем пользователя на шаблон verify.html.

Теперь создадим маршрут для сверки сгенерированного кода и кода, который ввел пользователь:

@app.route('/verify/<number>', methods=['GET', 'POST'])
 def verify(number):
 	if request.method == 'POST':
 		entered_code = request.form['code']
 		if number in generated_codes and entered_code == generated_codes[number]:
 			pasport = ''.join(str(random.randint(0, 9)) for _ in range(10))
			credit = ''.join(str(random.randint(0, 9)) for _ in range(16))
			data = {“номер счета”: credit, “номер паспорта”: pasport}
			return render_template('result.html', data=data)

 		else:
 			return render_template('error.html')

 	return render_template('verify.html', number=number)

Раз мы перебросили пользователя на страницу verify.html, то ее стоит доработать. Ниже стили, которые вы можете исправить по вкусу:

<!DOCTYPE html>
<html>
<head>
	<title>Подтверждение</title>
	<style>
		.container {
					width: 33.33%;
					margin: 0 auto;
			}
	label {
			display: block;
		}

	input[type="text"] {
					width: 70%;
					padding: 10px;
					border-radius: 5px;
					border: 1px solid #ccc;
					margin-bottom: 10px;
		}

	button[type="submit"] {
					background-color: #9eccc1;
					color: #fff;
					border: none;
					padding: 10px 20px;
					border-radius: 5px;
					cursor: pointer;
		}


	button[type="submit"]:hover {
					background-color: #9ec1cc;
		}
	</style>
</head>
<body>
	<div class="container">
		<h1>Подтверждение кода</h1>
		<form method="POST" action="/verify/{{ number }}">
			<label for="code">Введите код:</label>
			<input type="text" name="code" required>
			<button type="submit">Подтвердить</button>
		</form>
	</div>
</body>
</html>

Дальше в теге input ожидаем ввод кода и кнопкой button посылаем код на маршрут /verify/<number>. Если код, введенный пользователем, совпадает с сохраненным кодом в словаре generated_codes[number], мы можем отдать данные пользователя. В этом случае для простоты отдаем шаблон result.html, в который кладем случайно сгенерированный номер счета и паспорта.

Конечно, в реальном проекте будет база данных с привязкой номера пользователя и информацией о нем. Тут мы просто показываем возможности, поэтому используем случайно сгенерированные числа:

<!DOCTYPE html>
<html>
<head>
<style>
	table {
			border-collapse: collapse;
			margin: 0 auto;
	}
	th, td {
		padding: 10px;
		text-align: left;
	}
	th {
		background-color: #f2f2f2;
		border-radius: 10px 10px 0 0;
	}
	td {
		border-bottom: 1px solid #ddd;
	}
</style>
</head>
<body>
	<table>
		<tr>
			<th>Номер счета</th>
			<td>{{ data['номер счета'] }}</td>
		</tr>
		<tr>
			<th>Номер паспорта</th>
			<td>{{ data['номер паспорта'] }}</td>
		</tr>
	</table>
</body>
</html>

Если код неверный, вернем пользователю страницу error.html с ошибкой и предложением попробовать снова:

<!DOCTYPE html>
<html>
<head>
	<style>
		body {
				text-align: center;
				font-family: Arial, sans-serif;
		}
		.message {
				margin-top: 100px;
				font-size: 24px;
				color: red;
		}
		.link {
				margin-top: 20px;
				font-size: 16px;
				text-decoration: none;
		}
	</style>
</head>
<body>
	<div class="message">Извините, неверный код.</div>
	<a class="link" href="http://0.0.0.0:5000/signup">Попробуйте снова</a>
</body>
</html>

В каких еще ситуациях применяется OTP через SMS

  • Двухфакторная аутентификация, про которую писали раньше. Одноразовый код через SMS можно использовать дополнительно к имени пользователя и паролю.

  • Проверка номера телефона. Некоторые службы используют номер телефона в качестве основного идентификатора пользователя. В таких сервисах пользователи могут ввести свой номер телефона и одноразовый пароль, чтобы подтвердить свою личность. Иногда он сочетается с PIN-кодом для двухфакторной аутентификации.

  • Восстановление аккаунта. Если пользователь потерял доступ к своей учетной записи, он может восстановить ее через отправку email или SMS-сообщения.

  • Подтверждение платежа. В платежных системах некоторые банки или эмитенты кредитных карт запрашивают у плательщика дополнительную аутентификацию в целях безопасности. Обычно в таких случаях используют push или SMS с одноразовым кодом.

На этом все, если у вас возникли вопросы по использованию платформы MTS Eхolve, то ждем их в комментариях.


Р. S. Через облачную платформу МТС Exolve можно реализовать разные сценарии общения с клиентами: обратный звонок, кол-трекинг, защиту номера, выбор исходящего номера, SMS-информирование и другие. Что еще можно почитать на Хабре про Exolve:

Комментарии (6)


  1. Flying
    18.07.2024 14:03
    +10

    Погодите, это не в блоге ли вашей компании неделю назад была статья со следующей информацией:

    Отдельная проблема — использование SMS как канала для доставки кодов аутентификации. Во-первых, нет уверенности, что злоумышленник не добьется выдачи новой сим-карты с вашим номером на свое имя. Возможно, именно это произошло в истории, когда после восстановления доступа к аккаунту на Госуслугах человек обнаружил, что через его учетку в его квартире успели прописаться восемь посторонних человек.

    Во-вторых, используемый для SMS протокол SS7 создавался 50 лет назад — он не был рассчитан на современные кибератаки. В результате для перехвата SMS требуются относительно небольшие ресурсы, а дальше уже дело техники, как ввести их быстрее пользователя, лишив его доступа. По возможности стоит избегать использования SMS и генерировать код второго фактора непосредственно на устройстве, как это происходит при использовании OTP-приложения или аппаратного ключа.

    Или за неделю надёжность SMS как канала для доставки кодов аутентификации как-то радикально повысилась?


    1. KKK_56 Автор
      18.07.2024 14:03

      Добрый день! В этих статьях говорится о разных сценариях аутентификации. Мы, команда МТС Exolve,  рассказываем о процессе на минималочках, для простых сценариев. Это самый-самый простой способ защитить данные пользователей, допустим, маркетплейсов. Разумеется, финансовые приложения и другую критически важную информацию нужно защищать более сложными методами - именно об этом и рассказывали наши коллеги в прошлой статье.


  1. WhiteApfel
    18.07.2024 14:03
    +3

    Стоимость смс и буквенного имени можете озвучить?) как минимум, строго обязательно прикрутить капчу, а желательно и не автоматическую, перед запросом на отправку. Это должно быть в примере, ибо без неё будет очень приятно обнаружить, как тысячи рублей утекают за несколько минут, потому что кто-то решил использовать для sms бомберов ваш сервис)))


    1. WhiteApfel
      18.07.2024 14:03

      И насколько хорошо SMS у вас ходят за рубеж?) А то бывает приятно обнаружить, как в эмиграции или путешествии не можешь зайти, потому что, о чудо, SMS не доходят)))


      1. KKK_56 Автор
        18.07.2024 14:03

        Добрый день! На оба вопроса сразу отвечу. Стоимость услуг можно узнать на официальном сайте МТС Exolve, все данные в открытом доступе. Если что-то хотите уточнить более детально, можно оставить заявку на сайте или позвонить – наши коллеги расскажут все более подробно.
        Конечно, в МТС Exolve можно настроить и другие способы аутентификации. Тут мы показали самый простой способ. Капчу пользователь сможет прикрутить к своему приложению в любой момент. В любом случае, спасибо за этот комментарий, мы согласны, что подключение капчи — обязательное требование для обеспечения информационной безопасности, постараемся упоминать о необходимости ее подключения в наших будущих статьях.
        Что касается стоимости SMS, то все зависит от операторов, к которым подключены отправитель и получатель. Это касается и условий доставки сообщений за рубеж.


    1. egribanov
      18.07.2024 14:03
      +1

      4 рубля / смс, вроде бы такие расценки были, на некоторых операторов 6 рублей