Cookpad Tech Kitchen #9 〜1行のログの向こう側〜 を開催しました!

こんにちは! @yoshiori です。

2017/07/26、技術系イベント「Cookpad Tech Kitchen #9 〜1行のログの向こう側〜Cookpad Tech Kitchen #9」を開催しました。 (はい。僕が記事公開するの忘れててだいぶ遅くなっちゃいました>< ごめんなさい)

クックパッドの技術的な知見を定期的にアウトプットすることを目的とする本イベント。第9回目となる今回は「ログの活用方法」をテーマに開催しました。(月に1回程度開催しています)

月間6000万ユーザが使っているクックパッドには大量のログが集まってきます。そのログを効果的に活用してサービスやユーザに還元するための取り組みについて、インフラ、広告事業、サービス開発それぞれの視点で知見の発表を行いました。

発表資料を交えてイベントのレポートをしたいと思います。

f:id:cookpadtech:20171003220443p:plain イベントページ:

Cookpad Tech Kitchen #9 〜1行のログの向こう側〜 - connpass

発表内容

「クックパッドのログをいい感じにしているアーキテクチャ」

一人目の発表者であるインフラストラクチャー部部長の星(@kani_b)は、SRE としてAWSやセキュリティ関連でのサービスインフラ改善に携わっています。

今回は AWS Summit Tokyo 2016 Developer Conference で発表した内容 の続きとして発表を行いました。 具体的な数字として Fluentd に流れているログベースで言うとデータ総量は 400 〜 600GB / 日、レコード数は 8 億レコード以上 / 日(秒間 8,000 〜 25,000 レコードくらい)という規模のデータを扱っています。これをどのように集めて処理しているのかを紹介しました。

資料

「広告ログのリアルタイム集計とその活用」

二人目の発表者であるマーケティングプロダクト開発部の渡辺(@wata_dev)は、主に配信基盤の改善やマーケティングプロダクト開発部で開発しているサービスの基盤周りのサポートを行っています。

クックパッドでは広告の配信も自社で行っており、そのためにどのようにログを活用しているのかを、過去どういった問題点があってそれをどのように解決していったか、異常検知だけではなく配信制御や在庫予測などなど広告配信というドメインで実際に必要になるケースを出しながら紹介しました。

資料

「ログを活用したサービス開発」

3人目の発表者であるサービス開発部の外村(@hokaccha)は、バックエンドからWebフロントエンド、モバイルアプリの開発など幅広い分野でCookpadのサービス開発に携わっています。

発表ではモバイルアプリのロギングのやりかたから始まり、実際のログの活用方法としてサービス開発側でログをどのように扱っているか、どう活かしているかを紹介しました。 行動分析としてログの設計、ログの分析やその可視化によってサービス改善の意思決定に使われていること、ログを利用した機能開発として調理予測という実際にログがサービスとして使われている事例を紹介しました。

資料

「ログ」をテーマにしたご飯も登場!

クックパッドのイベントではご来場の感謝を込めて、会場で手作りしたご飯でおもてなしをします(食べながら飲みながら発表を聞いていただくスタイル)。今回はテーマである「ログ」にちなんだメニューを用意してもらいました。

f:id:cookpadtech:20171003220752j:plain

こちらはメッセージ入りのライスケーキ。クックパッドのインフラエンジニアが大切にしている言葉に「1行のログの向こうには1人のユーザがいる」というものがあります。画面で見るとたった1行のログだけど、その向こうには大切にすべき1人のユーザがいる、ということを思い出させてくれる合言葉です!

f:id:cookpadtech:20171003221913j:plain

川嶋シェフの粋な心意気で、素敵なメッセージもテーブルに並びました。

f:id:cookpadtech:20171003221721j:plain

こちらはログ=丸太をイメージした湯葉のデザートです。中に入っているのは甘すぎないところがおいしいあんこです。

まとめ

