itochin2の日記(仮)

主に備忘録。Perl、MySQL、Unity、開発管理などについて情報を残していきたい。

YAPC::Asia 2014に参加してきた。

ブログを書くまでがYAPCなので感想を書く。

前夜祭と1日目が仕事の都合で行けなくて、今回は2日目だけ参加した。
1日目は知り合いのトークも多かったので、見れなかったのは悔いが残った。

今年見たトーク

突然ITインフラを任された人のための…監視設計入門

インフラって普段専任の人にお任せで、あまり意識していなかったので興味があった。
障害が起きた時に慌てないように、何を決めておけばよいのかを学べて勉強になった。
アプリのバグとかを追うために、ログは出さないとっていうのは考えていたけど
サービス運営という目線で考えたら、ミドルウェアのログも意識しておかねばならないと思った。

google BigQueryでDWH構築

ビッグデータって良く聞くけど、よく知らないから聞いてみようと思って参加。
伊藤直也さんのトークは聞きやすかったし、スライドの構成もわかりやすかった。
アクセスログとかガンガン投げて、あとから解析できる技術にパラダイムシフトが起こっているのは
理解できて、バッチで解析のスクリプトを回す時代じゃないということが分かったのが収穫。

JSON SQLインジェクション脆弱性と、そこから学ぶセキュアプログラミングの原則

だいたいの内容は社内で共有された情報と被っていたので目新しいことではなかった。
バリデーションは範囲や型をちゃんとみましょう。
SQLのクエリは、プレースホルダを使って発行しましょう。
という確認をした。

地域.pmミートアップ 2014

Plack for Fun and Profit、ほんとにあったスキーマの話のどちらも非常に悩ましかったのだけど
我らがchiba.pmが飛び入り参加すると聞いて、そのまま見ることに。
酒飲みLT、ガチハッカソン、全員LTなどなど、地域ごとに特色があるなーと感じた。
10/11(土) chiba.pm行くぞ!
9/17(水) 五反田pm行くぞ!

Perlあるある

「人に理解されるように気をつけてコードを書こう」
これが印象に残っている。
仕事だと、あんまり一人で開発しないし、仮に一人だったとしても、今後引き継いだりするんだから
ロジックなりコメントなり、読み手に書き手の意図が伝わるように書いていきたい。
コードでコミュニケーションを取るってそういうことなんだろうな。

OAuth/OpenID Connectを用いてID連携を実装するときに気を付けること

FacebookのIDを使ってログインとか良く見るけど、途中でキャンセルした時の挙動とか
考えたことなかった。(実装したこともなかったし)
エラーとキャンセルは別にして扱い、ユーザーに次のアクションをきちんと提示してあげるのが
よいサービスという言葉が印象に残っている。

趣味開発のためのクラウド/VPS活用術

オススメのクラウドサービスを値段付きで紹介してくれた。
そしてVULTR押しだった。今はサクラVPSのアカウントあるけど正直全然使ってないので
他のを見るのはもう少し使ってからかなーと思った。
個人向けでもたくさんの選択肢がありますな。

感想

今年はインフラ寄りの話が多かった印象。
そして自分の弱いところでもあるので、自然とそっちに興味がいったのかもしれない。

無限コーヒー、無限ビール、無限かき氷、そんな無限に必要だったのかなw
あとパーカーは普通に使いやすそうでありがたい。
そして今年もwifiの安定さが抜群だった。不自由なく使えるありがたさ。

これからやること

よーしCPANに何か上げるぜーって言ってもいったい何を感があるので
これ便利だったとか、自分が詰まってしまったことのアウトプットする回数を増やそうと思った。


スタッフの方、スピーカーの方、お疲れ様でした。
また来年。

#yapcramenもやりました。


