kakakakakku blog

Weekly Tech Blog: Keep on Learning!

FuelPHP のセッション情報に含まれる previous_id と rotated_session_id の用途を調べた

FuelPHP のセッション管理で少し気になる部分があったため,フレームワークの実装を読んでみた.公式ドキュメントには詳細に書かれていないけど,知っておくと良さそうな仕様を知ることができた.なお,今回は FuelPHP 1.8 を前提にしている.

設定

まず,セッション管理用の設定ファイル config/session.php の重要なパラメータを紹介する.パラメータは他にもあって,詳細は公式ドキュメントに書いてある.

match_ua

セッションを照合するときに,ユーザーエージェントが異なるとセッションを破棄するという設定で,デフォルトでは true になっている.基本的には true にしておくのが良さそう.

expiration_time

パラメータ名の通り,セッションを破棄するまでの秒数を意味している.デフォルトでは 7200秒 = 2時間 になっている.なお,ドキュメントには記載がないが,セッションストアに Memcached を使う場合 2592000秒 = 30日 が上限となり,自動的に丸められる.これは Memcached の仕様と言える.

// adjust the expiration time to the maximum possible for memcached
$this->config['expiration_time'] = min($this->config['expiration_time'], 2592000);

rotation_time

公式ドキュメントには,セッションハイジャックを防ぐために rotation_time で指定した間隔で自動的にセッションを更新すると書かれている.デフォルトでは 300秒 = 5分 となる.今回気になったのは,このローテーションの仕組みとなる.

ローテートの仕組みを確認した

ローテートの仕組みを確認するために Session_Driver クラスの rotate メソッドを読んでみた.

すると,セッションを破棄するのではなく $this->keys['previous_id'] に一度退避をしてから $this->keys['session_id'] に新規セッションを登録していることがわかった.previous_id の値を活用する実装はフレームワークの中で見つけることはできなかったけど,少なくとも「1世代前のセッションを保持している」という事実を知ることができた.

/**
 * force a session_id rotation
 *
 * @param  bool $force  if true, force a session id rotation
 * @return \Session_Driver
 */
public function rotate($force = true)
{
    // do we have a session?
    if ( ! empty($this->keys))
    {
        // existing session. need to rotate the session id?
        if ($force or ($this->config['rotation_time'] and $this->keys['created'] + $this->config['rotation_time'] <= $this->time->get_timestamp()))
        {
            // generate a new session id, and update the create timestamp
            $this->keys['previous_id'] = $this->keys['session_id'];
            $this->keys['session_id']  = $this->_new_session_id();
            $this->keys['created']         = $this->time->get_timestamp();
            $this->keys['updated']     = $this->keys['created'];
        }
    }
    return $this;
}

セッション取得の仕組みを確認した

次にセッション取得の仕組みを確認するために Session_Memcached クラスの read メソッドを読んでみた.今回は Memcached を前提にしている.

詳細は割愛するとして,気になったのは以下の部分だった.