いかがでしたか。クックパッドでは毎月テーマを変えて技術イベントを開催しています。ご興味のある方は是非ご応募ください。

cookpad.connpass.com

新しい仲間を募集中

■ 日本最大の食のビッグデータを扱う「データ基盤」の開発に興味がある方 https://info.cookpad.com/careers/jobs/careers/data-infra-engineer

■ 広告事業の「Webアプリケーション開発」に興味がある方 https://info.cookpad.com/careers/jobs/careers/marketing-product-engineer

■ クックパッドアプリの「Webアプリケーション開発」に興味がある方 https://info.cookpad.com/careers/jobs/careers/software-engineer

Ruby の脆弱性を見つけた話

こんにちは、技術部の遠藤(@mametter)です。フルタイム Ruby コミッタとして、クックパッドにあたらしく入社しました。よろしくお願いします。

最近、Ruby や RubyGems の脆弱性を発見して、その結果セキュリティリリースにつながるということを経験しました。どういう動機でどのように脆弱性を発見したか、どのように通報したか、などについてまとめてみます。Ruby の脆弱性を見つけたけどどうしよう、という人の参考になれば幸いです。

HackerOne について

HackerOne という脆弱性情報の通報と公開のためのプラットフォームをご存知でしょうか。

OSS にとって脆弱性情報の管理は面倒なものです。脆弱性の通報を秘密裏に受け付け、関係者だけで議論しなければなりません。そのため、通常のバグトラッカとは別のコミュニケーションチャンネルを用意する必要があります。

そこで HackerOne が使えます。HackerOne は簡単に言えば、脆弱性情報の管理に特化した非公開のバグトラッカサービスです。登録されたOSSプロジェクトに対して誰でも脆弱性情報を通報できます。また、プロジェクトメンバ間や報告者の間で非公開の議論もできます。問題が解決された際には議論の内容が公開されます。

さらに、Internet Bug Bounty (IBB) program がインターネットを維持するために特に重要なソフトウェアと指定している一部のプロジェクトについては、通報されたバグが開発者によって脆弱性と認定された場合、IBB から報告者に報奨金が支払われます。

ただ、報奨金が出るのは良し悪しです。良い通報をしてくれた人が報われるのは当然良いことなのですが、報奨金目当ての雑な指摘がたくさん来るという副作用があります。完全に見当違いな例を上げると、「SVN が公開状態だぞ!」とか、「バグトラッカの issue 一覧が丸見えだぞ!」とか 1 。もちろん有益な通報も来るのですが、通報を受ける側としては、もうちょっとノイズが減るといいなあ、と思っています。

そこで、 Ruby ユーザの方々に HackerOne を紹介したいと思い、そのために一回、私自身が通報者としてのプロセスを経験してみました。

ターゲットの選定

自分が一番慣れている OSS プロジェクトは Ruby なので、Ruby のソースコードから脆弱性を探すことにしました 2 。Ruby に標準添付されたライブラリの中で、「脆弱性といえば WEBrick」。という直観にもとづき、そのへんをターゲットにしました。

探す脆弱性の選定

「Ruby の脆弱性」に明確な定義はありません。ある Rails アプリに任意コード実行(外部から攻撃コードを送り込んで実行させられる)があれば、どこかに脆弱性があることは確かですが、Ruby の脆弱性かもしれないし、Rails(またはサードパーティ)の脆弱性かもしれないし、はたまたユーザの書いたプログラムの脆弱性かもしれません。極端な例では、system("ls " + user_input) みたいなプログラムがあると OS コマンドインジェクションができますが、これを Ruby の system のせいだと言われても困ります。Ruby 本体かユーザプログラムかの切り分けは、わりと揉めやすいところです。

今回はここで揉めないよう、言い逃れしにくい脆弱性を探すことにしました。それは、そのプロジェクト自身が過去に脆弱性と認めたバグに近いバグを見つけることです。

