Python: Аутентификация с помощью протокола S/KEY.
Смотрю тут многие интересуются безопасностью, поэтому решил выложить с описанием свою реализацию протокола S/KEY ;-). В общем то задача тривиальная, на мой взгляд, но может кому-то понадобится, если зададут в универе.
S/KEY был разработан для аутентификации на на unix-подобных ОС, в частности, на «глупых» терминалах или на публичных компьютерах с низким уровнем доверия, где использовать долгосрочные пароли нецелесообразно. Как-то так =).
Начнём с описания протокола. Сам протокол предназначен для аутентификации пользователей с использованием одноразовых паролей. Распишем всё по шагам:
- Для инициализации программы надо взять случайное число w, которое может быть задано как пользователем, так и компьютером. Если это число станет известным злоумышленнику, то протокол будет скомпроментирован.
- H — односторонняя функция, но т.к. односторонних функций не существует (по крайней мере обычные смертные о них точно не знают ;-) ), то возьмём какую-нибудь функцию, которая претендует на роль односторонней. Говоря простым языком, H — хеш-функция. Возьмём и пременим её n раз. Сначала к w, потом к H(w), H(H(w)), …, H(…H(w)…).
- Начальное секретное число w уничтожается, дабы не досталось вражине.
- Выводим пользователю сгенерированные n-1 хешей, пользователь запоминает, или, если он не мнемоник, сохраняет их у себя в укромном месте.
- Сохраняем на сервере ключ под номером n для данного пользователя, все остальные уничтожаем.
Теперь при попытки войти пользователь передаёт своё имя и в качестве пароля последний ключ из своего списка (ключ, который был сгенерирован на шаге n-1). Сервер получает этот ключ, находит запись о ключе для данного имени пользователя, и сравнивает результат хеширования полученного пароля с найденым у себя в базе ключом, если результат операции совпадает с введённым паролем, то допускает пользователя к своему сверхсекретному сервису (для условности обозначим его xxx ;-) ). Но это ещё не всё, ведь протокол использует одноразовые пароли, поэтому на сервере при удачной попытке входа хранящееся значение ключа заменяется на полученное в начале от пользователя (т.е. праобраз хеша), а пользователь вычёркивает из своего списка последний ключ. Т.о., при первом входе пользователь передают ключ с номером n — 1, на сервере хранится ключ с номером n, при втором n — 2 и n — 1 соответственно, и т.д..
Достоинства протокола: даже если траффик между пользователем и сервером xxx будут прослушивать, то злоумышленник получит устаревший пароль, для него встанет задача нахождения праобраза хеша, что принято считать неосуществимым, т.е. данный протокол неуязвим к сниффингу паролей.
Недостатки: при перехвате начального секретного ключа w можно самому сгенерировать такую последовательность; пользователя придётся инициировать заново после того, как он «израсходует» все свои ключи.
А вот и обещанная реализация:
from md5 import md5 import sys, os def skey_register(): name = raw_input("Enter your name: ") if os.path.exists(name): print "Error, user always exists." sys.exit(-1) r = raw_input("Enter random sequence R: ") for i in range(100): r = md5(r).hexdigest() print i, r key = md5(r).hexdigest() try: f = open(name, "w") except: print "Error, can't open file %s for write!" % name sys.exit(-1) f.write(key) f.close() def skey_login(): name = raw_input("Enter your name: ") try: f = open(name, "r") except: print "Error, can't open file %s!" % name sys.exit(-1) passwd = f.read() f.close() upasswd = raw_input("Enter password: ") new_upasswd = md5(upasswd).hexdigest() if new_upasswd != passwd: print "Invalid password!" else: print "Login successful!" f = open(name, "w") f.write(upasswd) f.close() if __name__ == "__main__": while True: action = raw_input("Enter action ((r)egister, (l)ogin or (q)uit): ") if action == "r": skey_register() elif action == "l": skey_login() elif action == "q": sys.exit(0)
На мой взгяд код достаточно примитивен, поэтому комментировать не буду, если у кого вопросы — на форум ;-). В качестве хеш-функции была выбрана md5.
Ссылки к статье:
http://en.wikipedia.org/wiki/Skey — Skey на английской википедии.


Последние комментарии