Bug(バグ) #1985
完了Cookie for automatic login is deleted when automatic login is done (自動ログイン時に自動ログイン用のCookieが削除される)
100%
説明
Overview¶
Cookie for automatic login is deleted when automatic login is done.
(自動ログイン時に自動ログイン用のCookieが削除される。)
Way to repro¶
1. SNSに自動ログインを有効にしてログイン
2. ブラウザ終了
3. 同ブラウザでSNSにアクセス (自動ログイン・この時点でCookie消失)
4. 同ブラウザ終了
5. 同ブラウザでSNSにアクセスするとログイン画面になる。
Environment¶
OpenPNE3.6.x 〜
Causes¶
opBaseSecurityUser::isValidSiteIdentifier()
がセッション無効時に false を返す。そのため、同クラス initialize() により
ログアウト処理が実行され、自動ログイン用Cookieが削除される。
その後、自動ログインの処理が実行されるため、再現方法 3 の時点では
ログインすると考えられる。
Way to fix¶
opBaseSecurityUser::isValidSiteIdentifier() について セッションが無いときは
true を返すように変更
上記修正後 #1182 で再度発生した問題について¶
概要¶
ログイン時に自動ログインにチェックを入れているにも関わらず,次回自動ログインは成功するが,その次の自動ログインに失敗する.
失敗する原因として自動ログイン用のCookieが削除されていることが確認されている.
#1182 の修正によって再度発生するようになった.
再現手順¶
- SNSに自動ログインを有効にしてログイン
- ブラウザ終了
- 同ブラウザでSNSにアクセス
- 同ブラウザ終了
- 同ブラウザでSNSにアクセス
- ログアウト状態となっている
問題までの処理の流れ¶
- メンバが「次回から自動的にログイン」にチェックをいれてログインする
- 自動ログイン用のCookieと今回限りのログイン用のCookieがサーバから送られてきてブラウザに保存される
- ブラウザを終了する
- 今回限りのログイン用Cooikeが削除されるが自動ログイン用Cookieは残ったままとなる
- ブラウザを立ち上げてSNSにアクセスする
- 最初は未ログイン状態となりopAnonymousMemberのインスタンスが生成される
- opAnonymousMember生成時にログアウト処理が行われて自動ログイン用Cookieを削除する処理が行われる
- ブラウザから送られて来た自動ログイン用Cookieを用いてopRememberLoginFilterがログインに成功する
- ログインを完了してSNSを表示したときには今回限りのログイン用Cookieが存在するが自動ログイン用Cookieが削除されている状態となる
- ブラウザを終了する
- 今回限りのログイン用Cookieが削除されてなにもない状態となる
- ブラウザを立ち上げてSNSをにアクセスする
- opAmonymouseMemberとしてログイン画面にリダイレクトされる
原因¶
#1182 で修正されたコミット( decc4e2d )以降のソースで下記部分においてopAnonymousMemberのインスタンス生成時にログアウト処理が行われるように修正されたため.
lib/user/opSecurityUser.class.php 330行目
321 /** 322 * Initializes all credentials associated and status with this user. 323 */ 324 public function initializeUserStatus() 325 { 326 opActivateBehavior::disable(); 327 $member = $this->getMember(); 328 opActivateBehavior::enable(); 329 330 if ($member instanceof opAnonymousMember || $member->getIsLoginRejected()) 331 { 332 $this->logout(); 333 $isSNSMember = false; 334 } 335 else 336 { 337 $isSNSMember = (bool)$member->getIsActive(); 338 } 339 340 $this->setIsSNSMember($isSNSMember); 341 if ($isSNSMember) 342 { 343 $member->updateLastLoginTime(); 344 } 345 }
修正方針¶
opRememberLoginFilterを廃止し,Memberオブジェクトが初期化される際にopAnonymousMemberかどうかでログアウト処理を行うときと同じタイミングで自動ログインの判定を行う方針とする.
参考¶
Shogo Kawahara さんが13年以上前に更新
具体的な修正案を Pull Request しました。
セッションまわりの処理に変更を加えているため、一応チェックの上取り込んでいただければと思います。
https://github.com/kawahara/OpenPNE3/commit/708159d12065270dbbea8e494acd8ba34da67d74
Shogo Kawahara さんが13年以上前に更新
- 題名 を Cookie for automatic login is deleted when automatic login is done (自動ログイン時に自動ログイン用のCookieが削除される) から [PATCH] Cookie for automatic login is deleted when automatic login is done (自動ログイン時に自動ログイン用のCookieが削除される) に変更
Yuya Watanabe さんが13年以上前に更新
下記の動作を確認し、取り込みを行いました。
また、コーディング規約違反部分の修正も行いました。
- SNSに自動ログインを有効にしてログイン
- ブラウザ終了
- 同ブラウザでSNSにアクセス
- 同ブラウザ終了
- 同ブラウザでSNSにアクセスしてログイン画面にならないことを確認
wa ta さんが13年以上前に更新
- ステータス を Accepted(着手) から Pending Review(レビュー待ち) に変更
- 進捗率 を 0 から 50 に変更
更新履歴 eebcc9dae9f2fb175e3575949a559b119f3949a5 で適用されました。
Masato Nagasawa さんが13年以上前に更新
- ステータス を Pending Review(レビュー待ち) から Pending Testing(テスト待ち) に変更
- 進捗率 を 50 から 70 に変更
レビューOKです。
Shogo Kawahara さんが13年以上前に更新
- ステータス を Pending Testing(テスト待ち) から Pending Review(レビュー待ち) に変更
- 進捗率 を 70 から 50 に変更
更新履歴 708159d12065270dbbea8e494acd8ba34da67d74 で適用されました。
Yuya Watanabe さんが約13年前に更新
- ステータス を Pending Review(レビュー待ち) から Accepted(着手) に変更
下記手順で確認を行うとログアウト状態となり,本チケットの問題が解決できてないことを確認したためステータスを変更します.
- SNSに自動ログインを有効にしてログイン
- ブラウザ終了
- 同ブラウザでSNSにアクセス
- 同ブラウザ終了
- 同ブラウザでSNSにアクセス
- ログアウト状態となっている
Yuya Watanabe さんが約13年前に更新
note-12の動作中におけるブラウザ側のCookieを確認しましたが,3の時点で自動ログイン用のCookieが削除された状態であることを確認しました.ここでソースを見てみると,自動ログイン用のCookieが変更される部分はlib/user/opSecurityUser.class.phpのsetRememberLoginCookie()です.該当箇所は以下のようになります.
lib/user/opSecurityUser.class.php
154 /** 155 * set remember login cookie 156 */ 157 protected function setRememberLoginCookie($isDeleteCookie = false) 158 { 159 $key = md5(sfContext::getInstance()->getRequest()->getHost()); 160 $path = sfContext::getInstance()->getRequest()->getRelativeUrlRoot(); 161 if (!$path) 162 { 163 $path = '/'; 164 } 165 166 if ($isDeleteCookie) 167 { 168 if (!sfContext::getInstance()->getRequest()->getCookie($key)) 169 { 170 return; 171 } 172 173 if ($this->getMemberId()) 174 { 175 $this->getMember()->setConfig('remember_key', ''); 176 } 177 178 $value = null; 179 $expire = time() - 3600; 180 } 181 else 182 { 183 $rememberKey = opToolkit::getRandom(); 184 if (!$this->getMemberId()) 185 { 186 throw new LogicException('No login'); 187 } 188 $this->getMember()->setConfig('remember_key', $rememberKey); 189 190 $value = base64_encode(serialize(array($this->getMemberId(), $rememberKey))); 191 $expire = time() + sfConfig::get('op_remember_login_limit', 60*60*24*30); 192 } 193 194 sfContext::getInstance()->getResponse()->setCookie($key, $value, $expire, $path, '', false, true); 195 } 196
このソースより,setRememberLoginCookie()の引数にtrueを渡す処理を行うとCookieが削除される動作が行われると思います.
そしてsetRememberLoginCooike()の引数にtrueを渡すのは同ファイル内logout()だけであることを確認しました.
このlogout()がどこのタイミングで呼び出されるかを確認するために以下の実験を行いました.
実験内容¶
ログ出力部分変更を加えてdev環境によってログを収集し,logout()が呼び出されるタイミングを確認する.
実験手順¶
- SNSに自動ログインを有効にしてログイン
- ブラウザ終了
- ログ開始
- 同ブラウザを立ち上げてSNSにアクセス
- ホームを表示
ログ収集のための変更内容¶
diff --git a/lib/user/opSecurityUser.class.php b/lib/user/opSecurityUser.class.php index 7d66a46..490b26f 100644 --- a/lib/user/opSecurityUser.class.php +++ b/lib/user/opSecurityUser.class.php @@ -289,6 +289,7 @@ class opSecurityUser extends opAdaptableUser { $authMode = $this->getCurrentAuthMode(); + $this->dispatcher->notify(new sfEvent($this, 'application.log', array("opSecurityUser logout()"))); $this->setRememberLoginCookie(true); $this->setAuthenticated(false); @@ -329,6 +330,7 @@ class opSecurityUser extends opAdaptableUser if ($member instanceof opAnonymousMember || $member->getIsLoginRejected()) { + $this->dispatcher->notify(new sfEvent($this, 'application.log', array(sprintf("%s || %s", get_class($member), $member->getIsLoginRejected())))); $this->logout(); $isSNSMember = false; }
実験結果¶
結果は以下のとおり.ルーティングやレンダリングに関するログは省略.
Sep 28 21:14:22 symfony [info] {sfPatternRouting} Match route "homepage" (/) for / with parameters array ( 'module' => 'member', 'action' => 'home',) 9月 28 21:14:22 symfony [info] {myUser} opAnonymousMember || 9月 28 21:14:22 symfony [info] {myUser} opSecurityUser logout() 9月 28 21:14:22 symfony [info] {myUser} User is not authenticated 9月 28 21:14:22 symfony [info] {myUser} User is not authenticated 9月 28 21:14:22 symfony [info] {sfFilterChain} Executing filter "sfRenderingFilter" 9月 28 21:14:22 symfony [info] {sfFilterChain} Executing filter "opCacheControlFilter" 9月 28 21:14:22 symfony [info] {sfFilterChain} Executing filter "opCheckEnabledApplicationFilter" 9月 28 21:14:22 symfony [info] {sfFilterChain} Executing filter "opAppendXRDSHeaderFilter" 9月 28 21:14:22 symfony [info] {sfFilterChain} Executing filter "opRememberLoginFilter" 9月 28 21:14:22 symfony [info] {myUser} User is authenticated 9月 28 21:14:22 symfony [info] {myUser} Add credential(s) "SNSMember" 9月 28 21:14:22 symfony [info] {sfFilterChain} Executing filter "sfBasicSecurityFilter" 9月 28 21:14:22 symfony [info] {sfFilterChain} Executing filter "opEmojiFilter" 9月 28 21:14:22 symfony [info] {sfFilterChain} Executing filter "opExecutionFilter" 9月 28 21:14:22 symfony [info] {memberActions} Call "memberActions->executeHome()" 9月 28 21:14:22 symfony [info] {memberActions} Change layout to "layoutA" 9月 28 21:14:22 symfony [info] {opView} Set customize "cautionAboutFriendPre" (friend/cautionAboutFriendPre) 9月 28 21:14:22 symfony [info] {opView} Set customize "cautionAboutCommunityMemberPre" (community/cautionAboutCommunityMemberPre) … 9月 28 21:14:23 symfony [info] {opWebResponse} Send status "HTTP/1.1 200 OK" 9月 28 21:14:23 symfony [info] {opWebResponse} Send header "Content-Type: text/html; charset=utf-8" 9月 28 21:14:23 symfony [info] {opWebResponse} Send header "Expires: Fri, 22 Apr 1988 17:00:00 GMT" 9月 28 21:14:23 symfony [info] {opWebResponse} Send header "Lastmodified: Wed, 28 Sep 2011 12:14:23 GMT" 9月 28 21:14:23 symfony [info] {opWebResponse} Send header "Cache-Control: no-store, no-cache, private, max-age=0, must-revalidate, post-check=0, pre-check=0" 9月 28 21:14:23 symfony [info] {opWebResponse} Send header "Pragma: no-cache" 9月 28 21:14:23 symfony [info] {opWebResponse} Send cookie "2dc1b1d9f6936933a8af7f0f643c3ab9": "" 9月 28 21:14:24 symfony [info] {opWebResponse} Send content (875197 o) …
上記よりわかること¶
ブラウザを立ち上げ直した後最初にSNSにアクセスした場合,opAnonymousMemberでアクセスした状態となり,logout()が実行されて自動ログイン用のセッションが初期化される.この時,opSecurityUser.class.phpの175行目でセッションを初期化しようとするがopAnonymousMemberのためDBのテーブルの方は初期化されない.しかし194行目の処理によってCookieを削除してしまう.その後opRememberLoginFilterによってブラウザから送られてきたCookieを用いてログインが成功する.次にホームが表示されたときには先程の処理によってCookieが削除されてしまうため次のログインが行われない状態となっている.
ブラウザ内のCookieが削除される.
Rimpei Ogawa さんが約13年前に更新
(補足)
#1182 の修正で、opSecurityUser の初期化時に有効なセッションがない場合(ただし、この時点では自動ログイン用のCookieは評価されていない)、logout() を呼び出す修正がなされており、この修正以降で本チケットで報告されているのと同じ現象が再発しているようです。
Yuya Watanabe さんが約13年前に更新
修正方針¶
ログアウトを分類すると2種類あると考えられる.それは自動ログインの情報も初期化するログアウトと自動ログインの情報を保持したままするログアウトである.前者の例としてはメンバ自身がログアウトのリンクをクリックしたり,退会したり,自動ログインの有効期限が切れている場合がある.後者の例としては本チケットの問題となっているブラウザ終了がある.
本チケットではopRememberLoginFilterを廃止し,Memberオブジェクトが初期化される際にopAnonymousMemberかどうかでログアウト処理を行うときと同じタイミングで自動ログインの判定を行う方針とする.こうすることで自動ログインの有効期限切れも含めたログイン管理をうまく処理することができると考えられる.
Yuya Watanabe さんが約13年前に更新
- 題名 を [PATCH] Cookie for automatic login is deleted when automatic login is done (自動ログイン時に自動ログイン用のCookieが削除される) から Cookie for automatic login is deleted when automatic login is done (自動ログイン時に自動ログイン用のCookieが削除される) に変更
Yuya Watanabe さんが約13年前に更新
実装案¶
diff --git a/apps/pc_frontend/config/filters.yml b/apps/pc_frontend/config/filters.yml index b202269..c44ddce 100644 --- a/apps/pc_frontend/config/filters.yml +++ b/apps/pc_frontend/config/filters.yml @@ -9,9 +9,6 @@ enable_app: xrds_header: class: opAppendXRDSHeaderFilter -remember_login: - class: opRememberLoginFilter - security: ~ emoji: diff --git a/lib/filter/opRememberLoginFilter.class.php b/lib/filter/opRememberLoginFilter.class.php deleted file mode 100644 index b41e372..0000000 --- a/lib/filter/opRememberLoginFilter.class.php +++ /dev/null @@ -1,36 +0,0 @@ -<?php - -/** - * This file is part of the OpenPNE package. - * (c) OpenPNE Project (http://www.openpne.jp/) - * - * For the full copyright and license information, please view the LICENSE - * file and the NOTICE file that were distributed with this source code. - */ - -/** - * opRememberLoginFilter - * - * @package OpenPNE - * @subpackage filter - * @author Shogo Kawahara <kawahara@tejimaya.net> - */ -class opRememberLoginFilter extends sfFilter -{ - /** - * Executes this filter. - * - * @param sfFilterChain $filterChain A sfFilterChain instance - */ - public function execute($filterChain) - { - if ($this->isFirstCall() && !$this->context->getUser()->isAuthenticated()) - { - if ($memberId = $this->context->getUser()->getRememberedMemberId()) - { - $this->context->getUser()->login($memberId); - } - } - $filterChain->execute(); - } -} diff --git a/lib/user/opSecurityUser.class.php b/lib/user/opSecurityUser.class.php index 2dd1714..c39089c 100644 --- a/lib/user/opSecurityUser.class.php +++ b/lib/user/opSecurityUser.class.php @@ -327,7 +327,17 @@ class opSecurityUser extends opAdaptableUser $member = $this->getMember(); opActivateBehavior::enable(); - if ($member instanceof opAnonymousMember || $member->getIsLoginRejected()) + if ($member->getIsLoginRejected()) + { + $this->logout(); + $isSNSMember = false; + } + elseif ($memberId = $this->getRememberedMemberId()) + { + $this->setMemberId($memberId); + $isSNSMember = true; + } + elseif ($member instanceof opAnonymousMember) { $this->logout(); $isSNSMember = false;
動作確認¶
確認内容¶
影響がありそうな点について正しい動作が行えているかについて確認を行った.網羅的に行なっていないのでここに記述されているだけのテストでは不十分であることに注意.
確認環境¶
OpenPNE3.7.0-dev (master)
上記実装案を適用した状態+ #2469 の修正を施した状態
SNSログイン用ブラウザA: Chrome 14.0.835.202
管理画面用ブラウザB: Firefox 7.0.1
確認内容と確認結果¶
- 普通の状態
- 期待される結果:正しい入力及び自動ログインに成功する
- ブラウザAでログイン画面を表示する
- ログイン画面の表示に成功
- ブラウザAでSNSに自動ログインを有効にしてログイン
- ホームの表示に成功
- ブラウザA終了
- ブラウザAを立ち上げてSNSにアクセス
- ホームの表示に成功
- ブラウザA終了
- ブラウザAを立ち上げてSNSにアクセス
- ホームの表示に成功
- ログイン停止状態
- 期待される結果:正しい入力をしてもログイン出来ない状態
- ブラウザB:管理画面でメンバをログイン停止状態にする
- ブラウザAでログイン画面を表示する
- ログイン画面の表示に成功
- ブラウザAでSNSに自動ログインを有効にしてログイン
- 「ログインに失敗しました。」という表示がされてログインに失敗する
- 自動ログインにチェックを入れてログイン画面からログインしたあとログイン停止になった状態
- 期待される結果:何らかのページを開くときにログイン画面に遷移する
- ブラウザAでログイン画面を表示する
- ログイン画面の表示に成功
- ブラウザAでSNSに自動ログインを有効にしてログイン
- ホーム画面の表示に成功
- ブラウザB:管理画面でログイン停止を行う
- ブラウザAでSNSでホーム画面の表示を行う
- ログイン画面が表示される
- 自動ログイン用のCookieは削除されセッションCookieも新規のものが発行されている
- 自動ログインで直接ホームを表示したあとログイン停止になった状態
- 期待される結果:何らかのページを開くときにログイン画面に遷移する
- ブラウザAでログイン画面を表示する
- ログイン画面の表示に成功
- ブラウザAでSNSに自動ログインを有効にしてログイン
- ホーム画面の表示に成功
- ブラウザA終了
- ブラウザB:管理画面でログイン停止を行う
- ブラウザAを立ち上げてSNSにアクセス
- ブラウザAでSNSホーム画面の表示を行う
- ログイン画面が表示される
- 自動ログイン用のCookieは削除されセッションCookieも新規のものが発行されている
wa ta さんが約13年前に更新
- ステータス を Accepted(着手) から Pending Review(レビュー待ち) に変更
更新履歴 4cd2f0a94a80358ae36b3b9b0a93fa91f35ac725 で適用されました。
Kousuke Ebihara さんが約13年前に更新
- ステータス を Pending Review(レビュー待ち) から Pending Testing(テスト待ち) に変更
- 進捗率 を 50 から 70 に変更
Fumie Toyooka さんが約13年前に更新
- ステータス を Pending Testing(テスト待ち) から Rejected(差し戻し) に変更
- 進捗率 を 70 から 50 に変更
テスターテストで以下の動作の確認ができませんでした。ご確認ください。
(note-18 の確認内容と確認結果の3つめの動作に相当)
- ブラウザでSNSに自動ログインを有効にして member_id = 2 でログイン
- ブラウザを終了させる
- 別のブラウザで管理画面より member_id = 2 のログイン停止を行う
- (2.) のブラウザを立ち上げて SNS (トップのURL) にアクセスする
[期待結果]
4. の時点でログイン画面に遷移する
[テスト結果]
しかし 4. の時点でマイホームが表示されてしまった。
しかも 4. で表示されたのは 1. 時点のキャッシュではなかった。
再現性は100%では無く、たまにログイン画面が表示されたので、要調査。
その後の動作結果は次の通り:
5-a. (4.) の後、ページをリロード(ブラウザでリロード)したらログイン画面へ遷移した
5-b. (4.) の後、ナビゲーションから別のページへ遷移したらログイン画面へ遷移した
5-b2. ログイン不要のページにアクセスした場合は、ログアウト状態でそのページが表示された
Yuya Watanabe さんが約13年前に更新
- ステータス を Accepted(着手) から Pending Review(レビュー待ち) に変更
note-22の問題は #2585 で対応することにしました.本チケットでは対象の問題を除いた部分に関してを取り扱うことにします.
Yuya Watanabe さんが12年以上前に更新
- ステータス を Pending Review(レビュー待ち) から Pending Testing(テスト待ち) に変更
- 進捗率 を 50 から 70 に変更
- 3.6 で発生するか を Yes から Unknown (未調査) に変更
- 3.4 で発生するか を Unknown (未調査) にセット
Shouta Kashiwagi さんが12年以上前に更新
- ステータス を Pending Testing(テスト待ち) から Fixed(完了) に変更
- 進捗率 を 70 から 100 に変更
テストOKです。