Подтверждение регистрации или приглашения по электронной почте без базы данных

#ruby-on-rails #ruby #database #encryption #encoding

#ruby-on-rails #ruby #База данных #шифрование #кодирование

Вопрос:

Я хотел бы очистить свою базу данных от устаревших почти учетных записей, и я подумывал о том, чтобы сделать так, чтобы новые регистрации и приглашения помещали свои данные в приветственное электронное письмо в виде зашифрованного или хэшированного URL. Как только ссылка в URL посещается, информация затем добавляется в базу данных в качестве учетной записи. Есть ли что-то, что в настоящее время делает это? Есть какие-либо ссылки, мысли или предупреждения о регистрации пользователя таким образом? Спасибо!

Редактировать: я создал рабочий пример, и URL-адрес состоит из 127 символов.

 http://localhost/confirm?_=hBRCGVqie5PetQhjiagq9F6kmi7luVxpcpEYMWaxrtSHIPA3rF0Hufy6EgiH
+L3t9dcgV9es9Zywkl4F1lcMyA==

  

Очевидно, что больше данных = больший URL

 def create
# Write k keys in params[:user] as v keys in to_encrypt, doing this saves LOTS of unnecessary chars
  @to_encrypt = Hash.new
  {:firstname => :fn,:lastname => :ln,:email => :el,:username => :un,:password => :pd}.each do |k,v|
    @to_encrypt[v] = params[:user][k]
  end

  encrypted_params = CGI::escape(Base64.encode64(encrypt(compress(Marshal.dump(@to_encrypt)), "secret")))
end

private

def aes(m,t,k)
  (aes = OpenSSL::Cipher::Cipher.new('aes-256-cbc').send(m)).key = Digest::SHA256.digest(k)
  aes.update(t) << aes.final
end

def encrypt(text, key)
  aes(:encrypt, text, key)
end

def decrypt(text, key)
  aes(:decrypt, text, key)
end

# All attempts to compress returned a longer url (Bypassed by return)

def compress(string)
  return string
  z = Zlib::Deflate.new(Zlib::BEST_COMPRESSION)
  o = z.deflate(string,Zlib::FINISH)
  z.close
  o
end

def decompress(string)
  return string
  z = Zlib::Inflate.new
  o = z.inflate(string)
  z.finish
  z.close
  o
end
  

Комментарии:

1. Спасибо за комплименты! Рад, что людям нравится эта идея. Мне не так повезло на форуме ruby … railsforum.com/viewtopic.php?pid=108110 😛

Ответ №1:

Мысли:

  • Используйте истинный асимметричный шифр для «cookie», чтобы предотвратить создание учетных записей ботами. Зашифруйте «cookie» с помощью открытого ключа, проверьте его, расшифровав с помощью закрытого ключа.
    Обоснование: Если бы для кодирования cookie использовался только base64 или другой алгоритм, было бы легко перепроектировать схему и автоматически создавать учетные записи. Это нежелательно из-за спам-ботов. Кроме того, если учетная запись защищена паролем, пароль должен отображаться в файле cookie. Любой, у кого есть доступ к ссылке для регистрации, сможет не только активировать учетную запись, но и узнать пароль.

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

  • При проверке ссылки активации убедитесь, что созданная ею учетная запись еще не создана.

  • Как вы защищаетесь от одновременного создания учетной записи двумя пользователями с одинаковым именем?
    Возможный ответ: используйте электронную почту в качестве идентификатора для входа и не требуйте уникального имени учетной записи.

  • Сначала проверьте электронную почту, затем продолжайте создание учетной записи.
    Обоснование: Это сведет к минимуму информацию, которую вам нужно отправить в файле cookie.

Ответ №2:

  • Есть некоторые почтовые клиенты, которые прерывают URL-адреса после 80 букв. Я сомневаюсь, что вы сможете вместить туда всю информацию.

  • У некоторых браузеров есть ограничения на URL-адрес, например, в Internet Explorer 8 не более 2083 символов.