WEBrick の過去の脆弱性を探したら、『WEBrick にエスケープシーケンス挿入の脆弱性』が見つかりました。要するに、ログにエスケープシーケンスを紛れ込ませることができたら脆弱性のようです。個人的には「このくらいで脆弱性なんだ」という驚きがありますが、一部のターミナルエミュレータはエスケープシーケンスで危うい挙動を起こせることがあるそうです。詳しくはリンク先を読んで下さい。

脆弱性の発見

実際に脆弱性を探します。過去の脆弱性の修正コミットを手がかりに WEBrick のログ出力まわりを読解すると、WEBrick::AccessLog.escape というメソッドでエスケープシーケンスを除去(サニタイズ)し、WEBrick::BasicLog#error#warn などのメソッドで実際にログを書き出すらしいことがわかります。ここで、AccessLog.escapeWEBrick::HTTPStatus::Status#initialize の中でしか呼ばれていないことに気づきました。つまり、この例外経由でしかサニタイズがされないらしいということです。

そこで、#error#warn を直接呼び出すところを探したところ、WEBrick::HTTPAuth::BasicAuth#initialize に見つかりました。不正なユーザ ID で BASIC 認証すると、そのユーザ ID がサニタイズなしでログに流れ出るようです。

(あっさり見つけたように書いてますが、実際にはいろいろ探したり試行錯誤したりしながらだったので 2 晩くらいはかかったと思います)

脆弱性の確認

この脆弱性が実際に exploit 可能であることを確かめます。WEBrick の BASIC 認証のコードを Web 検索しながら書きます。

require "webrick"
require "webrick/httpauth"

srv = WEBrick::HTTPServer.new({ Port: 34567 })
db = WEBrick::HTTPAuth::Htpasswd.new("dot.htpasswd")
authenticator = WEBrick::HTTPAuth::BasicAuth.new(UserDB: db, Realm: "realm")
srv.mount_proc("/") do |req, res|
  authenticator.authenticate(req, res)
    res.body = "foobar"
  end
srv.start

↓サーバを起動した様子 f:id:ku-ma-me:20171004180721p:plain

このサーバに対して、エスケープシーケンスを混入した不正なユーザ ID でログインを試みます。ここでは、"\e]2;BOOM!\a" というエスケープシーケンスで実験しました。これは、端末のタイトルを BOOM! という文字列に変える命令です。

require "open-uri"

open("http://localhost:34567/login",
  http_basic_authentication: [
  "ESCAPE SEQUENCE HERE->\e]2;BOOM!\a<-SEE WINDOW TITLE",
  "passwd"
]).read

↓クライアントを起動する様子 f:id:ku-ma-me:20171004180728p:plain

この結果、WEBrick サーバを動かしている端末のタイトルが、BOOM! に変わることが確認できました。

↓攻撃成功した様子(タイトルバーが "BOOM!" になっているところがポイント) f:id:ku-ma-me:20171004180738p:plain

脆弱性の報告

めでたく(?)脆弱性を確認できたので、HackerOne に投稿します。Weakness や Severity は該当すると思うものを選ぶだけですが、よくわからなかったら空欄でもよさそうです。重要なのは Proof of Concept です。といっても、普通のバグ報告と同じです。どういう問題であるかと、再現手順をきっちり書けば十分でしょう。問題の重大さを書くとさらに親切です。今回の脆弱性は過去の脆弱性の修正漏れなので重大さに議論の余地はないと考え、ほとんど再現手順だけを簡単に書きました

あとは普通のバグ報告と同じ対応です。よほど致命的な問題でない限り(あるいは致命的な問題であっても)、開発者はなかなか返事をしてくれないものです。パッチを書いて送ったり、ときどき催促したりしながら、気長に待ちます。今回は、4 月に報告して、セキュリティリリースは 9 月でした。

セキュリティリリース

