Techioz Blog

Devise 登録パラメータの形式が異なる場合があるのはなぜですか?

概要

Rails 5 アプリとの Devise 統合を少しカスタマイズしました。 Devise::RegistrationsController から継承し、Super を呼び出すのではなく、User リソースに対する独自の作成アクションを実装しています。

開発中は常に、本番環境ではほとんどの場合、フォームからのパラメータは次のように渡されます。

{
  "utf8"=>"✓",
  "authenticity_token"=>"***",
  "user"=> {
    "email"=>"[email protected]",
    "password"=>"[FILTERED]",
    "password_confirmation"=>"[FILTERED]"
  },
  "commit"=>"Create account"
}

ただし、少数の場合、運用環境のみで、params は次のようになります。

{
  "utf8"=>"✓",
  "authenticity_token"=>"***",
  "user[email]"=>"[email protected]",
  "user[password]"=>"[FILTERED]",
  "user[password_confirmation]"=>"[FILTERED]",
  "commit"=>"Create account",
  "registration"=> {
    "user[email]"=>"[email protected]",
    "user[password]"=>"[FILTERED]",
    "user[password_confirmation]"=>"[FILTERED]",
    "commit"=>"Create account"
  }
}

なぜこの 2 番目の形式のパラメーターが表示されるのかわかりません。私の推測では、状況によっては、Devise がパラメーターを異なる方法で処理するということです。私が検討できる唯一のケースは、検証の問題の後に再送信することですが、これを試行するたびに、パラメータは依然として最初の形式 (開発および運用環境) で渡されます。

create アクションは、params[:user] にユーザー属性のハッシュがあることを前提として記述されていますが、この 2 番目の形式には当てはまりません。これらのパラメーターの 2 つの形式を処理できますが、なぜこれが起こるのかを理解したいのですが、このアクションがランダムに異なるパラメーターで呼び出されるのは望ましくありません。

関連する場合は、送信フォーム (スタイル クラスは省略):

  <%= form_for(@user, as: :user, url: registration_path(:user)) do |f| %>
    <div class="row">
      <%= f.label :email %>
      <%= f.email_field :email, autofocus: true %>
    </div>

    <div class="row">
      <%= f.label :password %>
      <%= f.password_field :password, autocomplete: "off" %>
    </div>


    <div class="row">
      <%= f.label :password_confirmation, 'Password confirmation' %>
      <%= f.password_field :password_confirmation, autocomplete: "off" %>
    </div>

    <div class="row">
      <div>
        <%= f.submit "Create account" %>
      </div>
    </div>
  <% end %>

アップデートに伴い編集されました。追加のログ記録により、興味深いことが確認されました。バグのあるリクエストの request.media_type は application/json です (通常のリクエストの場合は application/x-www-form-urlencoded である必要があります)。

アップデートに伴い編集しました。フォームデータが間違ったリクエストタイプで送信されているように見えます。これは、値が解凍されない理由(パラメータの属性名として user[email] がある理由)と、その登録オブジェクトにラップされたパラメータ (JSON に対して Wrap_parameters を有効にしました)。

これでは説明できないのは、なぜ一部のリクエストだけがこのメディア タイプでタグ付けされるのかということです。

最終編集: ログをしばらく観察した結果、この問題はボットによって引き起こされていると結論付けました。障害は常に同じ IP からの 3 つのリクエストの一部として発生し、最後のリクエストのみが JSON データを持ち、すべて数秒以内に発生します。ボットはフォームを読み取り、Rails にとって意味のない方法で JSON に変換しているだけです。これを見てくださった方々に感謝します。

解決策

これは、ActionController::ParamsWrapper が開始されるときに発生します。

有効にすると、params ラッパーは、現在のコントローラーの名前でまだネストされていない場合、受信パラメーターをその名前でラップします。

この例では、user の下にネストされたパラメータを RegistrationsController という名前のコントローラに送信しています。したがって、ParamsWrapper はそれらを登録で再度ラップします。これらを UsersController に送信した場合、それらは既に user の下にネストされているため、変更にはなりません。

私見ですが、これは Ruby on Rails の最も役に立たず、エラーが発生しやすい魔法の動作の 1 つです。問題が発生することが多いため、私は通常すぐに無効にします。たとえば、フロントエンドはネストを行わず、この機能にのみ依存しているが、バックエンドは再構築して名前空間を導入するか、コントローラーの名前を変更する必要がある場合です。