しかしこれつけ麺だったわー。
ラーメンじゃないから食べ直さないとダメだわー(棒

退職一時金の移換で思ったこと

ITソフトウェア厚生年金基金から手紙がきた。
退職一時金があるから、どうするか選べ、とのこと。

選べって言われてもよく分からなかったので、調べてメモした。

ITソフトウェア厚生年金基金 is 何

http://www.softkikin.or.jp/
国が運用してる年金の一部を代行して、年金を運用しているところ。
国とは独立して積立した年金を、将来支給する。

転職して、ITS基金を脱退したので、年金用に貯めてたお金どうする?
っていうのが今回の趣旨みたい。

追記:一時金が発生するのは、加入して3年以上の場合。

選択肢

一時金化する

そのままもらう。
20万以下の場合は、所得税の確定申告はいらない。

会社に企業年金制度がある場合

会社に企業年金制度がない場合

企業年金制度

企業が社員に対して年金を支給する仕組み。
退職金を一度にガッツリ支払うのは痛い会社側と
老後に定期的な収入が欲しい社員のニーズにマッチして、バブルの頃は流行った。
現在では運用が難しく、企業年金で会社が傾くと嘆かれるオワコン

ちなみに弊社も制度がなかった。

企業年金連合会

http://www.pfa.or.jp/
厚生年金基金が設立した特殊法人
運用利回りは年2.25%
メリット
65歳以降、死ぬまで受け取れる。
デメリット
65歳まで(基本的には)現金化できない。

65歳まで手を出せないし、年利2.25%ってあんまり魅力を感じない。
30年以上預けるより、自分で投資に回した方が良さそう。

個人型確定拠出年金

http://www.npfa.or.jp/401K/
国民年金基金連合会が運営する年金で運用する。
運用利回りは、年利3%程度。
メリット
掛け金に税金がかからないのが旨い。
投資する額も柔軟に増減でき、別の企業に転職しても運用を継続できる。
デメリット
60歳まで解約できない。
企業年金があるところに就職したら追加で積立できない。
その上、60歳まで口座維持手数料だけを払い続けるという制約もある。

企業年金オワコンであることを考えると、デメリットが発生する状況は少ないかもしれない。
銀行の定期預金よりも利回り良いし、税金面が優遇されているので
資産運用の一角に組み込むのもありかもしれない。

結論

年金として預けると30年は取り出せないので、少し慎重に考えたい。
個人型も確定で3%の利率じゃないので、いますぐに入れる程の魅力じゃない。
なので、一時金を受け取って、別の投資に回そうかなと思った。

その他

調べてて思ったけど、すごく理解しづらかった。
名称とか説明の粒度が統一されてないからだと思う。
サービス名と同じ階層に、組織名があるのが一番気持ちわるい。

以下のようにしたらわかりやすくないだろうか?

退職一時金

名前の通り。

会社に企業年金制度がある場合

会社がお金を出して、会社が運用する年金

社員がお金を出して、会社が運用する年金

会社に企業年金制度がない場合

個人がお金を出して、企業年金連合会が運用する年金

個人がお金を出して、国民年金基金連合会が運用する年金


名前と中身が正確に合致しているか微妙だけど、だいぶ見通しがよくなったと思う。

今後、年金のパターンが増えても、ルールに従って名称を決めれるようにした方が
名前をどうするか会議とかやらなくていいし、効率がよいはず。


法改正に携わる人、リファクタリングの本を読めばいいのにな。

新装版 リファクタリング―既存のコードを安全に改善する― (OBJECT TECHNOLOGY SERIES)

新装版 リファクタリング―既存のコードを安全に改善する― (OBJECT TECHNOLOGY SERIES)

Module::Spyでテンプレートに渡したパラメータのテストをする

概要

JSONを返すAPIのテストなら、スキーマの確認がやりやすいのだけど
テンプレを返すコントローラってどうやってやろうか、という課題。

テンプレを表示して目視する作戦は効率悪いし、絶対漏れるし
継続的にするの無理だし、プログラムのテストとテンプレのテストが
混ざっていてイケてない感。

IFで使用するパラメータを渡しているかが知りたいのであって
分岐した結果の表示は、また別問題だと思うのです。

で、ググった。

[perl] コントローラがどのテンプレを表示したかをテストする - blog.64p.org

テンプレの名前がわかるなら、渡しているパラメータも分かるのでは!?

で、実際にやってみたらできた!

ざっくりこんな感じ。

$spy = spy_on('Text::Xslate', 'render');

# コントローラ呼ぶ
my $mech = Test::WWW::Mechanize::PSGI->new(app => $app);
$dat = $mech->get('/');
is($dat->code. 200);

my $tmpl = $spy->calls_first->[1];
my $tmpl_params = $spy->calls_first->[2];

is($tmpl, '/index.tt');
my ($ok, $error) = ThaiSchema::match_schema(
    $tmpl_params,
    type_hash({
        member_id => type_int(),
        items => type_array(
            id => type_int(),
            name => type_str(),
            num => type_int(),
        ),
        pager => type_hash({
            total_entries => type_int(),
            page => type_int(),
            hashNext => type_bool(),
            rows => type_int(),
        })
    });
);
is_deeply($tmpl_params, $expected);

calls_firstの1番目以降に、renderメソッドに渡しているパラメータが入っているので
あとは煮るなり焼くなり、好きにできて嬉しい。

まとめ

なんかページの表示がおかしいって時に、コントローラのテストを行うことで
俺のせいじゃないです問題の切り分けが素早くできるようになるはず!

ajaxでクロスドメインのAPIを叩く時にやったこと

アプリのweb viiewから、ajaxでクロスドメインAPIを実行しようとして
とても大変な思いをしたので備忘録。

概要

  • APIサーバーから、アプリのWebViewで表示するHTML(文字列)を取得する
  • HTMLの中で別サーバーのJSを読み込む。
  • JSからXMLHttpRequestAPIを実行する。
  • APIはGETとPOSTの2種類。
  • APIサーバーはPerlで、WAFにAmon2を使用。

GETが失敗する件

ログを見ると、OPTIONSリクエストに403を返していた。

What is OPTIONSリクエスト?

プリフライトリクエストという。
リクエストを送信しても安全か?サーバーがリクエストに対応しているか?
ということを調べるために、ブラウザが特定の条件を満たす場合に飛ばす。

特定の条件

以下、HTTP access control (CORS) | MDNより引用。

  • GET または POST 以外のメソッドを使用します。また application/x-www-form-urlencoded、multipart/form-data、または text/plain 以外の Content-Type とともに POST を行う場合、例えば application/xml または text/xml を用いて XMLペイロードをサーバーへ送るために POST を用いるような場合は、リクエストでプリフライトを行います。
  • カスタムヘッダをリクエストに設定します (例えば、X-PINGOTHER のようなヘッダを用いるリクエスト)。

プリフライトリクエストに対応する

以下のモジュールを使った。
Plack::Middleware::CrossOrigin - search.cpan.org

app.psgi
enable 'Plack::Middleware::CrossOrigin',
    origins => '*',
    headers => '*',
    methods => ['GET', 'POST'];

結果

PC 動いた!
iPhone 動いた!
Android 4.1.2 動いた!
Android 2.3.6 動かない!

Androidェ・・・
ググったらこんな以下のような記事がヒット
Android 2.3 の WebViwe で GET によるクロスドメインリクエストが最初の1回しか成功しない - latest log
webviewでGETはまともに動かないので、JSONPにしろとのこと。

JSONPに対応する

What is JSONP

  • JSでは、クロスドメインにアクセスできない制限がある(同一生成元ポリシー)
  • クロスドメインからJSをダウンロードすることは可能である。
  • よって、クロスドメインから、APIの戻り値が入ったスクリプトを取得することは可能である。

という小細工仕組み。
JSONPで公開しているリソースは、誰からでも参照できてしまうので
機密情報を入れると情報漏えいのリスクがあり、注意が必要。

以下のモジュールを使った。
Plack::Middleware::JSONP - search.cpan.org

app.psgi
enable 'Plack::Middleware::JSONP';

結果

PC 動いた!
iPhone 動いた!
Android 4.1.2 403 forbidden
Android 2.3.6 403 forbidden

Androidェ・・・
これは、Amon2がJSONPを拒否していた。
JSON hijacking対策として以下の条件を満たす場合は403。
・GETリクエスト
・X-Requested-Withヘッダが付いていない
Cookie送っている
UserAgentにAndroidがある ←!!

JSONPスクリプトタグからのリクエストなので、ヘッダは付けられない。

他の対策案を考える

JSONPを拒否するのをやめる

せっかくフレームワークレベルで対策しているのに、いけてない感。

POSTでGET

語感だけは楽しそうだけど、いけてない感。

GETするのをやめる

HTMLを返すときに、APIの結果を埋め込めばいいんだ!
そして、埋め込まれた結果を使って、JSでDOMを弄くろう!
(いま考えると、なぜテンプレートを使おうという発想にならなかったのか)

APIの結果をHTMLに埋め込んだ。

こんな感じに。

{"result":{"id":1, "message":"hogehoge"}}

結果

PC 動いた!
iPhone 動いた!
Android 4.1.2 動かない!
Android 2.3.6 動かない!

htmlに書いたJSに「//」が含まれていた。
そしたら、以降がコメントとみなされて動かなくなってた。

加工済みのテンプレートを返す対応

結局こうなった。

結果

PC 動いた!
iPhone 動いた!
Android 4.1.2 動いた!
Android 2.3.6 動いた!

紆余曲折あり、なんとかGETできるようになった。
うそ。GETじゃなかった。条件分岐したテンプレを返しただけ。

POSTが失敗する件

Androidだけまた動かない。
OPTIONSで200 OKを返しているが、ブラウザでabortしている。

ここは理由が分からなかったので課題。
OPTIONSを飛ばさないでPOSTする分には動作していたので、
カスタムヘッダを削る&Content-Typeに「application/x-www-form-urlencoded」を指定で対応。

結果

PC 動いた!
iPhone 動いた!
Android 4.1.2 動いた!
Android 2.3.6 動いた!

こいつ、動くぞ!
要件は満たされた。

Access-Controll-Allow-Origin:"*"ってどうなのか

全てのホストからのリクエストを許可する設定はよくないので、Originを指定したいが
Androidのweb viewはリクエストのOriginがnullになっているというやる気のなさ。

しょうがないので、特定のリクエストの時のみ、CORSヘッダを付与するようにした。

まとめ

動いたことに満足しがちだけど、クロスドメインに紐づくセキュリティリスクも
きちんと知ることが重要。
あと、Androidのweb viewでクロスドメインは罠が非常に多かったので
jsをAPIと同じホストに置くのが一番良いのではないかと思う。

参考

http://www.w3.org/TR/cors/
https://developer.mozilla.org/ja/docs/HTTP_access_control
ここを熟読して、CORSとはなんなのかをよく学ぶべき。

http://www.atmarkit.co.jp/ait/articles/0908/10/news087.html
JSONPを使用するまえに熟読して、リスクを知るべき。

クロスドメインで調べた事メモ

ajaxでクロスドメインだと動かない件で、ググったことが多かったのでメモ。

そもそもクロスドメイン制約って?

Ajaxでは同一生成元ポリシーにもとづくセキュリティ上の制約がある。
以下の1つでも違うとアクセスできない。
・ホストが違う
プロトコルが違う
・ポートが違う

じゃあどうすれば?

JSONP

JSONPの仕組み
  1. javasciprtの読み込みは別ドメインからできる。
  2. なのでjavascriptを読み込む体で、HTTPリクエストする。リクエストにはcallbackってパラメータいる
  3. そしたらjavascriptのコードが返ってくる(おや?偶然だけど中身はJSON!)

request例
/api/item/detail?callback=jsonp&id=100

response例
content-Type: text/javascript
content: jsonp({"result":{"item_id":100, "item_name":'アイテム'}})

plackアプリで実装するとき
PLACK::Middleware::JSONPを使う
パラメータに'callback'があれば、自動的にjsonを上記の形にして
sizeとかcontent-Typeを修正してくれる。
JSONJSONPの対応を、モジュールに手を入れずにできる。

JSONPの課題
  • ブラウザ依存ある

XMLHttpRequest Level2に対応したブラウザじゃないとダメ。
IE7とかはさようなら。。

  • コードの精査はできない。

危険なコードも制御できずに実行される。

  • GETはできるけどPOSTできない。

ajaxで商品の参照はできるけど、購入は無理。

Access-Control-Allow-Originヘッダを付ける

レスポンスにヘッダを付けてあげる。
上記ヘッダで指定のオリジン(プロトコル・ホスト・ポート情報)が一致しているところからの
アクセスは受け付けるヘッダ。

まとめ

今回は深遠なる理由からJSONPで対応したが、
基本的にはヘッダを付けて制御するのが王道みたい。
ヘッダの値をアプリケーションで見て、制御がいるのかフレームワークで吸収されているのかを
調べるのがTODO。

user-agentでOSとか判定するのは辛かった

user-agentを使用して、OSとかブラウザとか判定する実装した。
ようは、動作環境を満たすかチェックしたかったので。
吐きそうなくらい辛かった気持ちを忘れないためにメモを残す。

要件

まずはCPANをチェック

HTML::ParseBrowser - search.cpan.org
[OK] OSのバージョン取れる。
[OK] ブラウザ名取れる。
[NG] スマホかどうかは不明。
[NG] タブレットかは分からない。

Parse::HTTP::UserAgent - search.cpan.org
[OK] OSのバージョン取れる。
[OK] ブラウザ名取れる。
[NG] スマホかどうかは不明。
[NG] タブレットかは分からない。

HTTP::UserAgentStringParser - search.cpan.org
そもそも外部と通信する必要あるのが懸念材料。

Woothee - search.cpan.org
[NG] OSのバージョン取れない。
[OK] ブラウザ名取れる。
[OK] スマホかどうか分かる。
[NG] タブレットかは分からない。

検討したこと

CPANモジュールはどれも少しずつ惜しい。

そしてアプリケーションで利用するためには、
windowsは7も8もwindowsとして管理したり、'mac'という文字列じゃなくて定数にしたかったり
様々な加工が必要だったので、泥臭く実装することにした。

面倒だったポイント

  • IEMSIEって入っていたが、IE11から急になくなった
  • WindowsXPは32bitと64bitで違う文字列(NT5.1とNT5.2)
  • OS Xのバージョン区切りが「_」と「.」のパターンある
  • OS X のバージョン終わりが「)」と「;」のパターンある
  • androidはMobileがないヤツがタブレットだが、例外もある
  • ChromeChrome.*Safariってなってる
  • テストケース用意するのがしんどい。

実装されたもの(いろいろ出していけない部分は削っている

感想

もうUserAgentで判定する世界はなくなればいいのに。
好き勝手に文字列作りすぎ。

perlの文字列をバイト数で切り取るヤツ

APIに渡す文字列は25文字(50byte)でよろしく、
という要件に対応するサブルーチンを実装した時のメモ。

サブルーチンでは以下の3つを考慮する。
・文字数制限を満たす
・バイト数制限を満たす
・文字列として成立する(単純にバイト数でぶった切ると、文字列がおかしくなる


実装の時に調べてしったこと

・bytesプラグラマは非推奨
http://perldoc.perl.org/bytes.html

雑感

文字列は入り口でデコードして、出口でエンコードなんだから、
encodeしてlengthを取るのはごく普通なことだと思った。

あと、文字数がバイト数より大きいくなることってありえるのかなぁ。
よくて同じな気がする。
そうだったら、文字列の長さチェックは省略してもよいのかもしれぬ。