if (isset($payload['rotated_session_id']))
{
    $payload = $this->_read_memcached($payload['rotated_session_id']);

Memcached を dump すると,セッション情報とは別に,以下のように破棄されたはずのセッション情報だけを保持しているキーが存在する.先ほどの例で言うとキーの値が previous_id と一致し,rotated_session_id の値が session_id と一致している.ようするに,1世代前のセッションから,最新のセッションを引き戻すことができるという意味になる.サンプルコードを使って動作確認をしてみたら,確かにそのような動きになっていた.この仕様は「セッションハイジャックを防ぐ」という目的とコンフリクトしているのではないか?どうなんだろう.

add key_11111111111111111111111111111111 0 1483196400 72
a:1:{s:18:"rotated_session_id";s:32:"22222222222222222222222222222222";}

add key_22222222222222222222222222222222 0 1483197000 72
a:1:{s:18:"rotated_session_id";s:32:"33333333333333333333333333333333";}

他にも,セッション情報の updated の値を見て,既に有効期限を過ぎている場合はエラーを返していたり,match_ipmatch_ua の一致確認をしていたり,読んでいて非常に勉強になるクラスだった.

/**
 * read the session
 *
 * @param  bool $force  set to true if we want to force a new session to be created
 * @return \Session_Driver
 */
public function read($force = false)
{
    // initialize the session
    $this->data = array();
    $this->keys = array();
    $this->flash = array();
    // get the session cookie
    $cookie = $this->_get_cookie();
    // if a cookie was present, find the session record
    if ($cookie and ! $force and isset($cookie[0]))
    {
        // read the session file
        $payload = $this->_read_memcached($cookie[0]);
        if ($payload === false)
        {
            // cookie present, but session record missing. force creation of a new session
            return $this->read(true);
        }
        // unpack the payload
        $payload = $this->_unserialize($payload);
        // session referral?
        if (isset($payload['rotated_session_id']))
        {
            $payload = $this->_read_memcached($payload['rotated_session_id']);
            if ($payload === false)
            {
                // cookie present, but session record missing. force creation of a new session
                return $this->read(true);
            }
            else
            {
                // unpack the payload
                $payload = $this->_unserialize($payload);
            }
        }
        if ( ! isset($payload[0]) or ! is_array($payload[0]))
        {
            logger('DEBUG', 'Error: not a valid memcached payload!');
        }
        elseif ($payload[0]['updated'] + $this->config['expiration_time'] <= $this->time->get_timestamp())
        {
            logger('DEBUG', 'Error: session id has expired!');
        }
        elseif ($this->config['match_ip'] and $payload[0]['ip_hash'] !== md5(\Input::ip().\Input::real_ip()))
        {
            logger('DEBUG', 'Error: IP address in the session doesn\'t match this requests source IP!');
        }
        elseif ($this->config['match_ua'] and $payload[0]['user_agent'] !== \Input::user_agent())
        {
            logger('DEBUG', 'Error: User agent in the session doesn\'t match the browsers user agent string!');
        }
        else
        {
            // session is valid, retrieve the rest of the payload
            if (isset($payload[0]) and is_array($payload[0]))
            {
                $this->keys  = $payload[0];
            }
            if (isset($payload[1]) and is_array($payload[1]))
            {
                $this->data  = $payload[1];
            }
            if (isset($payload[2]) and is_array($payload[2]))
            {
                $this->flash = $payload[2];
            }
        }
    }
    return parent::read();
}

類似した Issue を発見した

2011年の Issue で,FuelPHP のバージョンも大きく違うけど,コメントを読む限りは previous_id の扱いは昔から大きく変わっていないように感じた.論点としては rotation_time を超えた Ajax を呼び出したときを考慮しているという話だけど,そこまで長時間走る Ajax があったとしても,例えば php-fpm の request_terminate_timeout を設定していれば自動的にリクエストがタイムアウトするだろうし,設定で回避する方法もあるのでは?

github.com

memcached-tool

Memcached のデータを見るために memcached-tool を使った.実装を読むと dump のデータ構造も理解することができた.

$ memcached-tool localhost:11211 dump > memcached.dump

まとめ

  • FuelPHP 1.8 の実装を読んでみた
  • セッションをローテートするときに previous_id として「1世代前」のセッションを保持している
  • セッションを取得するときに rotated_session_id の値を使うため「1世代前」のセッションから「最新」のセッションを引き戻すことができる
  • FuelPHP の Issue を見たら,長時間走る Ajax の考慮という説明がされていた

関連記事

d.hatena.ne.jp

セルフアウェアネスを高めよう /「99%の人がしていないたった1%のメンタルのコツ」を読んだ

IBM 時代にお世話になった河野さんのシリーズ最新刊「99%の人がしていないたった1%のメンタルのコツ」が 9/14 に出版されたので,さっそく読んだ.出版おめでとうございます!改めて,自分自身を客観的に見直すことができたような気がする.

99%の人がしていないたった1%のメンタルのコツ

99%の人がしていないたった1%のメンタルのコツ

  • 作者: 河野英太郎,田中ウルヴェ京
  • 出版社/メーカー: ディスカヴァー・トゥエンティワン
  • 発売日: 2017/09/14
  • メディア: 単行本(ソフトカバー)
  • この商品を含むブログを見る

セルフアウェアネス

「自分のモチベーションを見つめ直す」という話が最初に出てくる.内発的動機と外発的動機に関しては「モチベーション3.0」に詳しく書いてあるけど,自分のことを知るのは本当に大切だと思う.また,経験とタイミングによってもモチベーションが推移するので,定期的に見つめ直す機会を作るのが良いと思う.

kakakakakku.hatenablog.com

ちなみに,個人的には「キャリア・アンカー」が好きで,定期的に読み直している.とは言え,何度やっても「奉仕・社会貢献」「純粋な挑戦」のスコアが高いため,僕のモチベーションはそういう方向性にあるんだなと自己認識をしている.

キャリア・アンカー―自分のほんとうの価値を発見しよう (Career Anchors and Career Survival)

キャリア・アンカー―自分のほんとうの価値を発見しよう (Career Anchors and Career Survival)

さらに,セルフアウェアネスとして「苦手な人」と「嫉妬」という話が出てくるのもすごく良かった.組織に所属していると,良くも悪くも「人間は感情に左右される」ことを感じるため,感情を言語化して対処するというのは,合理的なアクションだとは思うけど,いざ実践しようとすると感情的にタフだろうなという印象もある.

目標設定

「マンダラート」と呼ばれるメソッドは初耳だった.今までは基本的にマインドマップを使っていたけど,次に目標を整理する機会があったら試す.目標設定に限らず,自分の強みを洗い出したり,プロダクトの戦略を考えたり,幅広くユースケースがありそうなメソッドだと思う.

navi.dropbox.jp

リラックス

「笑う」ことでリラックスできるというのは前から知っていて,仕事で意識するようにしている.関連することとしては「ノンバーバル・コミュニケーション」を意識していて,常にリアクションをすることで,頭を活性化させている.瞑想の話も出ていて,これは「マインドフルネス」のことだと思うけど,僕がすごく苦手なことの一つだ.考えすぎてしまう性格なので,まずは数秒からでも挑戦してみたい.

感情

「落ち込むことをプラスと考える」という話も良かった.個人的によく落ち込むタイプなので,信頼のできる友人に相談してみたり,週末の過ごし方を工夫してみたり,本書に書かれているコツを積極的に実践してみようと思う.

XP 祭り 2017 で「Fearless Change を活用した組織変革」をテーマに発表してきた

今日は「XP 祭り 2017」で発表してきた!(最近は発表をしまくっている...w)

発表資料

今日の発表タイトルは 「全ては Fearless Change から学んだ,開発組織の変革を支えた実践的アプローチ」 にした.大好きな "Fearless Change" の活用事例を紹介したくて応募したので,採択されて良かった.

スクラムなどの特定の手法に依存せず,どんな組織にでも適用できる実践的なアプローチを紹介したはずなので,参考になれば嬉しいなと.

Fearless Change

全てのパターンを発表では紹介できなかったので,是非「組織変革のバイブル」として読んでみてもらえると!重要なのは,パターンを理解することではなくて,パターンを咀嚼して,自分の組織にフィットしたアプローチを実践することなので,間違えないように!本当にオススメの一冊!

Fearless Change アジャイルに効く アイデアを組織に広めるための48のパターン

Fearless Change アジャイルに効く アイデアを組織に広めるための48のパターン

  • 作者: Mary Lynn Manns,Linda Rising,川口恭伸,木村卓央,高江洲睦,高橋一貴,中込大祐,安井力,山口鉄平,角征典
  • 出版社/メーカー: 丸善出版
  • 発売日: 2014/01/30
  • メディア: 単行本(ソフトカバー)
  • この商品を含むブログ (16件) を見る

44 engineering management lessons

発表で少し紹介をした "44 engineering management lessons" も非常に参考になるため,読んでみてもらえればなと!

kakakakakku.hatenablog.com

他セッション

ワークショップには参加せず,セッションを聞いていた.まず,昼休みに行われてた「野良 LT」を聞いていて,チームの状態が悪いだけなのに「モブプロが悪い」みたいに言われてしまうのは,モブプロに限らず本当によくあるなーと思った.まずコミュニケーションの問題を立て直すことに着目するべきだと思う.逆に言えば,モブプロをしてチームの状態が悪いことに気付けたことに喜ぶべきかなー.

「陣形」の話で「プロのトラブルメーカー」に対して「ボックスワン」で対応する,という話があって笑った.つらい.今までの経験だと,エンジニアリングマネージャーがそういう対応をしている気がする.

まとめ

マネジメント系のコミュニティで発表したいなと思っていたので,今回発表できて良かった.会場は満席だったし,発表も盛り上がっていたように感じたので,少しでも参考になっていれば嬉しいなと.ちなみに冒頭で紹介をしたポッドキャスト omoiyari.fm にも出演をしているので,是非聞いてみてもらえると!

lean-agile.fm

今回「XP 祭り」は初参加だったけど,予想以上にエンプラな発表が多くて,こういうコミュニティなんだなーと感じた.一言に「アジャイル」と言っても,様々な形があり,多様性という意味では勉強になった.また来年も参加したいと思う!

関連記事

8月に July Tech Festa 2017 で発表した資料は以下にある.「DevOps + 組織変革」をテーマにしたため,より技術的な内容になっている.

kakakakakku.hatenablog.com

「パターン28. 経営層の支持者」をテーマに話したインタビュー記事もある!

developers.cyberagent.co.jp

参加者レポート

t-and-p.hatenablog.com

CA.io で「Amazon ES 移行」をテーマに LT してきた

昨日は Elasticsearch をテーマに開催された CA.io に参加してきた.サイバーエージェントグループのマッチング系サービスが対象なのに,友情出演という形で,特別に LT をさせてもらった.楽しかった!

cyberagent.connpass.com

発表資料

タイトルは「Amazon ES に移行をしたら幸せになれるのか?」にした.最近そこそこ大規模で,レガシーな Elasticsearch を Amazon ES に移行するプロジェクトを担当して,そこで学んだことをザックリと話した.LT だと全然時間が足りなくて,ネタっぽくなってしまった感はある.

speakerdeck.com

雰囲気

f:id:kakku22:20170912214903j:plain

補足

自動スナップショット

Amazon ES のダメな点として「自動スナップショットをリストアする場合はサポートに依頼する必要がある」という話をしたら,LT 中に大谷さんから「数日前に改善されたよ!」と指摘をもらって,実際に調べたら本当にリリースが出ていた.ただし,Amazon ES (Elasticsearch 5.5) 以降で使えるらしく,Amazon ES (Elasticsearch 2.3) では,引き続きダメだった.知れて良かった!!!

運用面

今回は時間が無くて話さなかった Amazon ES の運用面の話は去年に作った資料にまとめてある.参考になればと!

kakakakakku.hatenablog.com

Elasticsearch 6.0 is coming / @johtani

  • Elastic Pioneer Program で Elastic 6.0 を使うと粗品がもらえる!
  • Elastic 6.0 の特徴
    • ダウンタイムなしで 5.x から 6.x にアップデートできる
    • Upgrade Assistant
    • ソートクエリのパフォーマンスが改善する (index sorting)
      • 簡単に言うと,インデックスのときに順番を入れ替えてしまう
    • Elastic 7.0 からインデックスの _type を削除するので,警告が出るようになる

メジャーバージョンが上がるたびに進化していて凄いなと思う.Elasticsearch SQL の話を懇親会で大谷さんに聞いたら,6.0 では入らなそうとのことだった.良し悪しはありそうだけど,SQL 形式で書けることのメリットはありそうなので,期待してる.

タップル誕生が Elasticsearch を導入した3つの理由

  • MongoDB から Elasticsearch に移行した
  • 複数の検索項目があり,柔軟に実装できる点がメリット
  • 全文検索も現在実装中

位置情報を用いたElasticsearchの活用事例

  • すれ違いを検索するときに Elasticsearch を使っている
    • geo_point type でデータを格納している
    • geo_distance で検索をしている
  • 逆 Geocoding (緯度経度から住所を検索する)
    • 国土交通省のデータを Elasticsearch に入れている
  • Geo Shape を使うと Elasticsearch で範囲の中に特定の場所があるかを検索できる

Elasticsearch 活用 & 運用事例

  • mimi はマニアックな属性で検索ができるのが特徴
    • 完全一致 (年齢,住所…)
    • スコアマッチ (目/鼻/メガネ…)
  • インデックス再構築
    • エイリアスも reindex も採用しなかった

トルテが実践したマッチしたユーザーを除く3つの方法

  • マッチしたユーザーを除外する
  • Elastic Cloud と Amazon ES を検証した

speakerdeck.com

まとめ

最近は July Tech Festa 2017 / CROSS 2017 など,発表が多くてバタバタしているけど,移行事例を話せて良かった.また他社の事例を聞いて,まだまだ Elasticsearch を活用できるなと感じることができたため,適材適所に技術選定をしていきたい.

CROSS 2017 : Serverless Ninja Warriors でパネルディスカッションに参加してきた

今日 9/8 に横浜で開催された CROSS 2017 で,パネルディスカッションに参加してきた.テーマは Serverless Ninja Warriors で,サーバレスのガチ勢に囲まれて緊張したけど,楽しかったし,モチベーションも高まった.ありがとうございました!

2017.cross-party.com

資料

自己紹介として,簡単な資料を作成して最初に話をした.SpeakerDeck にアップロードするほどでもないため,そのまま画像で貼ってしまう.

話した内容としては,ザッと以下のような感じ.僕のポジションとしては「非ガチ勢」だったので,サーバレスに興味があるけど試したことがない人や,Lambda を管理コンソールで実装していて運用に困っている人などの参考になれば良いなと思っていた.

  • 個人的なサーバレスの定義は「FaaS の特性(ある意味で制約とも言える)を活かしたアーキテクチャ全般」と考えている(今のところ…)
  • サーバレスに限らず,スモールスタートを意識してチームに成功体験を作ると,導入障壁が下がる
    • そのために社内ツール系は最適
    • デプロイ/テストなど,ラストマイルも意識するとプロダクションレディな知見が得られる
  • CircleCI + Apex の組み合わせは非常に便利
    • Apex は複数ファンクションの管理も,複数環境へのデプロイもできる
    • ローカル環境は python-lambda-local を使っている

f:id:kakku22:20170908175225j:plain

f:id:kakku22:20170908175347j:plain

f:id:kakku22:20170908175406j:plain

質疑応答など

「CloudWatch Events など,Lambda 以外の AWS リソースはどのように管理しているのか?」という質問があり,ここは現状だと管理コンソールでポチポチしている.サービス全体で CloudFormation を導入するべきだと思うし,Apex + Terraform の構成にしても良いけど,現時点では既存のリソースへの影響が怖くて対応できていないという感じ.やらなきゃ!

「環境ごとにどのようにデプロイしているのか?」という質問もあり,これは Apex の Multiple Environments 機能を使っている.さらに Lambda の環境変数を Multiple Environments の定義ファイルに書けるため,それで invoke する Lambda の ARN などを管理している.あと IAM でも厳格にリソースを制限している.詳細は以下の記事を参照してもらえればと!

kakakakakku.hatenablog.com

補足など

ローカル環境の話が全体的にあまり無かったけど,僕は python-lambda-local を使っている.詳しくは以下の記事に!

kakakakakku.hatenablog.com

今日は話すタイミングが無かったけど,Apex の話をすると「Go 動くんでしょ?」とよく聞かれたりする.前に発表した資料の後半にまとめているけど,結局は Node.js の子プロセスで Go バイナリを動かして,プロセス間通信をしているだけなので,絶対に必要なら検討しても良いけど,AWS 公式でサポートされるまで待っても良いのでは?というのが個人的な判断かな.

kakakakakku.hatenablog.com

今回紹介したアーキテクチャは CloudWatch Events で Lambda を起動して SQS からメッセージを取得するため,ポーリング系の処理になっている.現時点では SQS のキュー操作は Lambda のイベントになっていないため,イベントドリブンにはなっていないけど,公式サポートが入ると嬉しいなとは思う.

会場で感じたこと

会場の各ホールが分離されていないため,隣のセッションの声がガンガン入ってきたり,マイクで音を拾えないと後ろの席まで届かなかったり,大規模カンファレンスの会場としては適していないのではないかなと思った.パネルディスカッションの序盤でそれに気付いたため,僕はかなり大きな声で話すようにした.

まとめ

今回パネルディスカッションに参加させてもらえて非常に刺激的だった.サーバレスとは何か?という疑問を自分なりに考え直せたことも良かったし,なによりも,パネルディスカッションに一緒に参加した皆さんの技術レベルが本当に高く,ディスカッションをするだけでも楽しかった.モチベーションも高まったし,もっともっと攻めて行かないとなと感じた.本当にありがとうございました!