Bug(バグ) #3150
完了メンバー登録時にデータが不正な状態になることがある
100%
説明
現象¶
OpenPNEでは認証機構が複数持てるようになっており、この関係でメンバー登録時には
1. メンバー情報の登録
2. 汎用的なアカウントの有効化
という2つの処理を行なっています。
PCのメールアドレスで認証を行う設定の場合は、1においてフォームからの情報入力が行われます。
2のアカウントの有効化処理は汎用処理となっており、1の処理が不要であったり、外部Webサービスと連携のためにフォーム入力不要となるような運用形態においても利用できるような設計となっております。
ただし、ブラウザからの2重フォーム送信やネットワークの状態等、サーバーの外部の要因によってデータが不正な状態になってしまいます。
具体的な現象の例として、 opAuthMailAddressPlugin を利用していて、プロフィール登録時にsubmitボタンを複数回押下すると、 is_active=0 のメンバーが作成されてしまうことがあります。
原因¶
member/registerInput
のフォームで「新規登録」ボタンを複数回クリックした場合に、POST /member/registerInput
のリクエストが重複して行われる事で 2 回目以降のリクエストが 404 エラーとなることによるもの。
これにより、本来は最初の POST /member/registerInput
のリクエストで opAuthMailAddress/registerEnd
にリダイレクトされるべき所が、ブラウザ上では 2 回目以降にレスポンスとして返ってきた 404 エラーの表示のまま止まってしまい、登録が完了しない。
(/opAuthMailAddress/registerEnd/token/...
の URL を手動で入力して表示すると続行できる)
修正内容¶
member/registerInput
の「新規登録」ボタンを複数回クリックできないように JavaScript で二重送信を抑制する。
ファイル
Yuma Sakata さんが約12年前に更新
- 対象バージョン を OpenPNE 3.9.0-old にセット
- 3.8 で発生するか を Unknown (未調査) にセット
Youichi Kimura さんが約9年前に更新
- ステータス を Accepted(着手) から Pending Review(レビュー待ち) に変更
- 進捗率 を 0 から 50 に変更
当チケットの修正のための Pull Request を送信しました。
https://github.com/openpne/OpenPNE3/pull/282
修正内容の詳細は下記の通りです。
(参考) 通常の登録フロー¶
[30/Oct/2015:21:01:47 +0900] "GET /member/register/token/230144fcee6e171ed5ff531f7d4007efa HTTP/1.1" 200 2206 [30/Oct/2015:21:01:57 +0900] "GET /member/registerInput/token/230144fcee6e171ed5ff531f7d4007efa HTTP/1.1" 200 3868 [30/Oct/2015:21:03:49 +0900] "POST /member/registerInput/token/230144fcee6e171ed5ff531f7d4007efa HTTP/1.1" 302 929 [30/Oct/2015:21:03:49 +0900] "GET /opAuthMailAddress/registerEnd/token/230144fcee6e171ed5ff531f7d4007efa HTTP/1.1" 302 685 [30/Oct/2015:21:03:50 +0900] "GET / HTTP/1.1" 200 3696
プロフィール入力画面 (member/registerInput) の「送信」ボタンをクリックすると、"POST /member/registerInput"
→ "GET /opAuthMailAddress/registerEnd"
→ "GET /"
とリダイレクトされホーム画面が表示されます。
IE7 で member/registerInput の「送信」ボタンを連打した場合¶
[30/Oct/2015:21:29:07 +0900] "GET /member/register/token/33668edc9e77cff2cadcaabcdf5392e59 HTTP/1.1" 200 2213 [30/Oct/2015:21:29:10 +0900] "GET /member/registerInput/token/33668edc9e77cff2cadcaabcdf5392e59 HTTP/1.1" 200 3699 [30/Oct/2015:21:30:04 +0900] "POST /member/registerInput/token/33668edc9e77cff2cadcaabcdf5392e59 HTTP/1.1" 302 929 [30/Oct/2015:21:30:04 +0900] "POST /member/registerInput/token/33668edc9e77cff2cadcaabcdf5392e59 HTTP/1.1" 404 3782 [30/Oct/2015:21:30:05 +0900] "POST /member/registerInput/token/33668edc9e77cff2cadcaabcdf5392e59 HTTP/1.1" 404 3782
プロフィール入力画面 (member/registerInput) で「送信」ボタンを連打すると "POST /member/registerInput"
が連続して発行されます。"POST /member/registerInput"
は 2 回目以降のリクエストでは、$this->getUser()->isRegisterBegin()
が false になるため 404 エラーが返されます。
この場合、"GET /opAuthMailAddress/registerEnd"
に遷移しないままエラー画面で止まるためメンバー登録は完了しません(is_active = 0 のまま)
このユーザーはリダイレクト等で "GET /opAuthMailAddress/registerEnd"
に誘導することができればメンバー登録を完了できます(アドレスバーに直接入力しても正常に完了します)
Firefox で member/registerInput の「送信」ボタンを連打した場合¶
[02/Nov/2015:20:36:22 +0900] "GET /member/register/token/48c745a912ad5add51a0d9d197d234908 HTTP/1.1" 200 2321 [02/Nov/2015:20:36:26 +0900] "GET /member/registerInput/token/48c745a912ad5add51a0d9d197d234908 HTTP/1.1" 200 3706 [02/Nov/2015:20:36:53 +0900] "POST /member/registerInput/token/48c745a912ad5add51a0d9d197d234908 HTTP/1.1" 302 961 [02/Nov/2015:20:36:54 +0900] "GET /opAuthMailAddress/registerEnd/token/48c745a912ad5add51a0d9d197d234908 HTTP/1.1" 302 685 [02/Nov/2015:20:36:54 +0900] "POST /member/registerInput/token/48c745a912ad5add51a0d9d197d234908 HTTP/1.1" 404 3948
Firefox からプロフィール入力画面 (member/registerInput) で「送信」ボタンを連打すると、"POST /member/registerInput"
へのリクエストが複数回行われる点は同じですが、その間に裏で "GET /opAuthMailAddress/registerEnd"
へのリダイレクトも行われています。
見かけ上は最後の "POST /member/registerInput"
の 404 エラーに遷移して止まる点で他のブラウザと同じですが、"GET /opAuthMailAddress/registerEnd"
へのリクエストが行われているためメンバー登録が完了している状態となっています(is_active = 1 の状態)
このユーザーはメンバー登録が完了しログイン済みの状態となっているため、リダイレクト等で "GET /"
に誘導できれば正常にホーム画面が表示されます。
修正内容¶
--- a/lib/action/opMemberAction.class.php
+++ b/lib/action/opMemberAction.class.php
@@ -170,6 +170,9 @@ public function executeRegisterInput(opWebRequest $request)
{
$this->forward404Unless(opToolkit::isEnabledRegistration((sfConfig::get('app_is_mobile') ? 'mobile' : 'pc')));
+ $this->redirectIf($this->getUser()->isSNSMember(), array('sf_route' => 'homepage'));
+ $this->redirectIf($this->getUser()->isRegisterFinish(), $this->getUser()->getRegisterEndAction());
+
$this->token = $request['token'];
$member = $this->getUser()->setRegisterToken($this->token);
それぞれ、$this->getUser()->isMember()
は Firefox 向け、$this->getUser()->isRegisterFinish()
はその他のブラウザ向けのリダイレクトとなっています。setRegisterToken()
より前に追加しているため、この判定は URL に含まれる register_token
ではなく Cookie に含まれる現在のセッションに紐付くユーザーに対して判定されます。
Shinichi Urabe さんが8年以上前に更新
- ステータス を Pending Review(レビュー待ち) から Pending Testing(テスト待ち) に変更
- 進捗率 を 50 から 70 に変更
みました
Youichi Kimura さんが7年以上前に更新
- 説明 を更新 (差分)
- ステータス を Rejected(差し戻し) から Pending Review(レビュー待ち) に変更
#3150-8 のリダイレクトによる対処では IE10 での検証でエラー表示を防ぐことができなかったため、クライアント側で JavaScript によってフォームの二重送信を抑制する方法に変更して改めて修正を行いました。
下記 Pull Request にて修正しました。
https://github.com/openpne/OpenPNE3/pull/468
Shinichi Urabe さんが7年以上前に更新
- ステータス を Pending Review(レビュー待ち) から Rejected(差し戻し) に変更
- 古い JS ファイルがブラウザキャッシュに残っているとキャッシュが削除されるまで動作しないと思いますので、 apps/pc_frontend/config/view.yml の記述を例えば、
util.js?20170605
とさせるなど対策をしておいたほうがよりよいと思います - 再現するか確認が難しいかもしれませんが、フィーチャーフォン向けの対策は不要でしょうか。
Shinichi Urabe さんが7年以上前に更新
- ファイル regist-404.png regist-404.png を追加
モバイルシミュレータ (Firefox の アドオン) ですが 、メンバー登録時の携帯個体識別番号取得設定 を 「登録時に取得しない」しておくと、再現はしました [取得する設定の場合、アラートがでるので、連投の抑止になるため]
Youichi Kimura さんが7年以上前に更新
- 古い JS ファイルがブラウザキャッシュに残っているとキャッシュが削除されるまで動作しないと思いますので、 apps/pc_frontend/config/view.yml の記述を例えば、
util.js?20170605
とさせるなど対策をしておいたほうがよりよいと思います
これは #4025 で指定されているサポート対象ブラウザにおいて発生する現象なのでしょうか?
具体的には util.js は静的ファイルとして Apache 等から直接配信されるため、ブラウザがキャッシュを利用する際に Last-Modified
や ETag
ヘッダを適切に扱っているかどうかに左右されます。
例えば IE9 (サポート対象外) においては以下の仕様のため Cache-Control
ヘッダーによって明示的にキャッシュを使用しないように指定する必要がありました。
https://support.microsoft.com/ja-jp/help/2530998
- 再現するか確認が難しいかもしれませんが、フィーチャーフォン向けの対策は不要でしょうか。
mobile_frontend で同様の現象が生じるかどうかについては確認します。
(フォームの二重送信が可能であるかどうかは実機でなければ検証できないと思います)
Shinichi Urabe さんが7年以上前に更新
Youichi Kimura さんは書きました:
- 古い JS ファイルがブラウザキャッシュに残っているとキャッシュが削除されるまで動作しないと思いますので、 apps/pc_frontend/config/view.yml の記述を例えば、
util.js?20170605
とさせるなど対策をしておいたほうがよりよいと思いますこれは #4025 で指定されているサポート対象ブラウザにおいて発生する現象なのでしょうか?
Google Chrome バージョン 58.0.3029.110 (64-bit) にて確認をしています。
具体的には util.js は静的ファイルとして Apache 等から直接配信されるため、ブラウザがキャッシュを利用する際に Last-Modified や ETag ヘッダを適切に扱っているかどうかに左右されます。
静的ファイルは Apache や Nginx などの設定によって左右されるのは把握していますが、
ミドルウェア側で Last-Modified, Etag が適切に出力設定されるかどうか、に関わらず、アプリ側でもできる限りの配慮をしておいたほうがよいという意図です。
フォームの二重送信が可能であるかどうかは実機でなければ検証できないと思います
念のため実機での確認をお願いします
Youichi Kimura さんが7年以上前に更新
- 古い JS ファイルがブラウザキャッシュに残っているとキャッシュが削除されるまで動作しないと思いますので、 apps/pc_frontend/config/view.yml の記述を例えば、
util.js?20170605
とさせるなど対策をしておいたほうがよりよいと思いますこれは #4025 で指定されているサポート対象ブラウザにおいて発生する現象なのでしょうか?
Google Chrome バージョン 58.0.3029.110 (64-bit) にて確認をしています。
Google Chrome 58 は Stable channel の最新バージョンであることからサポート対象ブラウザに含まれます。
このブラウザで再現するのであれば対処の必要があると思います。
具体的には util.js は静的ファイルとして Apache 等から直接配信されるため、ブラウザがキャッシュを利用する際に Last-Modified や ETag ヘッダを適切に扱っているかどうかに左右されます。
静的ファイルは Apache や Nginx などの設定によって左右されるのは把握していますが、
ミドルウェア側で Last-Modified, Etag が適切に出力設定されるかどうか、に関わらず、アプリ側でもできる限りの配慮をしておいたほうがよいという意図です。
まず、URL に util.js?20170605
のようなタイムスタンプを付与する方法は、抜け漏れが生じやすく良い方法ではないと考えています。
もしキャッシュに関する HTTP ヘッダーを適切に付与することで対処することが可能なのであれば、その方法を用いることがより望ましいのではないかと考えています。
そして #3150-18 の意図は、これは Apache の仕事だから OpenPNE では一切関知する話ではないという意味ではなく、例えばサポート対象外の古いブラウザについて対処するための workaround ならば必須ではないだろうという考えによるものでした。
Chrome 58 で再現するとのことですが、どうやら私の認識が間違っていたようで Last-Modified や ETag ヘッダを出力していても Cache-Control: must-revalidate
などを明示的に指定しなければ(サーバーへのリクエストを行わずに)キャッシュを優先して使用するようです。
そのため、.htaccess
に適切な Cache-Control
ディレクティブを付与する修正を行うのが今後の静的ファイルの更新のためにも良さそうだと思いました。
Shinichi Urabe さんが7年以上前に更新
Youichi Kimura さんは書きました:
そして #3150-18 の意図は、これは Apache の仕事だから OpenPNE では一切関知する話ではないという意味ではなく、例えばサポート対象外の古いブラウザについて対処するための workaround ならば必須ではないだろうという考えによるものでした。
意図理解しました。
Chrome 58 で再現するとのことですが、どうやら私の認識が間違っていたようで Last-Modified や ETag ヘッダを出力していても
Cache-Control: must-revalidate
などを明示的に指定しなければ(サーバーへのリクエストを行わずに)キャッシュを優先して使用するようです。
そのため、.htaccess
に適切なCache-Control
ディレクティブを付与する修正を行うのが今後の静的ファイルの更新のためにも良さそうだと思いました。
了解しました。
Youichi Kimura さんが7年以上前に更新
静的ファイルのキャッシュ動作について新規チケット #4200 を作成しました。
mobile_frontend での動作については引き続き当チケットで扱います。
Youichi Kimura さんが7年以上前に更新
- ステータス を Rejected(差し戻し) から Pending Review(レビュー待ち) に変更
member/registerInput
のフォームについて F001 (au), KC3Q (au) の各端末で確認した所、ボタンの連打によって複数のリクエストが送信されることはなく、登録も正常に完了しました。
Shinichi Urabe さんが7年以上前に更新
- ステータス を Pending Review(レビュー待ち) から Pending Testing(テスト待ち) に変更
- 進捗率 を 50 から 70 に変更
確認ありがとうございました
レビューOKです