فهرست منبع

HUE-4372 [core] Backport Django Security middleware or add HTTP security headers

Prakash Ranade 9 سال پیش
والد
کامیت
772c542

+ 16 - 0
desktop/conf.dist/hue.ini

@@ -84,6 +84,22 @@
   # Execute this script to produce the SSL password. This will be used when `ssl_password` is not set.
   ## ssl_password_script=
 
+  # X-Content-Type-Options: nosniff This is a HTTP response header feature that helps prevent attacks based on MIME-type confusion.
+  ## secure_content_type_nosniff=true
+
+  # X-Xss-Protection: \"1; mode=block\" This is a HTTP response header feature to force XSS protection.
+  ## secure_browser_xss_filter=true
+
+  # X-Content-Type-Options: nosniff This is a HTTP response header feature that helps prevent attacks based on MIME-type confusion.
+  ## secure_content_security_policy="img-src 'self' www.google-analytics.com; connect-src 'self'; child-src 'none'; object-src 'none'"
+
+  # Strict-Transport-Security HTTP Strict Transport Security(HSTS) is a policy which is communicated by the server to the user agent via HTTP response header field name “Strict-Transport-Security”. HSTS policy specifies a period of time during which the user agent(browser) should only access the server in a secure fashion(https).
+  ## secure_ssl_redirect=False
+  ## secure_redirect_host=0.0.0.0
+  ## secure_redirect_exempt=[]
+  ## secure_hsts_seconds=31536000
+  ## secure_hsts_include_subdomains=true
+
   # List of allowed and disallowed ciphers in cipher list format.
   # See http://www.openssl.org/docs/apps/ciphers.html for more information on
   # cipher list format. This list is from

+ 16 - 0
desktop/conf/pseudo-distributed.ini.tmpl

@@ -88,6 +88,22 @@
   # Execute this script to produce the SSL password. This will be used when `ssl_password` is not set.
   ## ssl_password_script=
 
+  # X-Content-Type-Options: nosniff This is a HTTP response header feature that helps prevent attacks based on MIME-type confusion.
+  ## secure_content_type_nosniff=true
+
+  # X-Xss-Protection: \"1; mode=block\" This is a HTTP response header feature to force XSS protection.
+  ## secure_browser_xss_filter=true
+
+  # X-Content-Type-Options: nosniff This is a HTTP response header feature that helps prevent attacks based on MIME-type confusion.
+  ## secure_content_security_policy="img-src 'self' www.google-analytics.com; connect-src 'self'; child-src 'none'; object-src 'none'"
+
+  # Strict-Transport-Security HTTP Strict Transport Security(HSTS) is a policy which is communicated by the server to the user agent via HTTP response header field name “Strict-Transport-Security”. HSTS policy specifies a period of time during which the user agent(browser) should only access the server in a secure fashion(https).
+  ## secure_ssl_redirect=False
+  ## secure_redirect_host=0.0.0.0
+  ## secure_redirect_exempt=[]
+  ## secure_hsts_seconds=31536000
+  ## secure_hsts_include_subdomains=true
+
   # List of allowed and disallowed ciphers in cipher list format.
   # See http://www.openssl.org/docs/apps/ciphers.html for more information on
   # cipher list format. This list is from

+ 45 - 0
desktop/core/ext-py/Django-1.6.10/django/middleware/security.py

@@ -0,0 +1,45 @@
+# This code is picked from Django 1.8 truck from https://github.com/django/django/blob/master/django/middleware/security.py
+import re
+
+from django.conf import settings
+from django.http import HttpResponsePermanentRedirect
+
+
+class SecurityMiddleware(object):
+    def __init__(self, get_response=None):
+        self.sts_seconds = settings.SECURE_HSTS_SECONDS
+        self.sts_include_subdomains = settings.SECURE_HSTS_INCLUDE_SUBDOMAINS
+        self.content_type_nosniff = settings.SECURE_CONTENT_TYPE_NOSNIFF
+        self.xss_filter = settings.SECURE_BROWSER_XSS_FILTER
+        self.redirect = settings.SECURE_SSL_REDIRECT
+        self.redirect_host = settings.SECURE_SSL_HOST
+        self.redirect_exempt = [re.compile(r) for r in settings.SECURE_REDIRECT_EXEMPT]
+        self.get_response = get_response
+
+    def process_request(self, request):
+        path = request.path.lstrip("/")
+        if (self.redirect and not request.is_secure() and
+                not any(pattern.search(path)
+                        for pattern in self.redirect_exempt)):
+            host = self.redirect_host or request.get_host()
+            return HttpResponsePermanentRedirect(
+                "https://%s%s" % (host, request.get_full_path())
+            )
+
+    def process_response(self, request, response):
+        if (self.sts_seconds and request.is_secure() and
+                'strict-transport-security' not in response):
+            sts_header = "max-age=%s" % self.sts_seconds
+
+            if self.sts_include_subdomains:
+                sts_header = sts_header + "; includeSubDomains"
+
+            response["strict-transport-security"] = sts_header
+
+        if self.content_type_nosniff and 'x-content-type-options' not in response:
+            response["x-content-type-options"] = "nosniff"
+
+        if self.xss_filter and 'x-xss-protection' not in response:
+            response["x-xss-protection"] = "1; mode=block"
+
+        return response

+ 48 - 0
desktop/core/src/desktop/conf.py

