[#44310] プログラムに対して意図したとおりの文字列を渡す方法 — "Information Kanasansoft" <kanasansoft@...>

kanasanです。

9 messages 2007/12/05

[#44332] クラス:相互参照系の作成方法について質問です — "Saburoh Sakai" <sabroh@...>

はじめまして、さかいと申します。

12 messages 2007/12/11

[#44366] Rake改善プロジェクト — "Hajime Hoshi" <hajimehoshi@...>

東京大学修士 1 年の星一と申します。

14 messages 2007/12/19

[ruby-list:44309] Re: multipartで送信した時のRails/cgi.rbの動作について

From: "Makoto Kuwata" <kwa@...>
Date: 2007-12-05 09:31:53 UTC
List: ruby-list #44309
桑田といいます。
DRbのほうは分からないので、tempfileのほうだけ。

On Dec 3, 2007 4:35 AM, KOSEKI Kengo <kengo@tt.rim.or.jp> wrote:
> 空のフィールドを大量に作ってmultipartで送信すると、
> テンポラリファイルが大量に作成されます。
> この現象は、Linuxでも確認しました。
>
> 1000個のフィールドで800個ほどのファイルが作成されていましたが、
> これは想定されている動作でしょうか?

cgi.rbの仕様でしょう(Railsもcgi.rbを使っていたと思います)。
CGI::QueryExtension#read_multipart()を見ると、multipartの場合、
<input type="file">だけでなく type="text" や type="radio" などの
パラメータもすべてTempfile(またはStringIO)オブジェクトに格納
しています。

動作自体は仕様通りだと思うのですが、そもそもこの仕様が正しいのかと
いう疑問はあります。
type="file" の場合は値がTempfileオブジェクトでいいと思いますが、
type="text" や type="radio" までもがTempfileオブジェクトになるのは
なんか間違ってるように思います。
Content-Disposition で filename が指定されていた場合のみTempfileに
して、それ以外はStringでいいように思います。


> 繰り返しpostするとDOS攻撃に利用できそうだと思ったんですが……。
> このくらいは平気なものでしょうか。

確かに、個数制限を設けたほうがいいと思います。
あと、1ファイルあたりのデータサイズの制限とかも。
今のcgi.rbだと、際限なく受け付けてしまいます。

試しにパッチを作成してみました。Ruby1.8.6のcgi.rbをもとにしています。
・データサイズがCGI::MAX_DATASIZE(=64*1024*1024)を超えると例外を発生
・パラメータ数がCGI::MAX_NUMPARAMS(=256)を超えると例外を発生
どの例外クラスを使うべきか分からなかったのでStandardErrorにしてます。
パッチはメールに添付しますので、興味があれば試してみてください。


--
makoto kuwata

Attachments (1)

cgi.rb.diff (1.85 KB, text/x-diff)
--- /usr/local/lib/ruby/1.8/cgi.rb	2007-03-13 02:55:03.000000000 +0900
+++ cgi.rb	2007-12-05 18:10:13.000000000 +0900
@@ -905,10 +905,18 @@
     end
 
     params
   end
 
+
+  # Maximum data size
+  MAX_DATASIZE  = 64 * 1024 * 1024
+
+  # Maximum number of request parameters
+  MAX_NUMPARAMS = 256
+
+
   # Mixin module. It provides the follow functionality groups:
   #
   # 1. Access to CGI environment variables as methods.  See 
   #    documentation to the CGI class for a list of these variables.
   #
@@ -982,13 +990,19 @@
         raise EOFError, "no content body"
       elsif boundary + EOL != status
         raise EOFError, "bad content body"
       end
 
+      max_datasize = MAX_DATASIZE
+      max_numparams = MAX_NUMPARAMS
+      n = 0
+
       loop do
+        raise StandardError.new("too many parameters.") if (n += 1) > max_numparams
+
         head = nil
-        if 10240 < content_length
+        if bufsize < content_length
           require "tempfile"
           body = Tempfile.new("CGI")
         else
           begin
             require "stringio"
@@ -1010,10 +1024,11 @@
             next
           end
 
           if head and ( (EOL + boundary + EOL).size < buf.size )
             body.print buf[0 ... (buf.size - (EOL + boundary + EOL).size)]
+            raise StandardError.new("data is too large.") if body.pos > max_datasize
             buf[0 ... (buf.size - (EOL + boundary + EOL).size)] = ""
           end
 
           c = if bufsize < content_length
                 stdinput.read(bufsize)
@@ -1033,10 +1048,11 @@
             content_length = -1
           end
          boundary_end = $2.dup
           ""
         end
+        raise StandardError.new("data is too large.") if body.pos > max_datasize
 
         body.rewind
 
         /Content-Disposition:.* filename=(?:"((?:\\.|[^\"])*)"|([^;]*))/ni.match(head)
 	filename = ($1 or $2 or "")

In This Thread