普通の報告者ならここで終わりですが、今回は私が Ruby コミッタでもあるということで、セキュリティリリースに少しだけ参加しました。といっても私がやったのは、ブランチマネージャや公式サイト管理人たち(@unak さん、@nagachika さん、@hsbt さん)の指示の下、私が書いたパッチをコミットしただけです。あとは彼らが一生懸命 tar ball を作ってリリースするのを応援していました。

コミットしてからリリースアナウンスを出すまでの時間を最小化するため、リアルタイムのコミュニケーションを取りながら進める必要があります。Ruby のブランチマネージャたちは、セキュリティリリースのたびに命を燃やして頑張っています。敬礼。

報奨金の獲得

無事セキュリティリリースがなされたということで、IBB から報奨金として $500 をいただきました。このプロセスも簡単に説明しておきます。

まず、初めて報奨金をもらう場合、税務上の書類 W-8BEN を作成して提出します 3 。すべてオンラインの手続きなので難しいことはありませんでした。

それから支払いの受取方法を登録します。PayPal 、Bitcoin via Coinbase 、銀行間振替がサポートされていました。私は銀行間振替を選んだので、口座情報を入力するだけでした。4

RubyGems の脆弱性

同じようなプロセスで、RubyGems にも通報をしました。

詳細は割愛しますが、CVE-2015-3900 という過去の脆弱性が適切に修正されていないというものでした。ただ、こちらはすでに他の人が通報済みだったので、Duplicate でクローズされました。

ただ、コードを読んでいるうちに次の 3 つの問題を新規発見しました。こちらの方の通報は認められたようです。

これらの通報に対する修正は、RubyGems 2.6.13 としてリリースされています。特に 3 つめの問題は、WEBrick の問題よりもう少し重大だと思うので、バージョンアップすることをおすすめします。なお、Ruby 2.4.2 は RubyGems 2.6.13 の修正を含んでいるので、Ruby 2.4.2 にするのでも大丈夫です。

まとめと所感

Ruby の脆弱性を探して HackerOne に通報した事例を紹介しました。

セキュリティ報告をすると、多くの場合、公式サイトでクレジットに載せてもらえるので、承認欲求が満たされますし、HackerOne ならちょっとした報奨金までもらえます 5

最初に触れたとおり、今のところ HackerOne 経由で Ruby にくる通報は、雑な通報が多くて Ruby 開発者的にはノイズが多いと感じられています。この記事を見た人が、(Ruby に限らず)有意義な通報を増やしてくれるといいなと思います。

最後になりましたが、クックパッドでは脆弱性のない Rails アプリを作れる Web アプリケーションエンジニアを募集しています。詳しくは募集要項ページをご覧ください。


  1. Ruby はオープンソースプロジェクトなので、もちろん意図的に公開しています。

  2. IBB の FAQ によると、プロジェクトの開発者自身でも、(1) そのプロジェクトで収入を得ていないこと、(2) 問題のコミットに関わった人間でないこと、の条件を満たせば報奨金がもらえます。私はフルタイムコミッタになったので、もう無資格のようですが、今回の通報は入社前にやりました。

  3. 米国非居住者が米国の人から支払いを受け取るときに、源泉徴収の金額を低減してもらうための書類。

  4. 正確には、海外からの送金を受け取るために、銀行にマイナンバーの登録をする手続きもありました。

  5. エスケープシーケンスインジェクションでは大した金額にはなりませんでしたが、もっと重大な脆弱性ならそれなりに高額になるはずです。たとえば有名な Shellshock だと $20,000 も支払われたそうです。

料理の追体験を実現する「タイムライン」のデザイン

こんにちは、サービス開発部のデザイナー若月(id:puzzeljp)です。

すでにご利用していただいている方もいらっしゃると思いますが、iOS / Android アプリにタイムラインという機能が登場しました。

f:id:puzzeljp:20170929172254p:plain

先日そのタイムラインのデザインについての登壇しました。 (イベントレポートはこちら)
今回はその時話しきれなかったこと、タイムラインの開発時のデザインの工夫や苦労についてご紹介します。

