JWTをセッション管理に転用するのはあまり良いアイデアではない(認証だけならいいよ)

JWTをセッションに使うことに関して最近少し議論があったので、自分のお気持ちを表明したいと思います。

私は以前SPAを書く時にJWTをセッション管理に使おうとしたことがありましたが、仔細に検討していくとJWTをセッション管理に使うのは無意味にセキュリティ上のリスクを増やすだけで、伝統的なクッキーを使ったセッション管理を使った方が良いという結論に至りました。

前提を整理するためにあらかじめ前置きすると、「JWTをセッション管理に使う」というのは認証APIなどで返ってきたJWTをlocalstorageなどJavaScriptからアクセスできるストレージに保管しておいて、ajaxでサーバにリクエストを投げるたびにAuthenticationリクエストヘッダなどにJWTを付けて送る使い方を指します。

JWTをセッション管理に使うべきではないと思った理由の詳細は以下適当に箇条書きにします。

  • JWTをセッションに使うと、サーバー側でセッションストレージを持たなくても済むが、同じことはいわゆるクッキーセッション(クッキーにHMACで署名したデータを書き込んでセッションとする方法)でもできる
  • JWTをlocalstorageなどのJavaScriptからアクセスできるストレージに保管するのは、認証IDや署名されたトークンをhttpOnlyフラグを立てたクッキーに保管するよりもリスクが大きい
  • なぜならlocalstorageに認証トークンを保持すると、XSSがあった際に誰にも気づかれずに認証トークンを収集され続けたりセッションハイジャックされる恐れがある
  • XSSは無いに越したことはないが、だからといってXSSを経由したセッションハイジャック対策をしなくてもいい、ということにはならない
  • なぜなら、XSSは開発者のエスケープし忘れなどのポカミスによって引き起こされると認識されることがあるが、実際にはそれだけではなく、ブラウザやサーバー、言語処理系、ミドルウェアなどのバグによっても引き起こされる。最近の徳丸さんのブログに取り上げられた例では、PHPとApacheのバグを組み合わせて引き起こされるXSS(https://blog.tokumaru.org/2018/09/cve-2018-17082-cache-poisoning.html)が紹介されている。こういった事例を鑑みるに、XSSはアプリケーション開発者が気をつけてコードを書けば100%防げると断言することはできない
  • 一般的なウェブアプリケーションで、ユーザーが危険な処理をする際に、ログインしているユーザーに対して再度パスワード入力させるのは、セッションハイジャックされても致命的な被害を避けるためでもある。これと同様に、XSSがあったとしてもそれがセッションハイジャックにつながらないように対策をするのは変な話ではない。セキュリティ対策を0か1かで判断するのは明らかに間違っていると思う
  • クッキーセッションを用いると、JWTと同じようなステートレスなセッション管理ができる。クッキーにhttpOnlyフラグを付けることで、XSSがあっても認証トークンを収集されてセッションハイジャックされるというようなシナリオは防ぐことができる
  • よくよく考えるとセッションストレージを持たない完全にステートレスなセッション管理は、一般的なアプリケーション開発の要件上それほど嬉しくない。ユーザがパスワード変更する際にそのユーザのセッションを強制ログアウトさせるというよくあるユースケースを考えると、完全にステートレスなセッション管理では要件を満たすことができない。これはクッキーセッションでも同様
  • JWTでこの要件を満たすためには、JWTのjtiクレームにユニークなトークンを設定してサーバー側で失効させるjtiクレームのトークンを覚えておく必要があるだろう。結局これはサーバー側でセッションストレージのようなものを持たないといけないことになる
  • セキュリティ上のリスクが増える割には得られるものは不完全にステートレスなセッションで、しかも同じことはよりリスクを減らせるクッキーセッションでも可能である
  • こうやって見ていくと、JWTをセッション管理に転用するのは無意味にセキュリティ上のリスクを増やしているだけのようにしか見えない

普通のセッション、クッキーセッション、JWTによるセッションというこれらの選択肢を見てみると、セキュリティ上一番割に合わない選択肢がJWTを使ったセッション管理で、わざわざこれを選択するのはよっぽど何か特殊な要件があったりする場合かなあという感じになりました。こちらからは以上です。

最新のウェブフロントエンド技術を無理してキャッチアップする必要はない

ウェブフロントエンド技術は変化が激しいと言われるけれども、多くの人にとって最新のウェブフロントエンド技術を無理してキャッチアップする必要は無い。以下理由。

  • ここでいう最新のウェブフロントエンド技術とは、新しいブラウザのAPIや新しいJavaScriptの文法や新しいフレームワーク・ツールなどを指す
  • 今のHTML5はドキュメントを表現するプラットフォームだけではなくアプリケーションプラットフォームとしても機能するように進化をしている最中
  • だからアプリケーションプラットフォームとしての進化を支える新技術がたくさん出てきている
  • 逆に言うと、アプリケーション(SPAとか)を書かない人にとってはキャッチアップする必要の無い場合がたくさんある
  • また、それらのウェブフロントエンドの新技術を全てキャッチアップするのは基本的に不可能だと思う
  • 自分はウェブフロントエンドやそれのパフォーマンスを専門の一つにしているけれども、WebRTCやWebGLの詳しい話をしろと言われてもできない
  • 基本的にウェブサイトにちょろっとJavaScriptを書いてるような人は新しい技術やフレームワークを沢山キャッチアップする必要はあまりない
  • AngularやReactや最近のフレームワークやツールなど、SPA開発を念頭に置いていることが多い
  • SPA書かない人がSPA書くための技術をキャッチアップしてもしょうがない
  • JavaScriptの新しいAPIや文法は互換性を念頭において設計されている
  • 古いと言われるコードや書き方であっても仕様として非推奨になったわけではないからそのまま利用できる
  • ウェブブラウザの開発者は昔ながらの書き方をしても壊れないように互換性を大事にしているので、それに乗っかっても罪悪感を感じることはないと思う
  • JavaScriptが使われるユースケースは五年前十年前に比べると格段に増えた
  • 昔はウェブサイトにちょっとしたインタラクションを実装するのに使われていただけだった(例えばカーソルに追従する星を付けたり)
  • 今は違っていて、 ウェブ上で動くGUIみたいなリッチなSPAも、Electronで動くクロスプラットフォームなデスクトップアプリも、Cordovaで動くHTML5モバイルアプリも、React Nativeで動くモバイルアプリも、Node.jsで動くサーバアプリケーションも、Node.jsで動くコマンドラインアプリケーションも全てJavaScriptで書ける
  • これらの新しいユースケースに対応するために新しいツールやフレームワークも当然出てくるし、必要とされる知識も変わってくる
  • ウェブサイトにちょっとしたインタラクションを付け加えるためにJavaScriptを書くというユースケースは、それがJavaScriptの唯一のユースケースだった頃に比べるとそれほど大きなユースケースではない
  • 専門でJavaScriptを書いているような人はある程度キャッチアップする必要は当然ある
  • しかしそうではない人が無理して新技術をキャッチアップする必要は無い。なぜなら想定されているユースケースがあまり被っていない場合が結構あるから
  • 片手間でJavaScript書いてる人がjQuery使って昔ながらのJavaScriptのコード書いてても、ウェブサイトにちょろっとインタラクションを付け加えるだけならそれなりに機能するので別にそれでいいと思う
  • 新技術に興味が持てなければ、自分の仕事にとって必要なものだけをキャッチアップすればよい
  • それがわからないのに闇雲に新技術をキャッチアップしなければいけないと思っている人は、自分を見失っている

こちらからは以上です。

ウェブパフォーマンスの最適化のためにできる事は何か、もしくは『Webフロントエンド ハイパフォーマンス チューニング』という本を書いたという話

Webフロントエンド ハイパフォーマンス チューニング

Webフロントエンド ハイパフォーマンス チューニング

技術評論社さんから『Webフロントエンド ハイパフォーマンス チューニング』を出版することになりました。題名通り、フロントエンド周りのウェブパフォーマンスについて書いた本です。

ウェブパフォーマンスというのは、昔はウェブページの初回の表示の速さのみを指すものでした。インタラクションを持たない静的なウェブページがほとんどであった頃には、一度ウェブページを表示し終わってからはパフォーマンス上の問題が発生することが比較的少なかったからです。ウェブパフォーマンスを改善するチューニングテクニックというのもこの初期のリソースの読み込みを改善するものが主でした。このようなテクニックは、『ハイパフォーマンスWebサイト』で語られている「ファイルをgzip圧縮して配信する」「DNSルックアップを減らす」「JavaScriptとCSSは外部ファイル化する」などが代表的なものです。

この状況は、複雑な振る舞いを持つウェブアプリケーションが登場する事で変わります。JavaScriptによる動的な振る舞いを持つウェブページでは、一度レンダリングが終わってからもユーザのアクションに応じてインタラクションを行う必要があります。その時に問題になるパフォーマンス上の問題とは、従来のリソースの読み込みに関する問題ではなく、JavaScriptやDOM操作の実行速度の問題であったり、CSSセレクタのマッチング速度の問題であったり、視覚的要素のレイアウトや描画の速度の問題であったりします。これらの新しく出てきたパフォーマンス上の問題は、これまで紹介されてきたリソースの読み込みの速度をチューニングするテクニックでは全く対応できません。例えば、アニメーションの描画をなめらかにするために初期表示のパフォーマンスの最適化を行っても意味が無いことは直感的にわかると思います。

こういった新しく登場したパフォーマンス上の問題を解決するには、ウェブパフォーマンスという言葉の意味を捉え直し、かつそれらの問題が起きる根本的な原理であるブラウザのレンダリングの仕組みを把握し、レンダリングの工程ごとに発生する異なる問題を解決するためのチューニングテクニックを正しく適用することが必要です。この本では、ウェブパフォーマンス上の解決すべき問題をGoogleが提唱しているパフォーマンスモデルであるRAILに添って説明しています。その上でブラウザのレンダリングの仕組みやウェブパフォーマンスの計測方法や様々なチューニングテクニックを紹介しています。この本では今現在のウェブパフォーマンスを扱う上で必要になる基礎知識やチューニングテクニックを体系的に解説しており、ここまでウェブパフォーマンスについて網羅した技術書は、日本語の書籍の中では現時点ではこの本だけです。

すでに書店などには並んでいるのでウェブパフォーマンスに関心のある方は一度手に取って読んでみて下さい。というわけで『Webフロントエンド ハイパフォーマンス チューニング』の宣伝エントリでした。

MastodonはP2Pではないという話、もしくはMastodonの脱中央集権の仕組みについて

Fukuoka.php vol22にてMastodonについて話してきました。

Mastodonは最近盛り上がってる分散型SNSですが、その仕組みに興味をもったので調べてみたのが今回の話になります。スライドの中で主に説明しているのは、Mastodonが実装している分散型SNSを実現するためのプロトコルOStatusについてです。

OStatusはAtomフィードを核とした仕様です。ユーザーのつぶやきをAtomフィードで表現し、さらにそれをコンタクト情報を形式化するPortable ContactsとSocial Network上の活動を形式化するActivity Streamsで拡張しています。フィードだけだと、リモートのサーバはポーリングする必要があるため、それを補うためにPubSubHubbubでAtomフィードの更新をほぼリアルタイムに受け取ることができるようにしています。また、FollowやFavやReplyやRetweetを相手先に通知するためにはSalmonを使っています。リモートフォローする際に入力する(ID)@(Mastodonのドメイン)という文字列からそのユーザーの情報を取得するためには、WebFingerを使っています。

日本国内ではP2Pと結び付けられて語られることのあるこのMastodonですが、こうやって見てみるとP2Pとはあまり関係がなく、フィード関係のプロトコルを使って分散型SNSを構築できる仕組みであることがわかると思います。

HTML5とか勉強会にて「大量の要素を高速に表示するバーチャルレンダリング入門」という題で話をした

第68回HTML5とか勉強会にて「大量の要素を高速に表示するバーチャルレンダリング入門」という題で話をしてきました。

ブラウザのレンダリングは、基本的にそのドキュメントに含まれるDOM要素の数が多ければ多いほどレンダリングが重たくなります。バーチャルレンダリングというのは、JavaScriptでDOM要素の数を調整して見えない部分の描画をうまくサボる仕組みです。

バーチャルレンダリング自体は結構いろんなところで利用されているにもかかわらずその仕組みやアルゴリズムの流れについて日本語での解説をあまり見かけなかったので、HTML5とか勉強会ではこれについて話すことにしました。

このテクニックは結構昔からあるテクニックで、知ってる人は知っているけれども知らない人は知らないままという感じになりがちなんですが、今回はHTML5とか勉強会という参加者が比較的大きな勉強会(180人ぐらい参加してる)で話せて良かったなと思いました。

JavaScriptのUIライブラリはどうあるべきかという話とOnsen UIのアーキテクチャ

Onsen UI Advent Calendar の12/9の記事です。

Onsen UIは、モバイルアプリのネイティブライクなUIをHTML + CSS + JavaScriptで簡単に構築することを目的としたUIライブラリです(UIフレームワークともたまに呼ばれます)。 ↓みたいなネイティブなモバイルアプリっぽい画面をサクッと作ることができます。

f:id:anatoo:20161209200818p:plain

私は数年前から開発メンバーとしてOnsen UIの設計開発を行っています。この記事では、Onsen UIに求められるUIライブラリとしての要件とそれを解決するためにどのようなアーキテクチャを取っているのかについて解説します。

特定のフレームワークに依存しない

jQuery UIやReactの上に乗っかっているUIライブラリなどのように特定のフレームワークの仕組みを使って実装されたUIライブラリというのはたくさんありますが、ある特定のフレームワークに依存することは避けるべきだという考えのもとにOnsen UIは開発されています。

ある特定のフレームワークに依存したUIライブラリを作ると、その他のフレームワークから使ったり素のJavaScriptから使うことが困難になります。例えばAngularJSに依存したUIコンポーネントは他のフレームワークから利用することは基本的にできなくなります。技術的にはできなくも無いでしょうが、それをわざわざやりたいと思う開発者はおそらくいないでしょう。

UIライブラリの提供者側にとっても特定のフレームワークに依存するのはリスクがあります。もしその依存しているフレームワークが使われなくなった場合に、UIライブラリを書き直すことになる可能性があります。

これは実際に起こったことですが、Onsen UIの1系はAngularJSのdirectiveとして実装されていたので、AngularJSと互換性の無いAngular2が登場した時にdirectiveとして実装されていたすべてのUIコンポーネントを書き直したことがありました。

フレームワーク非依存にするためにしていること

Onsen UIでは、特定のフレームワーク依存せず、かつどのフレームワークからでもある程度利用できるように次のような構成を取っています。

f:id:anatoo:20161211015934p:plain

CSS Components層は、Onsen UIが提供する最もレベルの低いコンポーネント層です。これは主にすべてのUIコンポーネントの見た目を提供します。CSS Componentsという名前のとおり、CSSファイルとして提供されます。

Web Components層では、Custom ElementsのAPIを使ってCSS ComponentsにJavaScriptで振る舞いを与えます。Custom Elementsを使っているので、素のJavaScriptからでも、フレームワークからでも同じように扱えるように設計されています。

Framework Bindings層では、各種フレームワーク用のバインディングを記述します。現在のところAngular1, Angular2, React.js,とVue2用のバインディングが記述されています。

CSS Components層

CSS Components層では各コンポーネントがCSSだけで完結するCSS Componentsとして実装されています。CSSだけで実装できるもの、すなわちコンポーネントの見た目はここで実装されています。

このCSS ComponentsはAdobe製の高速CSSフレームワークであるTopcoatをフォークして開発されたものです。CSSメタ言語としてStylus、CSSコンポーネントのドキュメントの記述にはtopdoc、設計規約としては高速なCSSセレクタを記述することができるBEM+MindBEMdingを採用しています。

開発者はCSS Components層が提供するCSSのみを使うこともできます。例えば、Onsen UIのリポジトリのCSSファイルを読み込んで、swtichコンポーネントのタグを記述するとiOSでよく見るSwitchのUIが表示されます。

<link href="https://unpkg.com/onsenui@2.0.4/css/onsen-css-components.css" rel="stylesheet" /> 
<label class="switch">
  <input type="checkbox" class="switch__input" checked>
  <div class="switch__toggle">
    <div class="switch__handle"></div>
  </div>
</label>

f:id:anatoo:20161209195916p:plain

リポジトリ的には次の場所に全て記述されています。

Web Components層

先ほどのCSS Components層の上に位置するのがWeb Components層です。CSSで記述された見た目に対してJavaScriptで振る舞いを追加します。

ここでは独自のHTML要素を定義することが出来るCustom ElementsのAPIを使ってCSSコンポーネントに振る舞いを追加するカスタム要素を定義しています。ドキュメントを見ると現在は約40程度のカスタム要素が定義されています。

例えば<ons-button>という要素があるのですがこれはCSSコンポーネントして実装したbuttonにCustom Elementsを被せたものです。

<button class="button">...</button> <!-- CSSコンポーネント -->
<ons-button>...</ons-button> <!-- ons-buttonカスタム要素 -->

Custom Elementsとして実装すると、そのDOM要素のプロパティやメソッドや属性などの振る舞いを定義することができます。これを使って素のJavaScriptやjQueryなどからでも扱える、かつAngular2やReact.jsやVue.jsなどのフレームワークからでも扱えるコンポーネントを定義することができます。

リポジトリ的には次の場所に全て記述されています。Custom Elements以外にも各種JavaScriptのAPIも提供しているのでこの部分をひっくるめてcoreとも呼ばれます。Onsen UIのonsenui.jsonsenui.cssはこのcoreから生成されます。

Framework Bindings層

この層では、各フレームワークごとのラッパーを定義しています。このラッパーはOnsen UIではバインディングと呼ばれています。現在対応しているフレームワークにはReact.js, AngularJS, Angular2, Vue.jsなどがあります。jQueryや素のJSから利用する場合には、このバインディングは利用しません。

開発者は、利用するフレームワークに合わせてこのバインディングも利用します。

なんでこのバインディングがあるかというと、Web Componentsを提供していたとしても、そのカスタム要素のプロパティやメソッドにアクセスできなければ意味がありません。各フレームワークごとにコンポーネントの操作をどのように行うかについても流儀が異なります。

例えば、React.jsではそもそもコンポーネントのメソッドを叩くといった操作はしないのが普通なので、Custom Elementsが持つメソッドに依る操作をReact.jsのコンポーネントのpropsやstateによって管理する必要があります。AngularJSやAnglar2の場合にはDOM要素が持つメソッドをDirectiveから叩けるようにする必要があります。

Framework Bindings層ではこのフレームワークごとに異なる流儀を吸収しながら、カスタム要素に対するインターフェイスを提供しています。

フレームワークによってどういうふうに書き方が変わるかというのは次の公式ブログの記事にも書かれています。

リポジトリ的には次の場所に全て記述されています。bindingsディレクトリ以下にフレームワークごとのnpmパッケージが提供されています。

まとめ

Onsen UIは様々なフレームワークに対応するために、Custom Elementsを使っています。その際のUIライブラリとしての大まかなアーキテクチャについてこの記事では説明しました。

「PHPに型推論を実装する〜入門編〜」という題でPHPカンファレンス福岡2016で話してきた

PHPカンファレンス福岡2016で型推論器ってどんな感じなのという話をしました。PHPカンファレンス福岡は去年も登壇したんですが今年は弊社もスポンサードしつつの登壇です。

参加者や運営スタッフの皆さんの対応含めて心地良い雰囲気だったので、ああ参加して良かったなと自然と思えるような素晴らしいイベントでした。参加者や運営のスタッフの皆さんお疲れ様でした!