Config option for insecure responses
oslo.log's "debug" option was co-opted to also indicate that the responses should include more information. A separate config option should be used instead so that deployers don't mistakenly expose themselves to security issues. The debug option still is used for what it does in oslo.log and how it works on all other projects -- if you're not using a log config file it sets the base logger to debug. SecurityImpact Change-Id: Icf8dd2f0b88abc89092d487bbcefb525960c4ec6 Closes-Bug: 1479523
This commit is contained in:
parent
40453931a9
commit
2afad4dc30
@ -110,6 +110,12 @@ FILE_OPTIONS = {
|
||||
'original request, even if it was removed by an SSL '
|
||||
'terminating proxy. Typical value is '
|
||||
'"HTTP_X_FORWARDED_PROTO".'),
|
||||
cfg.BoolOpt('insecure_debug', default=False,
|
||||
help='If set to true the server will return information '
|
||||
'in the response that may allow an unauthenticated '
|
||||
'or authenticated user to get more information than '
|
||||
'normal, such as why authentication failed. This may '
|
||||
'be useful for debugging but is insecure.'),
|
||||
],
|
||||
'identity': [
|
||||
cfg.StrOpt('default_domain_id', default='default',
|
||||
|
@ -161,13 +161,15 @@ class PKITokenExpected(Error):
|
||||
|
||||
|
||||
class SecurityError(Error):
|
||||
"""Avoids exposing details of security failures, unless in debug mode."""
|
||||
"""Avoids exposing details of security failures, unless in insecure_debug
|
||||
mode.
|
||||
"""
|
||||
|
||||
amendment = _('(Disable debug mode to suppress these details.)')
|
||||
amendment = _('(Disable insecure_debug mode to suppress these details.)')
|
||||
|
||||
def _build_message(self, message, **kwargs):
|
||||
"""Only returns detailed messages in debug mode."""
|
||||
if message and CONF.debug:
|
||||
"""Only returns detailed messages in insecure_debug mode."""
|
||||
if message and CONF.insecure_debug:
|
||||
if isinstance(message, six.string_types):
|
||||
# Only do replacement if message is string. The message is
|
||||
# sometimes a different exception or bytes, which would raise
|
||||
@ -381,7 +383,7 @@ class Conflict(Error):
|
||||
|
||||
|
||||
class UnexpectedError(SecurityError):
|
||||
"""Avoids exposing details of failures, unless in debug mode."""
|
||||
"""Avoids exposing details of failures, unless in insecure_debug mode."""
|
||||
|
||||
message_format = _("An unexpected error prevented the server "
|
||||
"from fulfilling your request.")
|
||||
|
@ -38,9 +38,9 @@ def configure(version=None, config_files=None,
|
||||
pre_setup_logging_fn()
|
||||
config.setup_logging()
|
||||
|
||||
if CONF.debug:
|
||||
if CONF.insecure_debug:
|
||||
LOG.warn(_LW(
|
||||
'debug is enabled so responses may include sensitive '
|
||||
'insecure_debug is enabled so responses may include sensitive '
|
||||
'information.'))
|
||||
|
||||
|
||||
|
@ -286,7 +286,7 @@ class AuthWithToken(AuthTest):
|
||||
|
||||
def test_auth_scoped_token_bad_project_with_debug(self):
|
||||
"""Authenticating with an invalid project fails."""
|
||||
# Bug 1379952 reports poor user feedback, even in debug mode,
|
||||
# Bug 1379952 reports poor user feedback, even in insecure_debug mode,
|
||||
# when the user accidentally passes a project name as an ID.
|
||||
# This test intentionally does exactly that.
|
||||
body_dict = _build_user_auth(
|
||||
@ -294,8 +294,8 @@ class AuthWithToken(AuthTest):
|
||||
password=self.user_foo['password'],
|
||||
tenant_id=self.tenant_bar['name'])
|
||||
|
||||
# with debug enabled, this produces a friendly exception.
|
||||
self.config_fixture.config(debug=True)
|
||||
# with insecure_debug enabled, this produces a friendly exception.
|
||||
self.config_fixture.config(debug=True, insecure_debug=True)
|
||||
e = self.assertRaises(
|
||||
exception.Unauthorized,
|
||||
self.controller.authenticate,
|
||||
@ -308,7 +308,7 @@ class AuthWithToken(AuthTest):
|
||||
|
||||
def test_auth_scoped_token_bad_project_without_debug(self):
|
||||
"""Authenticating with an invalid project fails."""
|
||||
# Bug 1379952 reports poor user feedback, even in debug mode,
|
||||
# Bug 1379952 reports poor user feedback, even in insecure_debug mode,
|
||||
# when the user accidentally passes a project name as an ID.
|
||||
# This test intentionally does exactly that.
|
||||
body_dict = _build_user_auth(
|
||||
@ -316,8 +316,8 @@ class AuthWithToken(AuthTest):
|
||||
password=self.user_foo['password'],
|
||||
tenant_id=self.tenant_bar['name'])
|
||||
|
||||
# with debug disabled, authentication failure details are suppressed.
|
||||
self.config_fixture.config(debug=False)
|
||||
# with insecure_debug disabled (the default), authentication failure
|
||||
# details are suppressed.
|
||||
e = self.assertRaises(
|
||||
exception.Unauthorized,
|
||||
self.controller.authenticate,
|
||||
|
@ -123,7 +123,7 @@ class UnexpectedExceptionTestCase(ExceptionTestCase):
|
||||
self.assertNotIn(self.exc_str, six.text_type(e))
|
||||
|
||||
def test_unexpected_error_debug(self):
|
||||
self.config_fixture.config(debug=True)
|
||||
self.config_fixture.config(debug=True, insecure_debug=True)
|
||||
e = exception.UnexpectedError(exception=self.exc_str)
|
||||
self.assertIn(self.exc_str, six.text_type(e))
|
||||
|
||||
@ -135,7 +135,7 @@ class UnexpectedExceptionTestCase(ExceptionTestCase):
|
||||
six.text_type(e))
|
||||
|
||||
def test_unexpected_error_subclass_debug(self):
|
||||
self.config_fixture.config(debug=True)
|
||||
self.config_fixture.config(debug=True, insecure_debug=True)
|
||||
subclass = self.SubClassExc
|
||||
|
||||
e = subclass(debug_info=self.exc_str)
|
||||
@ -151,14 +151,14 @@ class UnexpectedExceptionTestCase(ExceptionTestCase):
|
||||
six.text_type(e))
|
||||
|
||||
def test_unexpected_error_custom_message_debug(self):
|
||||
self.config_fixture.config(debug=True)
|
||||
self.config_fixture.config(debug=True, insecure_debug=True)
|
||||
e = exception.UnexpectedError(self.exc_str)
|
||||
self.assertEqual(
|
||||
'%s %s' % (self.exc_str, exception.SecurityError.amendment),
|
||||
six.text_type(e))
|
||||
|
||||
def test_unexpected_error_custom_message_exception_debug(self):
|
||||
self.config_fixture.config(debug=True)
|
||||
self.config_fixture.config(debug=True, insecure_debug=True)
|
||||
orig_e = exception.NotFound(target=uuid.uuid4().hex)
|
||||
e = exception.UnexpectedError(orig_e)
|
||||
self.assertEqual(
|
||||
@ -167,7 +167,7 @@ class UnexpectedExceptionTestCase(ExceptionTestCase):
|
||||
six.text_type(e))
|
||||
|
||||
def test_unexpected_error_custom_message_binary_debug(self):
|
||||
self.config_fixture.config(debug=True)
|
||||
self.config_fixture.config(debug=True, insecure_debug=True)
|
||||
binary_msg = b'something'
|
||||
e = exception.UnexpectedError(binary_msg)
|
||||
self.assertEqual(
|
||||
@ -192,7 +192,7 @@ class SecurityErrorTestCase(ExceptionTestCase):
|
||||
self.assertNotIn(risky_info, six.text_type(e))
|
||||
|
||||
def test_unauthorized_exposure_in_debug(self):
|
||||
self.config_fixture.config(debug=True)
|
||||
self.config_fixture.config(debug=True, insecure_debug=True)
|
||||
|
||||
risky_info = uuid.uuid4().hex
|
||||
e = exception.Unauthorized(message=risky_info)
|
||||
@ -208,7 +208,7 @@ class SecurityErrorTestCase(ExceptionTestCase):
|
||||
self.assertNotIn(risky_info, six.text_type(e))
|
||||
|
||||
def test_forbidden_exposure_in_debug(self):
|
||||
self.config_fixture.config(debug=True)
|
||||
self.config_fixture.config(debug=True, insecure_debug=True)
|
||||
|
||||
risky_info = uuid.uuid4().hex
|
||||
e = exception.Forbidden(message=risky_info)
|
||||
@ -232,7 +232,7 @@ class SecurityErrorTestCase(ExceptionTestCase):
|
||||
self.assertNotIn(exception.SecurityError.amendment, six.text_type(e))
|
||||
|
||||
def test_forbidden_action_exposure_in_debug(self):
|
||||
self.config_fixture.config(debug=True)
|
||||
self.config_fixture.config(debug=True, insecure_debug=True)
|
||||
|
||||
risky_info = uuid.uuid4().hex
|
||||
action = uuid.uuid4().hex
|
||||
|
@ -294,12 +294,13 @@ class MiddlewareTest(BaseWSGITest):
|
||||
self.assertEqual(exception.UnexpectedError.code, resp.status_int)
|
||||
return resp
|
||||
|
||||
# Exception data should not be in the message when debug is False
|
||||
self.config_fixture.config(debug=False)
|
||||
# Exception data should not be in the message when insecure_debug is
|
||||
# False
|
||||
self.config_fixture.config(debug=False, insecure_debug=False)
|
||||
self.assertNotIn(exception_str, do_request().body)
|
||||
|
||||
# Exception data should be in the message when debug is True
|
||||
self.config_fixture.config(debug=True)
|
||||
# Exception data should be in the message when insecure_debug is True
|
||||
self.config_fixture.config(debug=True, insecure_debug=True)
|
||||
self.assertIn(exception_str, do_request().body)
|
||||
|
||||
|
||||
|
@ -0,0 +1,7 @@
|
||||
---
|
||||
upgrade:
|
||||
- A new config option, ``insecure_debug``, is added to control whether debug
|
||||
information is returned to clients. This used to be controlled by the
|
||||
``debug`` option. If you'd like to return extra information to clients
|
||||
set the value to ``true``. This extra information may help an attacker.
|
||||
|
Loading…
Reference in New Issue
Block a user