REXMLのDoS脆弱性に関して

結構致命的な脆弱性なんだけど、そんなに騒がれていないのは何故だろう。まぁ、おかげで攻撃を受ける前に全環境にパッチを当てることができた。商用環境を抱えるとこの辺の動きがどうしても鈍くなる。


まずはRuby公式の発表から。
REXMLのDoS脆弱性
DoS vulnerability in REXML

ユーザから与えられたXMLを解析するようなアプリケーションをサービス不能(DoS)状態にすることができます。大部分のRailsアプリケーションはこの攻撃に対して脆弱です。

とある。わざとぼかしてあるのかもしれないが、これでは不十分。管理者が明示的にRailsデフォルトのある機能を無効にしていない限り、全てのRailsアプリケーションはこの攻撃に対して脆弱となっている。これはRails-1.1.6・1.2.6・2.1.0の各バージョンで確認できた。


世の中の九分九厘のRailsアプリケーションはパッチを当てる必要があるはずだ。もちろん、Rubricksもその例外ではないので利用されている方は最新版に更新するか自前でモンキーパッチを適用して頂きたい。


既にこの脆弱性に関して警鐘を鳴らしている方達
Railsの脆弱性: XML実体爆発攻撃
Rails が即死する REXML の DoS 脆弱性について
「REXMLのDoS脆弱性」が恐ろしい件

AJAXリクエストでもCSRF対策のトークンを自動送信する方法


前回の続きで、「request_forgery_protection」の問題点について。

  1. GETリクエストをスルーしているため根本的にCSRF対策になっていない
  2. 宣言したアクションだけにしか適用されないので開発者の宣言漏れの可能性がつきまとう
  3. FORMタグを使う部分は問題ないが、JavaScriptAJAXリクエストを発行する時は自前でトークンを付け加えてあげないといけない

2に関しては、所謂ブラックリスト方式かホワイトリスト方式かといった問題。
FWとして「CSRF対策済」と謳う以上はホワイトリスト形式にすべきなのではないかと思う。

3は結構ネット上でも対応方法を記載されている方がちらほら見受けられる。
具体的にどのような場面で問題になるかというと、

  • JavaScriptで直接 new Ajax.Updater() や new Ajax.Requext()を実行
  • InPlaceEditorを利用
  • AutoCompleteを利用


これらの問題に対してどのような対処がされているのだろうか。

  • parametersにrequest_forgery_protection_tokenを自力で混ぜ込む
  • リクエストメソッドをGETに変更する
  • 該当リクエストを受けるコントローラで「skip_before_filter :verify_authenticity_token」する

パラメータに自力でトークンを混ぜ込むのが正しい対処方法ではあるが、開発者にとってはめんどくさいと感じる人も多いんじゃないかな。というか俺はそんなのしたくない。

GETに変更する方法や、before_filterをスキップする方法はセキュリティレベルを下げるだけに過ぎない。その処理がCSRF攻撃されても影響が少ないような大したことのない機能であると認識して実施するのであればまぁ問題ないが、そのような記述無しにBlog等で公開するのは如何なもんかと思う。

では、どうするのが良いか。acculo.usのAJAX通信やprototypeのAjax.Updaterは全てAjax.Requestに集約されている。そして、Ajax.RequestはAjax.Baseを継承している。なので、Ajax.Baseのデフォルト値をごにょごにょしてあげれば全てのAjax通信のパラメータをいじることができる。具体的には以下の通り。

<%= javascript_include_tag 'prototype' %>
<script type="text/javascript">
  Object.extend(Ajax.Base.prototype, {
    initialize_original: Ajax.Base.prototype.initialize,
    initialize: function(options) {
      this.initialize_original(options);
      Object.extend(this.options.parameters, {'<%= request_forgery_protection_token %>':'<%= escape_javascript(form_authenticity_token) %>'});
    }
  });
</script>

これで、全てのAJAXリクエストのパラメータに自動的にトークンが付与されることになる。

Rails2のCSRF対策はレガシーなAPを保護しない

Rails-2.0がリリースされて半年が過ぎ、既にRails-2.1もリリースされているというのにRubricksはいまだにRails-1.2.6で動いている。というのも、Rubricksが使っているComponentsの仕組みそのものがRails2で消失してしまったため。