Почему вы регулярно не очищаете свою базу данных (cron script) и не удаляете все учетные записи, которые не были активированы в течение 24 часов?

Комментарии:

1. Хм, эти ограничения, вероятно, затруднят использование любого алгоритма шифрования, такого как RSA…

2. Цель состоит в том, чтобы удалить как можно больше временных и ненужных данных из базы данных. Я бы предпочел не удивляться, если какой-нибудь сверхумный робот разрушит мой процесс регистрации и добавит миллионы строк данных. Кроме того, различные этапы регистрации / приглашения требуют разумных усилий для создания и поддержки. Очевидно, что что-то вроде OpenID — это вариант, но не у всех он есть.

3. Я не вижу, чтобы 2083 символа были большой проблемой для небольших объемов данных. Этот метод также может предотвратить появление нелепых учетных записей (Angry / Dumb Human, Spambot), в которых имя пользователя / пароль состоит из 1000 символов. Количество обычных текстовых сообщений электронной почты ограничено от 78 (рекомендуется) до 998 символов в строке (см. Раздел 2.1.1) ietf.org/rfc/rfc2822.txt

4. Какие почтовые клиенты прерывают URL-адреса после 80 букв? Вы описываете разрыв строки в 80 символов, который привязывает URL?

5. Я голосую за регулярную очистку базы данных. Если вы беспокоитесь о производительности, используйте отдельную таблицу для непроверенных учетных записей. Вы можете установить другие проверки, если беспокоитесь о спаме. Независимо от того, каким способом вы реализуете регистрацию пользователей, вам придется беспокоиться о спам-ботах, поэтому вы также можете упростить процедуру.

Ответ №3:

Я делал почти то же самое раньше. У меня есть только 2 предложения для вас,

  1. Добавьте версию ключа, чтобы вы могли вращать ключ, не прерывая ожидающее подтверждения.
  2. Вам нужна временная метка или срок действия, чтобы вы могли установить ограничение по времени для подтверждения, если хотите. Мы даем одну неделю.

Что касается самого короткого URL, вы можете улучшить его, внеся следующие изменения,

  1. Используйте режим потокового шифрования, такой как CFB, чтобы вам не приходилось заполнять размер блока.
  2. Сжатие открытого текста поможет при большом объеме данных. У меня есть флаг, и я использую сжатие только тогда, когда оно сжимает данные.
  3. Используйте Base64.urlsafe_encode64() , чтобы вам не приходилось кодировать URL-адрес.

Комментарии:

1. Спасибо за вашу помощь! CFB определенно имеет больше смысла!

Ответ №4:

В вашем решении есть несколько проблем.

Во-первых, вы не устанавливаете IV для шифрования. На мой взгляд, это выявило серьезную ошибку в оболочке Ruby OpenSSL — она не должна позволять вам выполнять шифрование или дешифрование, пока не будут установлены оба key и iv , но вместо этого она продвигается вперед и использует IV из всех нулей. Использование одного и того же IV каждый раз в основном устраняет большую часть преимуществ использования режима обратной связи в первую очередь.

Во-вторых, и что более серьезно, у вас нет проверки подлинности. Одним из свойств режима CBC является то, что злоумышленник, имеющий доступ к одному сообщению, может изменить его, чтобы создать второе сообщение, в котором содержимое блока во втором сообщении полностью контролируется злоумышленником, за счет полного искажения предыдущего блока. (О, и обратите внимание, что режим CFB является такой же проблемой в этом отношении).

В данном случае это означает, что я мог бы запросить учетную запись с фамилией ААААААААААААААААААААААААААААААА и моим собственным адресом электронной почты для получения действительного URL. Затем я могу, не зная ключа, изменить адрес электронной почты на victim@victim.com (и при этом исказить фамилию, что не имеет значения), и у меня будет действительный URL, который я могу отправить на ваш сервер и создать учетные записи для адресов электронной почты, которые я не контролирую.

