From 9fefc052ca78bcf42cebb155474c6d774685702c Mon Sep 17 00:00:00 2001 From: Antony Chazapis Date: Tue, 29 Nov 2011 00:01:17 +0200 Subject: [PATCH] Restrict header count and size. URL-encode/decode specific headers that may contain unicode characters. Refs #1511 --- pithos/api/util.py | 39 +++++++++++++++++++++++++++++++++++- pithos/middleware/__init__.py | 1 - pithos/middleware/header.py | 43 ---------------------------------------- pithos/settings.d/00-apps.conf | 1 - 4 files changed, 38 insertions(+), 46 deletions(-) delete mode 100644 pithos/middleware/header.py diff --git a/pithos/api/util.py b/pithos/api/util.py index 1420714..223ffeb 100644 --- a/pithos/api/util.py +++ b/pithos/api/util.py @@ -37,6 +37,7 @@ from traceback import format_exc from wsgiref.handlers import format_date_time from binascii import hexlify, unhexlify from datetime import datetime, tzinfo, timedelta +from urllib import quote, unquote from django.conf import settings from django.http import HttpResponse @@ -758,6 +759,20 @@ def hashmap_hash(request, hashmap): h = [subhash(h[x] + h[x + 1]) for x in range(0, len(h), 2)] return hexlify(h[0]) +def update_request_headers(request): + # Handle URL-encoded keys and values. + meta = request.META + for k, v in meta.copy().iteritems(): + if ((k.startswith('HTTP_X_ACCOUNT_META_') or k.startswith('HTTP_X_ACCOUNT_GROUP_') or + k.startswith('HTTP_X_CONTAINER_META_') or k.startswith('HTTP_X_OBJECT_META_') or + k in ('HTTP_X_OBJECT_MANIFEST', 'HTTP_X_OBJECT_SHARING', + 'HTTP_X_COPY_FROM', 'HTTP_X_MOVE_FROM', + 'HTTP_X_SOURCE_ACCOUNT', 'HTTP_X_SOURCE_OBJECT', + 'HTTP_DESTINATION_ACCOUNT', 'HTTP_DESTINATION')) and + ('%' in k or '%' in v)): + del(meta[k]) + meta[unquote(k)] = unquote(v) + def update_response_headers(request, response): if request.serialization == 'xml': response['Content-Type'] = 'application/xml; charset=UTF-8' @@ -766,9 +781,20 @@ def update_response_headers(request, response): elif not response['Content-Type']: response['Content-Type'] = 'text/plain; charset=UTF-8' - if not response.has_header('Content-Length') and not (response.has_header('Content-Type') and response['Content-Type'].startswith('multipart/byteranges')): + if (not response.has_header('Content-Length') and + not (response.has_header('Content-Type') and + response['Content-Type'].startswith('multipart/byteranges'))): response['Content-Length'] = len(response.content) + # URL-encode unicode in headers. + meta = response.items() + for k, v in meta: + if (k.startswith('X-Account-Meta-') or k.startswith('X-Account-Group-') or + k.startswith('X-Container-Meta-') or k.startswith('X-Object-Meta-') or + k in ('X-Container-Object-Meta', 'X-Object-Manifest', 'X-Object-Sharing', 'X-Object-Shared-By')): + del(response[k]) + response[quote(k)] = quote(v) + if settings.TEST: response['Date'] = format_date_time(time()) @@ -824,6 +850,17 @@ def api_method(http_method=None, format_allowed=False, user_required=True): if len(args) > 2 and len(args[2]) > 1024: raise BadRequest('Object name too large.') + # Format and check headers. + update_request_headers(request) + meta = dict([(k, v) for k, v in request.META.iteritems() if k.startswith('HTTP_')]) + if len(meta) > 90: + raise BadRequest('Too many headers.') + for k, v in meta.iteritems(): + if len(k) > 128: + raise BadRequest('Header name too large.') + if len(v) > 256: + raise BadRequest('Header value too large.') + # Fill in custom request variables. request.serialization = request_serialization(request, format_allowed) request.backend = connect_backend() diff --git a/pithos/middleware/__init__.py b/pithos/middleware/__init__.py index 4c195f2..dcd24c5 100644 --- a/pithos/middleware/__init__.py +++ b/pithos/middleware/__init__.py @@ -1,3 +1,2 @@ from log import LoggingConfigMiddleware -from header import URLEncodedHeadersMiddleware from auth import AuthMiddleware diff --git a/pithos/middleware/header.py b/pithos/middleware/header.py deleted file mode 100644 index 2da7ba1..0000000 --- a/pithos/middleware/header.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright 2011 GRNET S.A. All rights reserved. -# -# Redistribution and use in source and binary forms, with or -# without modification, are permitted provided that the following -# conditions are met: -# -# 1. Redistributions of source code must retain the above -# copyright notice, this list of conditions and the following -# disclaimer. -# -# 2. Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following -# disclaimer in the documentation and/or other materials -# provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS -# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED -# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and -# documentation are those of the authors and should not be -# interpreted as representing official policies, either expressed -# or implied, of GRNET S.A. - -from urllib import unquote - - -class URLEncodedHeadersMiddleware(object): - def process_request(self, request): - meta = request.META - for k, v in meta.copy().iteritems(): - if k.startswith('HTTP_') and ('%' in k or '%' in v): - del(meta[k]) - meta[unquote(k)] = unquote(v) diff --git a/pithos/settings.d/00-apps.conf b/pithos/settings.d/00-apps.conf index e2be3ac..453547b 100644 --- a/pithos/settings.d/00-apps.conf +++ b/pithos/settings.d/00-apps.conf @@ -6,7 +6,6 @@ TEMPLATE_LOADERS = ( MIDDLEWARE_CLASSES = ( 'django.middleware.common.CommonMiddleware', - 'pithos.middleware.URLEncodedHeadersMiddleware', 'pithos.middleware.LoggingConfigMiddleware', 'pithos.middleware.AuthMiddleware' ) -- 1.7.10.4