Ответ 1
Я не могу претендовать на действительно полное понимание кода здесь. Но я могу сказать вам многое:
Я точно выполнил ваши шаги (используя Ruby 2.0.0-p247 и Rails 4.0), за одним исключением - я также добавил gem файл byebug в свой Gemfile и вставил точку останова отладки в действие HomeController#index
.
Из консоли beebug в этой точке останова я мог видеть неотредактированный файл cookie через:
(byebug) cookies["_my_app_session"]
"cmtWeEc3VG5hZ1BzUzRadW5ETTRSaytIQldiaTMyM0NtTU14c2RrcVVueWRQbncxTnJzVDk3OWU3N21PWWNzb1IrZDUxckdMNmZ0cGl3Mk0wUGUxU1ZWN3BmekFVQTFxNk55OTRwZStJSmtJZVkzVmlVaUI2c2c5cDRDWVVMZ0lJcENmWStESjhzRU81MHFhRTN4VlNWRlJKYTU3aFVLUDR5Y1lSVkplS0J1Wko3R2IxdkVYS3IxTHA2eC9kOW56LS1IbXlmelRlSWxiaG02Q3N2L0tUWHN3PT0=--b37c705a525ab2fb14feb5f2edf86d3ae1ab03c5"
И я мог видеть фактические зашифрованные значения с помощью
(byebug) cookies.encrypted["_my_app_session"]
{"session_id"=>"13a95fb545a1e3a2d4e9b4c22debc260", "_csrf_token"=>"FXb8pZgmoK0ui0qCW8W75t3sN2KLRpkiFBmLbHSfnhc="}
Теперь я отредактирую файл cookie, изменив первую букву на "A" и обновив страницу:
(byebug) cookies["_my_app_session"]
"AmtWeEc3VG5hZ1BzUzRadW5ETTRSaytIQldiaTMyM0NtTU14c2RrcVVueWRQbncxTnJzVDk3OWU3N21PWWNzb1IrZDUxckdMNmZ0cGl3Mk0wUGUxU1ZWN3BmekFVQTFxNk55OTRwZStJSmtJZVkzVmlVaUI2c2c5cDRDWVVMZ0lJcENmWStESjhzRU81MHFhRTN4VlNWRlJKYTU3aFVLUDR5Y1lSVkplS0J1Wko3R2IxdkVYS3IxTHA2eC9kOW56LS1IbXlmelRlSWxiaG02Q3N2L0tUWHN3PT0=--b37c705a525ab2fb14feb5f2edf86d3ae1ab03c5"
(byebug) cookies.encrypted["_my_app_session"]
nil
Итак, сеанс nil
в этот момент запроса:
(byebug) session
#<ActionDispatch::Request::Session:0x7ff41ace4bc0 not yet loaded>
Я могу принудительно загрузить сеанс с помощью
(byebug) session.send(:load!)
и когда я это сделаю, я вижу, что итоговый идентификатор сеанса
"f6be13fd646962de676985ec9bb4a8d3"
и, конечно, когда я позволю завершить запрос, то, что я вижу в представлении:
["session_id", "_csrf_token"] ["f6be13fd646962de676985ec9bb4a8d3", "qJ/aHzovZYpbrelGpRFec/cNlJyWjonXDoOMlDHbWzg="]
У меня также есть новое значение cookie, не связанное с тем, которое я редактировал.
Итак, из этого, я думаю, мы можем заключить, что происходит то, что, поскольку подпись cookie не может быть проверена, сеанс был аннулирован и регенерирован. Теперь у меня новый сеанс, с другим csrf_token.
Соответствующий код появляется в actionpack/lib/action_dispatch/middleware/cookies.rb:460-464
, в классе EncryptedCookieJar
:
def decrypt_and_verify(encrypted_message)
@encryptor.decrypt_and_verify(encrypted_message)
rescue ActiveSupport::MessageVerifier::InvalidSignature, ActiveSupport::MessageEncryptor::InvalidMessage
nil
end
Вместо дешифрования сообщения с недопустимой сигнатурой мы просто рассматриваем его как nil
. Таким образом, недопустимый файл cookie, который хранит идентификатор сеанса и токен csrf, не используется для загрузки сеанса, и все, что зависит от значений в cookie, не будет выполнено.
Итак, почему мы не получили ошибку, а не только новый сеанс? Это потому, что мы не пробовали ничего, что зависит от зашифрованных значений. В частности, хотя мы имеем
protect_from_forgery with: :exception
(в отличие от :null_session
) в ApplicationController
, Rails не проверяет токен csrf в запросах GET или HEAD - он полагается на разработчика для реализации этих действий в соответствии со спецификацией, разрушительный. Если вы попробовали то же самое в запросе POST, вы получили бы ошибку ActionController::InvalidAuthenticityToken
(как вы можете легко убедиться для себя).