Чтобы решить эту проблему, вам нужно вычислить HMAC для данных с секретным ключом, который известен только серверу, и отправить его как часть URL. Обратите внимание, что единственная причина, по которой вам вообще нужно шифрование, — это защита пароля пользователя, кроме того, что это может быть просто открытый текст плюс HMAC. Я предлагаю вам просто отправить в качестве URL что-то вроде:

 ?ln=Last Nameamp;fn=First Nameamp;email=foo@bar.comamp;hmac=7fpsQba2GMepELxilVUEfwl3+N1MdCsg/Z59dDd63QE=
  

… и попросите страницу подтверждения ввести пароль (похоже, нет причин возвращать пароль туда и обратно).

Комментарии:

1. Спасибо, что нашли время написать ответ! Я прочитал это и согласен с тем, что вы говорите относительно запроса пароля при проверке. Я, вероятно, сохраню шифрование на месте и буду использовать IV вместе с хэшем sha1 или md5 внутри зашифрованных параметров, таким образом, запрос не сможет быть подделан. Хотя, по моему опыту, тестирование текущей уязвимой методики путем изменения URL привело к сбою процесса расшифровки. Мне нужно будет выяснить, имеет ли aes256 cfb собственную проверку согласованности.

2. Чтобы изменить это, вам нужно быть осторожным, чтобы не нарушить разделители сортировки объектов Ruby, но это, безусловно, возможно. Конечно, это зависит от вас, но я настоятельно рекомендую использовать правильный инструмент для правильной работы (Ruby предоставляет функцию HMAC, с OpenSSL::HMAC которой, кажется, все в порядке).

3. Хорошо, я не знал, что в Ruby есть функция HMAC. По моему опыту, в OpenSSL действительно плохая документация, и трудно что-либо найти, не зная, что вы ищете. Спасибо!

Ответ №5:

Я попробую описать дизайн, который может сработать.

Предварительные требования:

  • Библиотека криптографии с поддержкой RSA и некоторой защищенной хэш-функции H (например, SHA-1)
  • Одна пара закрытого и открытого ключей

Дизайн:

  • Уникальным идентификатором пользователя является адрес электронной почты
  • С учетной записью связан пароль и, возможно, другие данные
  • Cookie-файл активации должен быть как можно меньше

Процесс:

  • Пользователя запрашивают адрес электронной почты и пароль. При отправке формы файл cookie вычисляется как cookie = ENCRYPT(CONCAT(email, '.', H(password)), public key)
  • Отправляется электронное письмо, содержащее ссылку на страницу активации с файлом cookie, например. http://example.org/activation?cookie=[cookie]
  • Страница активации по адресу http://example.org/activation расшифровывает файл cookie, переданный в качестве параметра: data = SPLIT(DECRYPT(cookie, private key), '.')
  • На той же странице активации у пользователя запрашивается пароль (который должен быть хэширован с тем же значением, что и в cookie) и любая другая информация, необходимая для создания учетной записи
  • После отправки страницы активации создается новая учетная запись

Пожалуйста, укажите на все, что я пропустил, или на какие-либо улучшения. Я был бы рад соответствующим образом обновить ответ.

Комментарии:

1. Проверьте мой пример кода gist.github.com/181566 Используя aes256, я использую параметр URL вместо файла cookie. Это соответствует основным принципам, которые вы изложили, но я сократил их. Спасибо!

2. Да, я имел в виду параметр url, «cookie» не должен был быть HTTP cookie. Вы использовали AES, который, кажется, идеально подходит для этой цели. Мне раньше не приходило в голову, что symmetric cypher может здесь работать. Обратите внимание, что использование username приводит к проблеме, когда два пользователя выбирают одно и то же имя пользователя и не получают уведомления об этом, пока они оба не попытаются активировать. Я бы настоятельно рекомендовал использовать логины через адрес электронной почты, если это возможно.

3. В данном конкретном случае я буду придерживаться имени пользователя, поскольку число пользователей относительно невелико. Я понимаю вашу точку зрения и согласен, что электронные письма будут более надежными для более крупных сайтов с большим количеством пользователей.