タイムラインとは

フォローしているユーザーさんやすべてのユーザーさんの新しいレシピ投稿やつくれぽが見られるようになりました。 レシピ検索では出会えなかった料理に出会うことができ、実際にレシピが見られるので料理をすることができます。

どんな使い方があるかと言うと例えば「Aさんがパエリアを作っている!私作ったことないけど、Aさんが作っているなら私でも作れそう。作ってみよ!」のような料理の追体験ができるようになります。

f:id:puzzeljp:20170929172351p:plain

新しい見え方

タイムラインを開くと、「レシピのカード」と「つくれぽのカード」があります。検索結果と比べて料理の写真を大きく見やすく表示しています。ユーザーさんのアイコンや名前がカードに表示されているので、誰がどんな料理をしているかわかるようになりました。

f:id:puzzeljp:20170929172408p:plain

登壇資料

当日の登壇資料については、以下で見ることができます。

デザインアプローチ〜工夫と苦労〜

つくれぽカードのデザイン

タイムラインでは、「つくれぽのカード」で料理の追体験ができるような様々な工夫をしています。

工夫した点

つくれぽとレシピ投稿を比較すると、つくれぽは気軽に投稿できます。そのため、タイムラインを見ると「つくれぽのカード」の方が多く存在します。もう1つに、つくれぽは作った直後に送るため、今日何の料理を作ったこともわかるようになります。 そのため、レシピ投稿と違い「誰のレシピを作ったのか」「何のレシピを作ったのか」が必要となります。

「誰のレシピを作ったのか」「何のレシピを作ったのか」がタイムライン上でより伝わる物は何かをWebプロトタイピングで検証をしました。Webプロトタイピングのメリットして、以下があげられます。

  1. 実データを利用できること
  2. アプリよりもより高速に検証できること
  3. デザインプロトタイピングよりも正確に検証できること

実際に検討したレイアウトについてご説明します。

A案
作者名やレシピ名を同じ文章として扱いました。文字の大きさは同じですが、色はそれぞれ分けています。1文として見えるので文章としては見やすくしました。
B案
A案と似ていますが、レシピ名を目立たせるために、作者名を小さくすることでバランスを取りました。1文というのは同じですが、文章内で優先度がつきました。

f:id:puzzeljp:20170929172428p:plain

他にもレイアウトを考えましたが、最終的には以下のレイアウトになりました。
理由としては、タイムラインでカードが並んでいる場所に、文章があっても読まないのでは?という仮説がありました。それを解決するために、写真の上に「誰のレシピなのか」、写真の中に「何のレシピなのか」というレイアウトにしました。このレイアウトにしたことにより、適度な文章量と写真も目立つように、カードの高さも少なくなりました。

f:id:puzzeljp:20170929172450p:plain

苦労した点

今回のWebプロトタイピングは、メリットを活かした検証ができました。 問題点として、細かいデザインはアプリと異なるため、実際のアプリに実装してみたらイメージと異なったことやWebプロトタイピングも実装の時間がかかり、アプリの実装にも時間がかかってしまうことでしたが、デザインのプロトタイピングよりは検証は正確にできる点はやはりメリットだと感じました。

誰が作ったがわかる機能

フォローユーザーさんが既につくれぽを送ったレシピに対して、自分自身がつくれぽを送ると、送ったつくれぽが、フォローユーザーさんの「つくれぽのカード」に表示されるようになります。他のフォローユーザーさん同士でも同じレシピにつくれぽを送っていても「つくれぽのカード」に表示されます。

工夫した点

表示される内容は、「誰が作ったかわかる見出し」、それぞれのアイコン・名前とつくれぽの写真です。 例えば見出しには、「○○さんと△△さん他n人が作りました」や「○○さんがn回リピートしています」等と表示され、「誰が作っているか」「誰が何回作っているのか」「自分自身が何回作ってるか」がわかるような文言を20パターンほど用意しています。パターンが多いことで、より正確に「誰が作っているか」「誰が何回作っているのか」「自分自身が何回作ってるか」がわかるようになりました。

