Pull to refresh

Генерация одноразовых паролей при помощи смартфона

Reading time 7 min
Views 9.6K
Пролог: данная статья готовилась в течении прошлой недели и была отправлена как статья для песочницы в воскресную ночь. Сегодня я получил инвайт, но так же обнаружил статью с похожей тематикой — К вопросу о двухфакторной аутентификации с помощью мобильных устройств, так что я полностью согласен с автором этой статьи — «что слишком много в последнее время совпадений». Статью решил публиковать как она была изначально задумана, добавил только ссылки на исходники.

После недавних публикаций на Хабрахабре посвященных безопасности, паролям, переборе паролей и т.п. мне в голову пришла идея авторизации при помощи одноразовых паролей которые генерируются специальным приложением на смартфонах пользователей. Идея очень похожа на идею которая используется в устройствах SecureID (и которые тоже недавно освещались на Хабрахабре). Современные смартфоны доступны все большему количеству людей и их возможностей вполне достаточно для реализации подобной схемы. В данной статье я хочу описать идею взаимодействия связки смартфон + веб сайт, некоторые особенности реализации и показать работающий прототип.



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

Последовательность действий пользователя при регистрации на сайте следующая:
  • при регистрации на сайте (или после регистрации) пользователь может выбрать дополнительный вариант аутентифкации при помощи одноразовых паролей.
  • если такой вариант выбран, то сервер генерирует секретный ключ, сохраняет его в базе и ассоциирует его с пользователем.
  • После создания секретного ключа, пользователю генерируется изображение с QR-кодом, который содержит секретный ключ.
  • пользователь считывает значение секретного ключа при помощи камеры смартфона.
  • приложение на смартфоне сохраняет секретный ключ, ассоциирует его с сайтом и использует для генерации одноразовых паролей.


QR-коды были выбраны из-за достаточно широкой рапространенности, так же используя QR-коды можно передавать довольно большие объемы данных (сотни и тысячи символов), но ничто не мешает использовать любые другие способы кодирования данных.
За основу генерации отдноразовых паролей был взят алгоритм HOTP. Алгоритм является открытым стандартом и находится в свободном доступе. Так же для него есть соответсвующий RFC 4226.

Для реализации и проверки идеи была выбрана связка Google App Engine + смартфон на базе Android. Демо-версия сайта расположена по адресу http://grey-box.appspot.com/ там же есть ссылка на небольшое демо-приложение для смартфона на базе Android, поддерживаются версии Android OS от 1.6 и выше. Оба приложения достаточно простые, так что я не буду описывать их детально, остановлюсь только на наиболее интересных моментах.

Основой обоих приложений является вычисление одноразовых паролей. Каждый раз когда пользователь хочет авторизоваться на сайте он генерирует одноразовый пароль на смартфоне, используя специальное приложение. Пароли привязаны ко времени и имеют ограниченный срок действия. Когда пользователь вводит созданный пароль, сервер выполняет те же самые действия — генерирует пароль на своей стороне. Если пароли совпадают то авторизация проходит успешно. Чтобы сгенерировать одинаковые пароли сервер и смартфон должны создавать пароли на основе одинаковых начальных данных. Этими начальными данными являются секретный ключ пользователя и текущее время. Когда сервер генерирует секретный ключ он так же сохраняет в какой момент времени был сгенерирован ключ (в виде unix timestamp). Секретный ключ и время генерации ключа кодируются в QR-код и передаются на сматфон. Приложение на смартфоне считывает секретный ключ и время генерации ключа и вычисляет разницу между временем на строне сервера и на стороне сматрфона. В дальнейшем эта разница используется чтобы синхронизировать время.

На стороне сервера должно выполнятся 3 основных действия:
  • генерация секретного ключа
  • генерация QR-кода
  • генерация и проверка одноразового пароля

Ниже приведены соответсвующие части кода на Python.
# создание секретного ключа.
import time
import hmac
from hashlib import sha1
...

OTP_TTL = 300 # время жизни одноразового пароля, в секундах

# входные данные
username = request.get(“username”)
timestamp = int(time.time()) / OTP_TTL

# создать секретный ключ
secret_key = hmac.new(str(timestamp) + username, digestmod=sha1).hexdigest().upper()
# далее сохранить ключ в базе и т.п.


Для генерация картинки с QR-кодом простейшим вариантом является использование любого сервиса для генерации различных штрих-кодов. Таких сервисов довольно много, в данной системе был использован сервис который предоставляет корпорация Google — Google Charts Tools. Данный сервис позволяет генерировать различные графики, диаграммы и различные штрих-коды в том числе. Подобный способ был использован исключительно по соображениям простоты реализации, для реальных систем лучше генерировать штрих-коды на стороне сервера.

QR_TPL = "https://chart.googleapis.com/chart?chs=100x100&cht=qr&chl=%s&choe=UTF-8"
qr_url = QR_TPL % ('%s:%s' % (secret_key, timestamp))
# далее полученная ссылка используется в HTML шаблоне страницы регистрации.
...


Генерация одноразового пароля.

import time
import hmac
from hashlib import sha1
…

OTP_TTL = 300 # время жизни одноразового пароля, в секундах
TRUNCATE_MASK = 0x7FFFFFFF # маска для сброса самого старшего бита
MOD = 1000000 # используется чтобы получить заданное число цифр одноразового пароля

