Browse Source

[core] Disallow TRACE and TRACK HTTP methods

Using middleware to disallow methods.
Abraham Elmahrek 12 năm trước cách đây
mục cha
commit
a1ca5ecf33

+ 8 - 1
desktop/core/src/desktop/conf.py

@@ -22,7 +22,7 @@ import stat
 from django.utils.translation import ugettext_lazy as _
 
 from desktop.lib.conf import Config, ConfigSection, UnspecifiedConfigSection
-from desktop.lib.conf import coerce_bool, validate_path
+from desktop.lib.conf import coerce_bool, coerce_csv, validate_path
 from desktop.lib.i18n import force_unicode
 from desktop.lib.paths import get_desktop_root
 
@@ -52,6 +52,13 @@ HTTP_PORT = Config(
   type=int,
   default=8888)
 
+HTTP_ALLOWED_METHODS = Config(
+  key="http_allowed_methods",
+  help=_("HTTP methods the server will be allowed to service."),
+  type=coerce_csv,
+  private=True,
+  default=['OPTIONS', 'GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'CONNECT'])
+
 SSL_CERTIFICATE = Config(
   key="ssl_certificate",
   help=_("Filename of SSL Certificate"),

+ 8 - 1
desktop/core/src/desktop/lib/conf.py

@@ -81,7 +81,7 @@ GLOBAL_CONFIG = None
 
 LOG = logging.getLogger(__name__)
 
-__all__ = ["UnspecifiedConfigSection", "ConfigSection", "Config", "load_confs", "coerce_bool"]
+__all__ = ["UnspecifiedConfigSection", "ConfigSection", "Config", "load_confs", "coerce_bool", "coerce_csv"]
 
 class BoundConfig(object):
   def __init__(self, config, bind_to, grab_key=_ANONYMOUS, prefix=''):
@@ -612,6 +612,13 @@ def coerce_bool(value):
     return True
   raise Exception("Could not coerce %r to boolean value" % (value,))
 
+def coerce_csv(value):
+  if isinstance(value, str):
+    return value.split(',')
+  elif isinstance(value, list):
+    return value
+  raise Exception("Could not coerce %r to csv array." % value)
+
 
 def validate_path(confvar, is_dir=None, fs=os.path, message='Path does not exist on the filesystem.'):
   """

+ 9 - 0
desktop/core/src/desktop/middleware.py

@@ -27,6 +27,7 @@ from django.contrib.auth import REDIRECT_FIELD_NAME, BACKEND_SESSION_KEY, authen
 from django.contrib.auth.middleware import RemoteUserMiddleware
 from django.core import exceptions, urlresolvers
 import django.db
+from django.http import HttpResponseNotAllowed
 from django.core.urlresolvers import resolve
 from django.http import HttpResponseRedirect, HttpResponse
 from django.utils.importlib import import_module
@@ -625,3 +626,11 @@ class HueRemoteUserMiddleware(RemoteUserMiddleware):
     if not 'RemoteUserDjangoBackend' in desktop.conf.AUTH.BACKEND.get():
       LOG.info('Unloading HueRemoteUserMiddleware')
       raise exceptions.MiddlewareNotUsed
+
+class EnsureSafeMethodMiddleware(object):
+  """
+  Middleware to white list configured HTTP request methods.
+  """
+  def process_request(self, request):
+    if request.method not in desktop.conf.HTTP_ALLOWED_METHODS.get():
+      return HttpResponseNotAllowed(desktop.conf.HTTP_ALLOWED_METHODS.get())

+ 20 - 0
desktop/core/src/desktop/middleware_test.py

@@ -17,6 +17,7 @@
 
 from desktop.lib.django_test_util import make_logged_in_client
 from desktop.lib.test_utils import add_permission
+import desktop.conf
 
 from nose.tools import assert_equal
 
@@ -65,3 +66,22 @@ def test_view_perms():
 
   response = c.get("/useradmin/users/edit/user") # Can access his profile page
   assert_equal(200, response.status_code, response.content)
+
+
+def test_ensure_safe_method_middleware():
+  try:
+    # Super user
+    c = make_logged_in_client()
+
+    # GET works
+    response = c.get("/useradmin/")
+    assert_equal(200, response.status_code)
+
+    # Disallow GET
+    done = desktop.conf.HTTP_ALLOWED_METHODS.set_for_testing([])
+
+    # GET should not work because allowed methods is empty.
+    response = c.get("/useradmin/")
+    assert_equal(405, response.status_code)
+  finally:
+    done()

+ 1 - 0
desktop/core/src/desktop/settings.py

@@ -97,6 +97,7 @@ TEMPLATE_LOADERS = (
 
 MIDDLEWARE_CLASSES = [
     # The order matters
+    'desktop.middleware.EnsureSafeMethodMiddleware',
     'desktop.middleware.DatabaseLoggingMiddleware',
     'django.middleware.common.CommonMiddleware',
     'desktop.middleware.SessionOverPostMiddleware',

+ 3 - 10
desktop/libs/libsaml/src/libsaml/conf.py

@@ -20,19 +20,12 @@ import os
 
 from django.utils.translation import ugettext_lazy as _t
 
-from desktop.lib.conf import Config, coerce_bool
+from desktop.lib.conf import Config, coerce_bool, coerce_csv
 
 
 BASEDIR = os.path.dirname(os.path.abspath(__file__))
 
 
-def csv(value):
-  if isinstance(value, str):
-    return value.split(',')
-  elif isinstance(value, list):
-    return value
-  return None
-
 def dict_list_map(value):
   if isinstance(value, str):
     d = {}
@@ -73,13 +66,13 @@ ALLOW_UNSOLICITED = Config(
 REQUIRED_ATTRIBUTES = Config(
   key="required_attributes",
   default=['uid'],
-  type=csv,
+  type=coerce_csv,
   help=_t("Required attributes to ask for from IdP."))
 
 OPTIONAL_ATTRIBUTES = Config(
   key="optional_attributes",
   default=[],
-  type=csv,
+  type=coerce_csv,
   help=_t("Optional attributes to ask for from IdP."))
 
 METADATA_FILE = Config(