Shibuya.xss で話してきました
すごい面白かった。会いたかった人にも沢山会えたし。
適当な感じのスライドですが、そこそこ反応があって嬉しかった。
Sinatra での HAML::Template.options の設定に悩む
Sinatra で以下のように書いたとき
Haml::Template::options[:escape_html] = true
このようなエラーが出ることがある
uninitialized constant Haml::Template (NameError)
ものによって出たり出なかったりなのでどういうタイミングでどう書いてあったら期待通りに動くかわからなくて、ざっと Sinatra::Base の set 呼んでるところあたり見て、以下で大丈夫な気がしたので書いてみたところ、問題ないようだ。
set :haml, :escape_html => true
結局、なんで上手く動いたのか、なんでダメだったのが全くわかってないし、やっぱり俺は Ruby 全然使えてないなあと改めて思った。
まだ、ソロでフレームワーク使えるレベルじゃないのかな。
文化だったり文脈だったりがもっとわかれば、突破口が開けるんじゃないかなとおもったりもするけど、言語仕様自体、なんとなくで使えちゃってるし、ちゃんとしたところわかってないよなあとおもったりする
Facebook ではてなスター
Facebook ではてなスターを使いたかったんだけど、 SiteConfig 書いただけだと HatenaStar Everywhere が満足に動かなかったので、ちょこっと user.js 等を書いたりした。
いるもの
- Greasemonkey
- http://s.hatena.ne.jp/js/HatenaStarEverywhere.user.js
- http://userscripts.org/scripts/show/97266
あと Star つけたときに like も押したかったのでこういうのも書いてみた。
今のところ Greasemonkey でしか動かないけど、はてなで siteconfig.json 取得する際に Access-Control-Allow-Origin ヘッダちゃんと吐いてくれれば Google Chrome とかでも動くようになるんじゃないかな。
今年中にまた引っ越します。
Ciphers をサイト毎に管理する
SSH の平文回復できる脆弱性対応として、Ciphers ガチガチにしていたところ、仕事で使うサーバの一部の OpenSSH が古くて aes*-ctr だと通らなくて*1少し困った。
ちょっと試してみたところ Host 毎で Ciphers 設定できたので忘れないようにメモしておく
- .ssh/config
ServerAliveInterval 60 ServerAliveCountMax 60 Host oldssh hostname 192.0.2.1 User example IdentityFile ~/.ssh/oldssh.pem Ciphers aes128-cbc,3des-cbc,blowfish-cbc Host !oldssh Ciphers aes128-ctr,aes256-ctr,arcfour256,arcfour,aes128-cbc,aes256-cbc
安全な HTMLDocument の生成方法について
何が危ないのか
img.onerror や img.onload は src 属性の内容が評価された段階で実行されるので、外部ソースに対して HTMLDocument を構築する際などで、意図していないタイミングでスクリプトが実行されるケースがある。
具体的には、以下のような場合。
var source = '<img src="not_found.jpg" onerror="alert(1)">'; var range = document.createRange(); range.createContextualFragment(source); // onerror が実行される
var img = document.createElement('img'); img.setAttribute('onerror', 'alert(1)'); img.src = 'not_found.jpg'; // onerror が実行される
各ブラウザの状況
では、どのようにすれば安全かということを nanto_vi さんの http://nanto.asablo.jp/blog/2009/10/29/4660197 で紹介されている各々の手法をもとに調べてみた。(関数名等はそちらのものを使っています)
- ブラウザ先般
- document から Range を生成して createContextualFragment を使用する際に doc.adoptNode を呼ぶのを忘れると img.onerror, img.onload が動く
- document.createElement のとき img.onload, img.onerror が動く
- Firefox
- Range 生成時には document ではなく doc を使わないと createContextualFragment 実行時に img.onerror と img.onload が動くことがある
- document の時の DocumentFragment に対する adoptNode の挙動がよくわからない
- fragment 生成から adoptNode の間に重い処理(XMLHttpRequest を同期で走らせる等)を入れたらハンドラは実行されてしまう
- Greasemonkey スクリプトだと重い処理がなくてもハンドラが実行されるときがある(eval が関係ある?)
- document の時の DocumentFragment に対する adoptNode の挙動がよくわからない
- createHTMLDocument_XSLT で作成した Document から作った img の addEventListener で付加した img.onerror, img.onload が動く
- createHTMLDocument_cloneNode で作成した Document から作った img の addEventListener で付加した img.onerror が動く
- Range 生成時には document ではなく doc を使わないと createContextualFragment 実行時に img.onerror と img.onload が動くことがある
- Safari
- Chrome
- createHTMLDocument_createDocument_DTD で Range 生成に doc 使うと NOT_SUPPORTED_ERR
- Opera
- DocumentFragment は adoptNode しなかった時点で Unhandled exception 発生
- document.createRange を使って Range オブジェクトを生成しないとブラウザクラッシュ
- createDocument を名前空間指定をせずに呼び出したとき以外は doc.createElement で img.onload, img.onerror が動く
まとめ
現象をざっと列挙したみたけど、すごくわかりづらかったので、ちょっと乱暴だけど簡単な落としどころにまとめてみる。
- DocumentFragment 生成
var range = doc.createRange();
range.selectNodeContents(doc.documentElement);
var range = document.createRange(); range.selectNodeContents(document.documentElement);
-
- Firefox は createDocument, それ以外は createHTMLDocument 使う
- Element 生成
- img 生成は、onerror や onload の setAttribute や addEventListener をしない
- createDocument(null, 'html', null) で作った Document から生成したりしてもたぶん大丈夫だけど、個別に要素作るケースだと入力値検証ちゃんとすべきだと思うので
- img 生成は、onerror や onload の setAttribute や addEventListener をしない
- 検証に使った UA
- Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; ja-JP-mac; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3
- Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_2; ja-jp) AppleWebKit/531.22.7 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7
- Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_2; en-US) AppleWebKit/533.2 (KHTML, like Gecko) Chrome/5.0.342.9 Safari/533.2
- Opera/9.80 (Macintosh; Intel Mac OS X; U; ja) Presto/2.2.15 Version/10.10
- 検証に使ったソース等
- http://send.sh/sandbox/img_test.html
- ソース一式
- 現象がよくわからなくて適当にダラ書きしながら検証してたので、まったくテストケースの体裁を取ってないです。すいません。
雑感など
- createContextualFragment 実行時に img.onerror が動くことに気付いて、まだ現象の整理も理解も出来てない状態で、これは Mozilla のバグなんじゃないのと Buzilla に投げてみたら、やっぱりあっさり WONTFIX で close でした。
- https://bugzilla.mozilla.org/show_bug.cgi?id=557420
- 英語力と技術力が足りなくて言いたいことが全然伝えられなかったけど、やっぱり Mozilla の挙動は不自然だと思う
- Mozilla 勉強会のあとの飲み会でちょっと相談させて頂いたみなさん、投稿する際に英語ちょっとみてくれた @hajime, ありがとうございました。
- adoptNode とか createContextualFragment での DocumentFragment が構築終了するタイミングとかどうなってるのかとかよくわからない
- range 生成は doc 側に倒すべきなのか document 側に倒すべきなのかどっちなんだろう
- img が Node 構築のタイミングで src の中身を取りに行くのはブラウザの仕様上理解出来る。でも、document に挿入される前に onload/onerror が走るのはちょっと怖いことだと思う。実装の都合だと思うけど。
- 逆にそれを上手く使ってる JSDeferred などのライブラリは凄いと思った。
- エントリ書くのにすごい時間掛かったけど全然わかりやすく書けなかった…
apply/call での継承の話
この件について。
継承というかスコープがわかりやすいというのもメリットだと思うけど、カプセル化しやすいのも大きなメリットかなと思う。
function Foo(){} (function(){ var bar = 'bar'; this.bar = bar.toUpperCase(); function baz () { console.log('baz'); } this.baz = function() { baz(); console.log('BAZ'); } }).call(Foo.prototype); function Bar(){} Bar.prototype = new Foo(); (function() { this.hoge = 'hoge'; this.baz = function() { Foo.prototype.baz.call(this); baz(); // 実行時に ReferenceError console.log('Bar::baz'); } this.bar = bar; // 評価時に ReferenceError }).call(Bar.prototype);