Смотрю, тут многие интересуются безопасностью, поэтому решил выложить с описанием свою реализацию протокола 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 можно самому сгенерировать такую последовательность; пользователя придётся инициировать заново после того, как он «израсходует» все свои ключи.
А вот и обещанная реализация:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | 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.