社内で1年ぶりにXSSの話した
1年前
だいたい1年前、社内でXSSの認知を広めるべくXSSの話をわからないなりにしました。
1年経ったのでもう一回、社内で発表しました。
XSSの無い最高の夏を過ごそう
ほんと、タイトルの通り。 今回は座学より実践形式に重きを置いていて、いくつか例題を作って実際にみんなに触ってもらいました。
かなり時間かかったけど、解けなくて悔しがってる人が何人書いたようで攻撃サイドの気持ちになってもらうっていう意味ではとても良かったように感じました。
XSS Tour
CTFほどじゃないけどそれらしきエセサイトをいろんな問題(過去の解けなかったセキュキャン応募用紙の問題やGoogleのXSS Gameあたりを参考に)を参考に用意しました。
リポジトリも公開してるので興味のある方はどうぞ。
JSのArrayBufferがよくわからなかったのでほんの少しだけ調べた
前提
初心者なりの超浅い理解です。
(というのも、元々Web Audio APIに食わせるためだけに調べてたので…)
ArrayBufferとは
ひとことで言うと「JavaScriptでバイナリを扱う」ために生まれたという認識。
なので画像処理等に使われていることが多い印象です。
少なくとも僕はWeb Audio API以外で使ったことがない(´・ω・`)
ArrayBufferは以下のようにインスタンス生成することができます。
const buf = new ArrayBuffer(30);
普通にクラスみたいにnewします。引数には確保したいバイナリの長さを渡します。
この瞬間、何が起きるかというと指定した長さのメモリ空間がバイナリ保存用に確保されます。
なので当然、アホみたいにでかい数字を渡してインスタンス生成しようとすると「メモリ足りないぜよ!」となってエラーになります。
また、このArrayBufferにはもう1つの特徴があり、直接読み書きすることができません。
以下のドキュメントを見てもそれらしきAPIやプロパティが存在しないことがわかると思います。
ArrayBuffer - JavaScript | MDN
せっかくバイナリを保存するためのメモリ確保してもなんにもできないやーん、となってしまいます。
そこで出てくるのが型付き配列というものです。
型付き配列(Typed Array)
型付き配列とは、その名の通り型を持つ配列のことです。
通常のArrayとは違うものですが、通常のArrayのように使用することができます。
TypedArrayにはいくつかのサブクラスが存在し、実際にはそれらを使用します。
では実際、こいつが何の役に立つのかというとArrayBufferの確保するメモリ空間にアクセスするために使用します。
具体的には以下のような感じで使えます。
const buf = new ArrayBuffer(100); // メモリ空間の確保 const view = new Uint8Array(buf); // 1行めで確保されたメモリ空間へアクセスするための型付き配列
これでview変数を通じてbufで確保されているメモリ空間にアクセスすることが可能になります。
(Uint8Arrayはバイナリへのアクセス方式を8bit整数(符号なし)で行いたい時に使用する型付き配列のサブクラスの一種)
これで例えば以下の様なコードを書くとbufを書き換えているのが同義になります。
view[0] = 0x12; // bufの0番めに書き込んでいるのと同義
このためのTypedArrayだったんですね~~~
まとめ
- ArrayBufferはバイナリ保存用にメモリ空間を確保するためのもの
- 直接の読み書きは不可
- ArrayBufferに格納されるバイナリを読み書きするのにTypedArrayを利用する
- TypedArrayはだいたい配列と同じノリで扱えるよ!
参考
ファイルサイズをチェックするfslint作りました
なぜ作ったのか
会社でフロントサイドJSを書く時、Browserifyとnpmでいろいろモジュール入れてゴリゴリやってたのだがファイルサイズがでかくなってしまうことが多々あった。
ファイルが1個ならいいが、吐き出すファイルが1個ではなかったしみんなが修正する度にファイルサイズチェックするのも非常に面倒だったのでeslintみたいなノリでファイルサイズをチェックできる何かが欲しいなと思ったので作った。
fslint
lint系をパクってfslintとした。
やってくれるのは本当にシンプルで、指定したファイルが指定したサイズをオーバーしてるかどうかをチェックするだけ。
使用方法はこんな感じ。

本当にこれだけ!
チェックがこけるとexit(1)するのでCIに組み込んでファイルサイズがでかすぎたら弾く、なんてこともできます。
作ってみて
npmライブラリはこれまでもいくつか作ってたんですこのfslintは作るのに全然時間がかかりませんでした。
というのも欲しい機能はだいたいnpmに落ちていたのでそれらをガッチャンコして見やすくしたくらいしか実装してないです。
ES2015で書いて公開ってのも初めてやったのでよい勉強になりました。(こちらを参考にしました!)
まとめ
気軽にファイルサイズチェックしたい人はぜひ使ってね!
node.jsでprocess.env.HOGEを書き換えようとしてハマった
nodeのコード中で環境変数を書き換えたい
そう思って以下の様なことをしてみました。
process.env.LOCAL_ENV = undefined; /* ~略~ */ if (_.isUndefined(process.env.LOCAL_ENV)) { console.log('LOCAL_ENV is not specified'); }
しかし何回やってもprocess.env.LOCAL_ENVにundefinedが入ってることにならない…
process.env.HOGEにはstring型しか入らない
きちんとDocumentに書いてありました。
process Node.js v6.1.0 Manual & Documentation
process.envから生えるオブジェクトをundefinedにしたい場合は何を代入してもstring型になるのでdelete句を使う必要があります。
// 間違い ('undefined'という文字列がセットされるだけ) process.env.LOCAL_ENV = undefined; // こうするべき delete process.env.LOCAL_ENV;
テスト書いてるとき見事にハマりました。。。 よく考えればわかることだった。。。
HTMLのイベントハンドラ属性における文字実体参照、数字文字参照
文字参照とは
基礎的な話だけどきちんと調べたことなかったので適当に調べてみた。
文字参照とはHTML等のマークアップ文書において直接参照できない文字(例えば文章中に<を入れるとタグが崩れちゃったりする)を表現するために用いられる文字列です。
PHPだとhtmlspecialchars()を一度は使用すると思うのですが、その時に出力される文字達が文字参照です。
この文字参照には以下の二種類あります。
それぞれ説明します。
数値文字参照(文字参照)
数値文字参照は特定の文字を10進数、もしくは16進数によって指定する方法です。
例えば①は①を10進数の数値文字参照で表した文字列です。
文字実体参照(実体参照)
こちらは数値文字参照と違い、特定のキーワード文字列でHTML等に使用される文字集合の該当文字列を表現する方法です。
代表的なものだと<, >, &, "等があります。
以下のような文章を打ちたいときには文字実体参照を使うことで実現できます。
<html>
<head>
<title>sample</title>
</head>
<body>
<!-- これだとブラウザによってはタグが破壊される -->
<p><title>タグはページのタイトルをつけるのに用います</p>
<!-- こう書けば見た目上は<title>になる -->
<p><title>タグはページのタイトルをつけるのに用います</p>
</body>
</html>
知っとくと面白いイベントハンドラ属性における実体参照
そもそもこの話を調べることになったキッカケがHTML内にhtmlspecialchars()を使わずにサニタイズしたURLを出力したかったからです。
その際にセキュリティ最強マンの友人にいろいろ聞いた過程で出てきた話がイベントハンドラ属性における実体参照の話です。
そもそもイベントハンドラ属性とは、特定のHTMLタグにつけることのできる特定のイベント時の処理を書くための属性値です。 具体的には以下の様なものがイベントハンドラ属性値。
<button onclick="alert('ボタンがクリックされたよ!');">
<img src="x" onerror="alert('画像読み込みエラーだよ!');">
これはしばしばXSSの温床になる属性だったりするのですが、このイベントハンドラ内での実体参照の解釈がすこし特殊。
例えば以下の様なPHPのエスケープは有効です。
<p class="<?php echo htmlspecialchar($className, ENT_QUOTES); ?>">ほげ~</p>
このようにエスケープすれば通常は$classNameに"onclick="alert('xss');みたいな文字列を入れられても出力時点で実体参照に置き換えられるので問題はないです。
じゃあ以下のような時はどうなるでしょう。
<button onclick="alert('<?php echo htmlspecialchars($onClickMessage, ENT_QUOTES); ?>');">押せよオラオラ</button>
この際、$onClickMessageに');location.href='http://evil.com';alert('のような文字列を混ぜます。
すると出力されるのは以下のHTMLです。
<button onclick="alert('');location.href='http://evil.com';alert('');">押せよオラオラ</button>
数値文字参照に置き換えられて、一見問題がないように思えます。 しかしブラウザがイベントハンドラ属性の値を評価する際、文字参照を解釈した上でコードの実行を試みます。 つまり見た目上はエスケープされていても実際に攻撃が成功します。
<!-- 「ソースを読む」で見たらこうなってるのに -->
<button onclick="alert('');location.href='http://evil.com';alert('');">押せよオラオラ</button>
<!-- ブラウザの解釈はこう -->
<button onclick="alert('');location.href='http://evil.com';alert('');">押せよオラオラ</button>
コワイですね。
どうすればいいの
この記事に全て書いてあります。
1行でまとめるなら「全ての文字列をHTMLエスケープしよう」です。 XSS対策する上で一番の敵はブラウザの仕様やコードでなく、人です。 「危なそうなところはエスケープ」といった運用ではヒューマンエラーでうっかりエスケープ忘れ、なんてことが必ず起こります。 半年くらいなら起きないかもしれませんが、10年開発して0件なんてことはないですよね…? 文字列は必ずエスケープしましょう。 どうしてもエスケープできない場面はエスケープする実装方法に差し替えられないか検討し、それでもダメならセキュリティできる人、ないしはチームメンバーに一言相談するのがマストだと思います。