Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove authentication headers from webhook payloads #4983

Merged
merged 5 commits into from
Sep 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ Changed
* Renamed reference to the RBAC backend/plugin from ``enterprise`` to ``default``. Updated st2api
validation to use the new value when checking RBAC configuration. Removed other references to
enterprise for RBAC related contents. (improvement)
* Remove authentication headers ``St2-Api-Key``, ``X-Auth-Token`` and ``Cookie`` from webhook payloads to
prevent them from being stored in the database. (security bug fix) #4983

Contributed by @potato and @knagy

Fixed
~~~~~
Expand Down
6 changes: 6 additions & 0 deletions st2api/st2api/controllers/v1/webhooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
urljoin = urlparse.urljoin

from st2common import log as logging
from st2common.constants.auth import HEADER_API_KEY_ATTRIBUTE_NAME, HEADER_ATTRIBUTE_NAME
from st2common.constants.triggers import WEBHOOK_TRIGGER_TYPES
from st2common.models.api.trace import TraceContext
from st2common.models.api.trigger import TriggerAPI
Expand Down Expand Up @@ -126,6 +127,7 @@ def post(self, hook, webhook_body_api, headers, requester_user):
permission_type=permission_type)

headers = self._get_headers_as_dict(headers)
headers = self._filter_authentication_headers(headers)

# If webhook contains a trace-tag use that else create create a unique trace-tag.
trace_context = self._create_trace_context(trace_tag=headers.pop(TRACE_TAG_HEADER, None),
Expand Down Expand Up @@ -219,6 +221,10 @@ def _get_headers_as_dict(self, headers):
headers_dict[key] = value
return headers_dict

def _filter_authentication_headers(self, headers):
auth_headers = [HEADER_API_KEY_ATTRIBUTE_NAME, HEADER_ATTRIBUTE_NAME, 'Cookie']
arm4b marked this conversation as resolved.
Show resolved Hide resolved
return {key: value for key, value in headers.items() if key not in auth_headers}

def _log_request(self, msg, headers, body, log_method=LOG.debug):
headers = self._get_headers_as_dict(headers)
body = str(body)
Expand Down
20 changes: 20 additions & 0 deletions st2api/tests/unit/controllers/v1/test_webhooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,26 @@ def get_webhook_trigger(name, url):
self.assertTrue(controller._is_valid_hook('with_leading_trailing_slash'))
self.assertTrue(controller._is_valid_hook('with/mixed/slash'))

@mock.patch.object(TriggerInstancePublisher, 'publish_trigger', mock.MagicMock(
return_value=True))
@mock.patch.object(WebhooksController, '_is_valid_hook', mock.MagicMock(
return_value=True))
@mock.patch.object(HooksHolder, 'get_triggers_for_hook', mock.MagicMock(
return_value=[DUMMY_TRIGGER_DICT]))
@mock.patch('st2common.transport.reactor.TriggerDispatcher.dispatch')
def test_authentication_headers_should_be_removed(self, dispatch_mock):
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'St2-Api-Key': 'foobar',
'X-Auth-Token': 'deadbeaf',
'Cookie': 'foo=bar'
}

self.app.post('/v1/webhooks/git', WEBHOOK_1, headers=headers)
self.assertNotIn('St2-Api-Key', dispatch_mock.call_args[1]['payload']['headers'])
self.assertNotIn('X-Auth-Token', dispatch_mock.call_args[1]['payload']['headers'])
self.assertNotIn('Cookie', dispatch_mock.call_args[1]['payload']['headers'])

def __do_post(self, hook, webhook, expect_errors=False, headers=None):
return self.app.post_json('/v1/webhooks/' + hook,
params=webhook,
Expand Down