@@ -162,6 +162,54 @@ SSL_VALIDATE = Config(
   type=coerce_bool,
   default=True)
 
+SECURE_HSTS_SECONDS = Config(
+  key="secure_hsts_seconds",
+  help=_('Strict-Transport-Security: max-age=31536000 This is a HTTP response header, Once a supported browser receives this header that browser will prevent any communications from being sent over HTTP to the specified domain and will instead send all communications over HTTPS.'),
+  type=int,
+  default=31536000)
+
+SECURE_HSTS_INCLUDE_SUBDOMAINS = Config(
+  key="secure_hsts_include_subdomains",
+  help=_('Strict-Transport-Security: This is a HTTP response header, Once a supported browser receives this header that browser will prevent any communications from being sent over HTTP to the specified domain and will instead send all communications over HTTPS.'),
+  type=coerce_bool,
+  default=True)
+
+SECURE_CONTENT_TYPE_NOSNIFF = Config(
+  key="secure_content_type_nosniff",
+  help=_('X-Content-Type-Options: nosniff This is a HTTP response header feature that helps prevent attacks based on MIME-type confusion.'),
+  type=coerce_bool,
+  default=True)
+
+SECURE_BROWSER_XSS_FILTER = Config(
+  key="secure_browser_xss_filter",
+  help=_('X-Xss-Protection: \"1; mode=block\" This is a HTTP response header feature to force XSS protection.'),
+  type=coerce_bool,
+  default=True)
+
+SECURE_CONTENT_SECURITY_POLICY = Config(
+  key="secure_content_security_policy",
+  help=_('X-Content-Type-Options: nosniff This is a HTTP response header feature that helps prevent attacks based on MIME-type confusion.'),
+  type=str,
+  default="img-src 'self' www.google-analytics.com; connect-src 'self'; child-src 'none'; object-src 'none'")
+
+SECURE_SSL_REDIRECT = Config(
+  key="secure_ssl_redirect",
+  help=_('If all non-SSL requests should be permanently redirected to SSL.'),
+  type=coerce_bool,
+  default=False)
+
+SECURE_SSL_HOST = Config(
+  key="secure_redirect_host",
+  help=_('If all non-SSL requests should be permanently redirected to this SSL host.'),
+  type=str,
+  default="0.0.0.0")
+
+SECURE_REDIRECT_EXEMPT = Config(
+  key="secure_redirect_exempt",
+  help=_('Comma separated list of strings representing the host/domain names that the Hue server can not serve https.'),
+  type=coerce_csv,
+  default=[])
+
 # Deprecated by AUTH_PASSWORD
 LDAP_PASSWORD = Config(
   key="ldap_password",

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

@@ -670,3 +670,17 @@ class MetricsMiddleware(object):
     self._response_timer.stop()
     metrics.active_requests.dec()
     return response
+
+
+class ContentSecurityPolicyMiddleware(object):
+  def __init__(self, get_response=None):
+    self.secure_content_security_policy = desktop.conf.SECURE_CONTENT_SECURITY_POLICY.get()
+    if not self.secure_content_security_policy:
+      LOG.info('Unloading ContentSecurityPolicyMiddleware')
+      raise exceptions.MiddlewareNotUsed
+
+  def process_response(self, request, response):
+    if self.secure_content_security_policy and not 'Content-Security-Policy' in response:
+      response["Content-Security-Policy"] = self.secure_content_security_policy
+
+    return response

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

@@ -148,7 +148,9 @@ MIDDLEWARE_CLASSES = [
     'django.middleware.locale.LocaleMiddleware',
     'babeldjango.middleware.LocaleMiddleware',
     'desktop.middleware.AjaxMiddleware',
+    'django.middleware.security.SecurityMiddleware',
     'django.middleware.clickjacking.XFrameOptionsMiddleware',
+    'desktop.middleware.ContentSecurityPolicyMiddleware',
     # Must be after Session, Auth, and Ajax. Before everything else.
     'desktop.middleware.LoginAndPermissionMiddleware',
     'django.contrib.messages.middleware.MessageMiddleware',
@@ -348,6 +350,14 @@ SESSION_EXPIRE_AT_BROWSER_CLOSE = desktop.conf.SESSION.EXPIRE_AT_BROWSER_CLOSE.g
 # HTTP only
 SESSION_COOKIE_HTTPONLY = desktop.conf.SESSION.HTTP_ONLY.get()
 
+SECURE_HSTS_SECONDS = desktop.conf.SECURE_HSTS_SECONDS.get()
+SECURE_HSTS_INCLUDE_SUBDOMAINS = desktop.conf.SECURE_HSTS_INCLUDE_SUBDOMAINS.get()
+SECURE_CONTENT_TYPE_NOSNIFF = desktop.conf.SECURE_CONTENT_TYPE_NOSNIFF.get()
+SECURE_BROWSER_XSS_FILTER = desktop.conf.SECURE_BROWSER_XSS_FILTER.get()
+SECURE_SSL_REDIRECT = desktop.conf.SECURE_SSL_REDIRECT.get()
+SECURE_SSL_HOST = desktop.conf.SECURE_SSL_HOST.get()
+SECURE_REDIRECT_EXEMPT = desktop.conf.SECURE_REDIRECT_EXEMPT.get()
+
 # django-nose test specifics
 TEST_RUNNER = 'desktop.lib.test_runners.HueTestRunner'
 # Turn off cache middleware