他の数多のPluginのように外出しされたとかなら兎も角、完全に無くなってしまったのは痛い。かといって、泣き言ばっかり言ってても仕方ないので最近重い腰をあげてRails-2.1のソースを読み込んでます。

Rails2の特徴の1つとして、「セキュリティの向上」が謳われている。その内容は主に以下の2点である。

  1. CSRF対策
  2. sanitizeの方式がホワイトリスト方式に変更

この2点、Rubricks Projectで提供しているrails_protection pluginと完璧に被るので、まずはこの周辺から読み込んでみる。

続きを読む

Railsのセッション管理にmemcachedを使う

▼ 参照サイト
HowtoChangeSessionStore
dreammindの日記
夜のDiscovery
くりまるwebつくる

==[RAILS_ROOT/config/environment.rb]====
...
SESSION_CACHE = MemCache.new(['192.168.0.1:11211'], {:namespace => 'rails'})
ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS.merge!({'cache' => SESSION_CACHE})
...

どの記事も恐らくはHowtoChangeSessionStoreを参考にされていると思われ、やり方はほぼ等しいようだが、なんで設定ファイルでコネクションをnewしないといけないのか、他に設定方法はないのかなぁと思う。

というわけでMemCacheの拡張プラグインを作ってみた。これを導入することで以下のようにmemcachedの設定をすることができる。

続きを読む

autoRuby.jsをDISってみる

http://ceo.sourcelab.jp/archives/97
ちょうどRubricksのユーザやグループの読み仮名の仕様を見直している際にこの記事がホッテントリにあったので興味を持った。着眼点はとても素晴らしい。ギミックはありがちと謙遜されているが、自分は今までWEBアプリでは見たことがなかった。しかし、導入も視野に入れてコードを読んでみたらこれが実にいただけない。


1. 編集候補の切換時、平仮名を含む候補が出る度にカナが追加されていく
2. 名前用、カナ用のテキストボックスを各1つしか指定できないため複数設置が不可能
3. 設定のためにJSファイルを編集しなければならない
4. ライセンスが記述されていないので開発者としては使いにくい
5. グローバル領域の関数・変数で構成されているので変数名衝突の可能性が高い
6. タイマー流しっぱなし(その割にsetIntervalじゃなくてsetTimeout)
7. 'name'と'ruby'というDOM-IDがハードコーディングされている (バグなのですぐ修正されると思う)

続きを読む

Rails2.0Preview登場

Rails2.0のPreviewが出たようです。
Rails本家のBlogに色々機能について書いてあったので読んでみた。下のようなことが書いてあった気がします。
たぶん間違いだらけなので、信用しないで元の文章読んでください。
http://feeds.feedburner.com/~r/RidingRails/~3/163455924/rails-2-0-0-preview-release

