You are viewing a plain text version of this content. The canonical link for it is here.
Posted to mod_python-dev@quetz.apache.org by Steven Hazel <sa...@thalassocracy.org> on 2004/02/15 10:07:11 UTC
FieldStorage can't limit Content-Length
I'm using mod_python to build a web site that allows photo uploads, and
was surpised to find that the FieldStorage class doesn't provide a way
to limit the Content-Length of requests. That's a potential Denial of
Service vulnerability.
I'm attaching the wrapper module I wrote to fix the problem for my site
-- the __init__ method could be dropped into FieldStorage to fix the
problem in the mod_python distribution.
--
Steven Hazel
sah@thalassocracy.org
Re: FieldStorage can't limit Content-Length
Posted by Steven Hazel <sa...@thalassocracy.org>.
David Fraser wrote:
> Note that you can do this directly with an Apache Configuration
> Directive:
> http://httpd.apache.org/docs-2.0/mod/core.html#limitrequestbody
> LimitRequestBody limits the total size of input fields...
> In many ways this is more effective than the solution provided below
> as it is the standard Apache solution - unless for some reason this
> needs to be done within mod_python
It would be a lot more convenient for me to control this from within
mod_python, so that I can specify a reasonable limit for each web
service along with all of the web service's other configuration stuff.
I'll possibly even want to set different limits for different user accounts.
Is there a way to set Apache directives from Python code? I can't find
one...
--
Steven Hazel
sah@thalassocracy.org
Re: FieldStorage can't limit Content-Length
Posted by David Fraser <da...@sjsoft.com>.
Steven Hazel wrote:
> I'm using mod_python to build a web site that allows photo uploads,
> and was surpised to find that the FieldStorage class doesn't provide a
> way to limit the Content-Length of requests. That's a potential
> Denial of Service vulnerability.
>
> I'm attaching the wrapper module I wrote to fix the problem for my
> site -- the __init__ method could be dropped into FieldStorage to fix
> the problem in the mod_python distribution.
Note that you can do this directly with an Apache Configuration Directive:
http://httpd.apache.org/docs-2.0/mod/core.html#limitrequestbody
LimitRequestBody limits the total size of input fields...
In many ways this is more effective than the solution provided below as
it is the standard Apache solution - unless for some reason this needs
to be done within mod_python
David
>
>------------------------------------------------------------------------
>
>import StringIO
>from mod_python import apache
>from mod_python import util
>from mod_python.util import Field
>from mod_python.util import parse_header
>from mod_python.util import parse_qsl
>
>class FieldStorage(util.FieldStorage):
>
> def __init__(self, req, keep_blank_values=0, strict_parsing=0,
> max_content_length=-1):
>
> self.list = []
>
> # always process GET-style parameters
> if req.args:
> pairs = parse_qsl(req.args, keep_blank_values)
> for pair in pairs:
> file = StringIO.StringIO(pair[1])
> self.list.append(Field(pair[0], file, "text/plain", {},
> None, {}))
>
> if req.method == "POST":
>
> try:
> clen = int(req.headers_in["content-length"])
> except (KeyError, ValueError):
> # absent content-length is not acceptable
> raise apache.SERVER_RETURN, apache.HTTP_LENGTH_REQUIRED
>
> if ((max_content_length != -1) and
> (clen > max_content_length)):
> raise ValueError, ("content length exceeds maximum", clen)
>
>
> if not req.headers_in.has_key("content-type"):
> ctype = "application/x-www-form-urlencoded"
> else:
> ctype = req.headers_in["content-type"]
>
> if ctype == "application/x-www-form-urlencoded":
>
> pairs = parse_qsl(req.read(clen), keep_blank_values)
> for pair in pairs:
> file = StringIO.StringIO(pair[1])
> self.list.append(Field(pair[0], file, "text/plain",
> {}, None, {}))
>
> elif ctype[:10] == "multipart/":
>
> # figure out boundary
> try:
> i = ctype.lower().rindex("boundary=")
> boundary = ctype[i+9:]
> if len(boundary) >= 2 and boundary[0] == boundary[-1] == '"':
> boundary = boundary[1:-1]
> boundary = "--" + boundary
> except ValueError:
> raise apache.SERVER_RETURN, apache.HTTP_BAD_REQUEST
>
> #read until boundary
> line = req.readline()
> sline = line.strip()
> while line and sline != boundary:
> line = req.readline()
> sline = line.strip()
>
> while 1:
>
> ## parse headers
>
> ctype, type_options = "text/plain", {}
> disp, disp_options = None, {}
> headers = apache.make_table()
>
> line = req.readline()
> sline = line.strip()
> if not line or sline == (boundary + "--"):
> break
>
> while line and line not in ["\n", "\r\n"]:
> h, v = line.split(":", 1)
> headers.add(h, v)
> h = h.lower()
> if h == "content-disposition":
> disp, disp_options = parse_header(v)
> elif h == "content-type":
> ctype, type_options = parse_header(v)
> line = req.readline()
> sline = line.strip()
>
> if disp_options.has_key("name"):
> name = disp_options["name"]
> else:
> name = None
>
> # is this a file?
> if disp_options.has_key("filename"):
> file = self.make_file()
> else:
> file = StringIO.StringIO()
>
> # read it in
> self.read_to_boundary(req, boundary, file)
> file.seek(0)
>
> # make a Field
> field = Field(name, file, ctype, type_options,
> disp, disp_options)
> field.headers = headers
> if disp_options.has_key("filename"):
> field.filename = disp_options["filename"]
>
> self.list.append(field)
>
> else:
> # we don't understand this content-type
> raise apache.SERVER_RETURN, apache.HTTP_NOT_IMPLEMENTED
>
>