# входные данные
username = request.get("username")
password =  request.get("password")

# получить секретный ключ пользователя из базы
st = storage.gql("where user = :1", username).get()
secret_key =st.secret_key
# получить timestamp
timestamp = int(time.time()) / OTP_TTL
# создать одноразвый пароль ...
one_time_password = str((int(hmac.new(secret_key, str(timestamp), sha1).hexdigest(), 16) & TRUNCATE_MASK) % MOD)
# ... и сравнить его с паролем который ввел пользователь
if password == one_time_password:
	# успешная авторизация
else:
	# ошибка


На стороне сматрфона выполняются 2 основных действия:
  • считывание и распознавания QR-кода с последующим сохранением данных
  • генерция одноразового пароля

Для считывания и распознавания штрих-кодов существует несколько библиотек, пожалуй самой активно используемой и распространенной является открытая библиотека Zxing. Данная библиотека поддерживает ряд платформ, включая Google Android и Apple iOS. На основе этой библиотеки так же реализовано открытое приложение Barcode Scanner, данное приложение также достаточно распространено и часто является предустановленным на многих современных смартфонах на базе Android. Первоначально было задумано интегрировать данную библиотеку в приложение, но после продолжительных поисков в интеренете и документации пришло осознание того что это не самый быстрый и легкий путь. Было решено использовать механизм вызовов intent который предоставляет платформа Android. При таком подходе значительно проще интегрировать необходимую функциональность в приложение, хотя пользователям и прийдется дополнительно устанавливать Barcode Scanner в случае необходимости. Это минус такого подхода, но есть и плюсы — не приходится сопровождать и поддерживать все возможные модели смартфонов, нынешние и будущие — этим занимается команда разработчиков Zxing. Так же можно реализовать поддержку других приложений для распознавания штрих-кодов и не ограничиваться одним Barcode Scanner.

Далее приведен небольшой фрагмент кода на Java который используется для вызова и обработки ответа приложения Barcode Scanner.
...
// вызов Barcode Scanner Intent, например по нажатию на кнопку
Intent intent = new Intent("com.google.zxing.client.android.SCAN");
intent.putExtra("SCAN_MODE", "QR_CODE_MODE");
startActivityForResult(intent, 0);
...

// метод в котором обрабатывается результат полученный после возврата из приложения Barcode Scanner
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
	if (requestCode == 0) {
		if (resultCode == RESULT_OK) {
			// получаем распознанную строку с данными
			String contents = intent.getStringExtra("SCAN_RESULT");
			// … обработка и сохранение полученных данных …
		}
	}
}
...


Генерация одноразового пароля.

// использованные библиотеки
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
...

// константы, назначение аналогичное константам описанных для сервера
long OTP_TTL = 300;
long TRUNCATE_MASK = 0x7FFFFFFF;
long MOD = 1000000;
...

// получить сохраненный секретный ключ
String secret_key = key_storage.getString("secret_key", "");
// вычислить время на сервере
long time_delta = key_storage.getLong("time_delta", 0);
long local_time = (System.currentTimeMillis()/1000) / TIME_MASK;
long server_time = local_time + time_delta;
try {
	SecretKeySpec keySpec = new SecretKeySpec(secret_key.getBytes(), "HmacSHA1");
	Mac mac = Mac.getInstance("HmacSHA1");
	mac.init(keySpec);
	byte[] result = mac.doFinal(Long.toString(server_time).getBytes());
	password = Long.toString((new BigInteger(1, result).intValue() & TRUNCATE_MASK) % MOD);
} catch (NoSuchAlgorithmException e) {
	...
} catch (InvalidKeyException e) {
	...
}
// показать пользователю полученный пароль
...


Простой прототип сайта и демо-приложение для смартфонов доступно по адресу http://grey-box.appspot.com/. Приложение для Android называется Auth ID и не требует каких-либо специальных разрешений или прав доступа для работы. Единственное требование — это наличие приложения Barcode Scanner, которое используется для сканирования QR-кодов. На данный момент функциональность приложения Auth ID минимальна, приложение может сохранять ключ только для одного пользователя и только для демо-сайта. Если регистрироваться под другим пользователем то секретный ключ будет переписан поверх существующего. В дальнейшем можно добавить сохранение ключей для различных сайтов и пользователей, все это конечно при условии что данная схема авторизации получит рапространение. Буду рад любым комментариям и пожеланиям.

Ссылки:
http://ru.wikipedia.org/wiki/QR-%D0%BA%D0%BE%D0%B4 – что такое QR-коды
http://en.wikipedia.org/wiki/HOTP — описание алгоритма HOTP
http://tools.ietf.org/html/rfc4226 – RFC 4226 для алгоритма HOTP
https://chart.googleapis.com – Google Chart Tools
http://code.google.com/p/zxing/ — библиотека Zxing для сканирования и распознавания различных штрих-кодов.
http://grey-box.appspot.com/ — демо-версия сайта и приложения иллюстрирующие идею.

P.S. Исходники доступны после тестовой регистрации и логина. Код тестовый и создавался исключительно для проверки работоспособности идеи, не рекомендуется для реального использования.
Tags:
Hubs:
+33
Comments 37
Comments Comments 37

Articles