機能強化系

  • ActiveResourceの強化(namespaceの導入、rake routesで動的生成されるroutesの確認など)
  • respond_toの強化(show.html.erb , index.atom.builder , edit.iphone.hamlみたいな)
  • XSS対策の新方式(ブラックリストでなくホワイトリスト方式へ。TextHelper#sanitize)
  • アクション毎の細かな例外ハンドリング設定(rescue_action_in_publicの代わりにrescue_fromを宣言的に使用)
  • ActiveRecordシリアライズに加えてデシリアライズにも対応(Person.new.from_xml(“David“)みたいな。XMLJSONなどに対応)
  • プラグインの読み込み順序を指定できるようになる(config.plugins)
  • ActionMailerでテストメソッドの提供(assert_emails)。別のテンプレートエンジンが使えるようにもなる。
  • マイグレーション記法の洗練化(型毎にまとめられるように)
  • デバッガの強化(irbベースからruby-debugベースに。ステップバックや現在地表示が可能らしい。)
  • asset_hostの強化(ActionController::Base.asset_host = "assets%d.example.com"で%dが1〜4の数値に自動的に書き換わる)

新規機能系

  • HTTPのBASIC認証に対応
  • production時にJSを1ファイルに固めてくれる機能の追加(javascript_include_tag(:all, :cache => true)
  • CSRF対策の提供(AjaxRequestとFormにトークンを入れる)
  • HTTP only cookiesのサポート
  • Atom生成ライブラリの提供
  • Array#rand、Hash#exceptなどの細かいメソッドの追加

性能向上系

  • assetタグの性能向上、簡単なroutesのキャッシュ
  • Queryのキャッシュ(同一リクエスト内でのクエリキャッシュ)
  • fixtureの高速化(50-100%)

整理、分離系

  • インプレイスエディタ、オートコンプリートをプラグインにして外だし
  • 標準で付属していたacts_as_*をプラグイン
  • MySQLSQLitePostgreSQL以外(≒商用DB)のアダプタをgem化外だし(リリースサイクルも個別に)
  • ActionWebServiceの外だしgem化(かつあまり更新に乗り気でない雰囲気)
  • その他、pagination使うにも従来型がよければpluginを入れる必要がでたりとか

制限系

  • ActiveRecord::Base.with_scopeが非publicになる(玄人は.send(:with_scope)で)


(Shouta)

RubyのRssParserとGigazine

Rubyに標準添付されているRssParserでGigazineのフィードから更新日時が取得できない!
http://gigazine.net/index.php?/news/rss_2.0/

よくよくフィードを見てみた。


...
    
...
      モバイル
      2007-08-01T15:30:00+09:00
     
...

これは・・・間違っている・・・。Gigazineともあろうお方が・・・。

  • RSS0.9x/2.0とRSS1.0は一部タグ名が似ているものの完全に別もののフォーマット。
  • 要素から始まるのはRSS0.9x/2.0。
    • RSS0.9x/2.0系で日付を示すタグは
  • 要素が出てくるのはRSS1.0。


つまり、バージョンが混在しているんです。
Rubyとしては、要素を見た時点でRSS0.9x/2.0系と判断しているんだと思われます。で、を探しにいくが見つからない、はそもそも探しに行かない、と。
とりあえず、強引にdc:dateをpubDateに置換したらなんとかなりました。こんな対応でいいのかはよくわかりませんが。

require 'rexml/document'
require 'rss/1.0'
require 'rss/2.0'
require 'rss/dublincore'
require 'rss/syndication'
require 'rss/content'
require 'rss/trackback'
require 'rss/image'

require 'open-uri'
uri = URI.parse("http://gigazine.net/index.php?/news/rss_2.0/")

rss_source = uri.read
rss_source.gsub!('dc:date', 'pubDate') #強引に置換

rss = nil
begin
  rss = RSS::Parser.parse(rss_source, true, true)
rescue RSS::InvalidRSSError
  rss = RSS::Parser.parse(rss_source, false, true)
end

rss.items.each do |item|
  date = item.respond_to?(:dc_date) ? item.dc_date : item.pubDate
  p date
end

この間違い結構多いらしく、上の対応で読めるようになるサイトは結構ありました。
ここ→とか。http://www.rurubu.com/rss/rssdc.xml


あと別の話ですがRssParserはEUC-JPのフィードを読もうとすると落ちてしまいます。これは、フィードを解析するXMLパーサにREXMLを利用するように指定したらうまく動くようになりました。

begin
  rss = RSS::Parser.parse(rss_source, true, true, RSS::REXMLParser)
rescue RSS::InvalidRSSError
  rss = RSS::Parser.parse(rss_source, false, true, RSS::REXMLParser)
end

ところでRssParserの最新版はATOM対応してるんですね!
1年くらい前に作者の須藤さんとお話したことがあり、そのとき「ATOM対応しないんですか?」とか厚かましくリクエスト出したのが功を奏したんでしょうか??いずれにしてもありがたいことです。

http://www.cozmixng.org/~rwiki/?cmd=view;name=RSS+Parser


(追記)
RSS2.0でも拡張モジュールの使用はできるできないこともない(使えるとは書いていないが使えないとも書いていない)ようです。なので、バージョン混在ではないみたいです。また、最近のRssReaderライブラリでは対応していて普通にパースできるそうです。
ですので、本エントリは「Ruby1.8標準のRssParserで、RSS2.0+拡張モジュール形式のフィードを強引に読む方法」ということでお願いします。

(Shouta)