f:id:puzzeljp:20170929172510p:plain

苦労した点

見出しの部分が一番苦労しました。まず起こりうる組み合わせを考え、その後実際に表示される文言を考えましたが、「カード投稿者が他人の場合は他人をテキスト内に含めない」や「テキストがカード投稿者が自分の場合と異なる」などのカードの見た目は共通しているものの表示される文言が違うと複雑になってしまいました。 複雑になってしまったことで、テストケースを回した時に、その起こりうる組み合わせを出すことが難しく、専用のアカウントを作成し、ログインし確認を行う作業を20回以上しました。
もし次回こういった文言を考える時には、ユーザーさんに最低限伝わる文章を考えようと思いました。ユーザーさんにわかりやすいものをと思いましたが、開発が遅くなってしまいユーザーさんに届くのが遅くなってしまうよりも早くリリースを行い、検証をしたほうが良いためです。

まとめ

タイムラインは、時間をかけて開発を行ってきました。新しい機能のため、実装に時間がかかるのはもちろんですが、デザイン的にも様々な工夫を行ったためです。 そんなにデザインを工夫する必要があるの?…といった部分があるかもしれませんが、「タイムラインを見て料理がしたくなる」「タイムラインを見ていて、料理をしたくなったらレシピを見たら料理ができる。」そんな体験が自然とできるようにと開発を行いました。
タイムラインがリリースされたことによって、みんながどんな料理をしていることがわかるようになりました。 気になった作者さんをフォローをすると、よりタイムラインが楽しく、料理がしたくなるようになると思います。ぜひタイムラインを使ってみてください!

/* */ @import "/css/theme/report/report.css"; /* */ /* */ body{ background-image: url('https://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527163350.png'); background-repeat: repeat-x; background-color:transparent; background-attachment: scroll; background-position: left top;} /* */ body{ border-top: 3px solid orange; color: #3c3c3c; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; line-height: 1.8; font-size: 16px; } a { text-decoration: underline; color: #693e1c; } a:hover { color: #80400e; text-decoration: underline; } .entry-title a{ color: rgb(176, 108, 28); cursor: auto; display: inline; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; font-size: 30px; font-weight: bold; height: auto; line-height: 40.5px; text-decoration: underline solid rgb(176, 108, 28); width: auto; line-height: 1.35; } .date a { color: #9b8b6c; font-size: 14px; text-decoration: none; font-weight: normal; } .urllist-title-link { font-size: 14px; } /* Recent Entries */ .recent-entries a{ color: #693e1c; } .recent-entries a:visited { color: #4d2200; text-decoration: none; } .hatena-module-recent-entries li { padding-bottom: 8px; border-bottom-width: 0px; } /*Widget*/ .hatena-module-body li { list-style-type: circle; } .hatena-module-body a{ text-decoration: none; } .hatena-module-body a:hover{ text-decoration: underline; } /* Widget name */ .hatena-module-title, .hatena-module-title a{ color: #b06c1c; margin-top: 20px; margin-bottom: 7px; } /* work frame*/ #container { width: 970px; text-align: center; margin: 0 auto; background: transparent; padding: 0 30px; } #wrapper { float: left; overflow: hidden; width: 660px; } #box2 { width: 240px; float: right; font-size: 14px; word-wrap: break-word; } /*#blog-title-inner{*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-position: left 0px;*/ /*}*/ /*.header-image-only #blog-title-inner {*/ /*background-repeat: no-repeat;*/ /*position: relative;*/ /*height: 200px;*/ /*display: none;*/ /*}*/ /*#blog-title {*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-image: url('https://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527172848.png');*/ /*background-repeat: no-repeat;*/ /*background-position: left 0px;*/ /*}*/