Pull to refresh

ActionMailer_X509: подписываем и шифруем письма прямо в Ruby On Rails

Reading time3 min
Views1.3K
В одном из последних проектов понадобилось подписывать и шифровать с помощью сертификатов X.509 письма, отправляемые приложением на Ruby on Rails 3. Беглый поиск привёл к плагину actionmailer_x509, а вот дальше начались проблемы.

Выяснилось, что он не обновлялся с 2008 года, скорее всего не работает с Rails 3 (в частности, смущал комментарий автора: «It has been tested with Rails 2.0.1») и умеет только подписывать, но не шифровать письма. Поиск альтернативных решений ничего не дал, и пришлось знакомиться с плагином поближе.



Знакомство осложнилось очень быстро: набор тестов, шедший в комплекте с плагином падал при прогоне. Как оказалось, хотя сходу заметить это было не просто, дело было в том, что истёк срок действия сертификатов, которые автор включил в тестовый набор. Пересоздал сертификаты. Далее, я убрал из тестового хелпера библиотеки от Rails 2 и добавил соответствующие от Rails 3, сделал ещё ряд изменений

Тут же выяснилось, что Mail, новый gem обработки почты, который теперь используется в Ruby on Rails 3, существенно отличается от Tmail. Пришлось врубаться во внутреннее устройство Mail и Tmail, переписывать все части, затрагивающие Tmail. Ок, готово. Конечно, повозился, и попереносил неперенесённые заголовки письма, но это было решаемо.

Новая проблема: в комментариях к коду автор пишет «NOTE: we can not reparse the whole mail, TMail adds a \r\n which breaks the signature...». И действительно, проверка подписи не работает и в результирующем теле объекта Mail \r\n стоят через каждые 60 символов, в то время как в оригинальном, через каждые 64. Вот над этой проблемой пришлось биться очень долго. Я пытался gsub'ом вернуть всё на место, изучал код gem'а Mail, оставлял баг-репорты в Mail, писал в список расслыки, но всё было тщетно. В конце концов меня спас вопрос на stackoverflow. Оказалось, что Mail добавляет content-id, который меняет заголовки письма, и, следовательно, подпись становится неверна, и это никак не связано с \r\n, о которых писал автор оригинального actionmailer_x509.

После исправления и принудительной установки content_id всё пошло как по маслу: я почистил код, завернул всё в отдельный gem, поправил rake-задания и дописал код, отвечающий за шифрование письма.

Результатом моих трудов можете наслаждаться в моём форке actionmailer_x509 на github'е или с помощью строчки gem 'actionmailer_x509' в вашем Gemfile.

Он позволяет следующим незатейливым кодом включить подпись и\или шифрование исходящего письма

class FooMailer < ActionMailer::Base
  def sending_method(email, from , subject = "Empty subject for signed")
    # Для включения подписи
    x509_sign  true
    x509_sign_cert  "certs/yourwebsite.crt"
    x509_sign_key   "certs/yourwebsite.key"
    # Пароль для приватного ключа
    x509_sign_passphrase "my passphrase for the certificate"
    # Для включения шифрования
    x509_crypt  true
    x509_crypt_cert  "certs/crypt.crt"
    # Можно изменить алгоритм шифрования, если не устраивает стандартный DES
    x509_crypt_cipher "AES-128-CBC"

    mail(:subject => subject, :to => email, :from => from)
  end
end


Кроме того плагин позволяет выставить все эти настройки ключей для всего приложения в целом и предоставляет несколько rake задач для тестирования настроек и скорости работы. Подробнее об этом читайте в README.

Вместо заключения

Я потратил больше месяца на адаптацию actionmailer_x509 к особенностям Ruby on Rails 3. Я изучил smime openssl, gem Mail, gem Tmail и ActionMailer. Я узнал, как заворачивать код в gem и как вообще должна выглядеть структура gem'а; как правильно, с помощью railtie, подгружать rake задачи, что б они стали видны в итоговом Rails приложении. Были моменты, когда я был готов сдаться, и искал обходные пути решения моей проблемы, но, к счастью, я сумел дожать gem.

Я не жалею о потраченном времени, и смотрю на новосозданный gem с чувством удовлетворения. Теперь я гораздо лучше чувствую всю почтовую кухню Ruby on Rails, и я стал гораздо подкованней в вопросах, связанных с OpenSSL. Если вам вдруг придётся выбирать допилить\создать существующее решение или попытаться обойти проблему как-то иначе — попробуйте сделать что-то новое для сообщества, вам должно понравиться!
Tags:
Hubs:
Total votes 32: ↑31 and ↓1+30
Comments6

Articles