deluge/deluge/tests/test_security.py
DjLegolas b9a9e06c1d [WebUI][Daemon] Enhance TLS Security
This applies the following for both WebUI and Daemon:
1. Raised minimal TLS version to TLSv1.2
2. Added specific cipher suite list
3. Added support for ECDSA auth keys
4. Added support for ECDHE key exchange algorithm

We disabled the ability to perform TLS/SSL renegotiation and therefore
will prevent the clients from renegotiating, which can be exploit for
DoS attacks.

New security tests now will be skipped when running `pydef` and `trial`
testenvs. To run the test, use the testenv `security` or add the environment
variable `SECURITY_TESTS` before running the tests. Also should only run when
adding to the commit message the string `SECURITY_TEST`.
2018-09-06 19:14:13 +01:00

185 lines
5.6 KiB
Python

# -*- coding: utf-8 -*-
#
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
# the additional special exception to link portions of this program with the OpenSSL library.
# See LICENSE for more details.
#
from __future__ import print_function, unicode_literals
import os
import pytest
from twisted.internet.utils import getProcessOutputAndValue
import deluge.component as component
import deluge.ui.web.server
from deluge import configmanager
from deluge.common import windows_check
from deluge.ui.web.server import DelugeWeb
from .basetest import BaseTestCase
from .common import get_test_data_file
from .common_web import WebServerTestBase
from .daemon_base import DaemonBase
SECURITY_TESTS = bool(os.getenv('SECURITY_TESTS', False))
class SecurityBaseTestCase(object):
if windows_check():
skip = 'windows can`t run .sh files'
elif not SECURITY_TESTS:
skip = 'Skipping security tests'
http_err = 'can\'t run http tests on daemon'
def __init__(self):
self.home_dir = os.path.expanduser('~')
self.port = 8112
def _run_test(self, test):
d = getProcessOutputAndValue(
'bash',
[
get_test_data_file('testssl.sh'),
'--quiet',
'--nodns',
'--color',
'0',
test,
'127.0.0.1:%d' % self.port,
])
def on_result(results):
if test == '-e':
results = results[0].split('\n')[7:-6]
self.assertTrue(len(results) > 3)
else:
self.assertIn('OK', results[0])
self.assertNotIn('NOT ok', results[0])
d.addCallback(on_result)
return d
def test_secured_webserver_protocol(self):
return self._run_test('-p')
def test_secured_webserver_standard_ciphers(self):
return self._run_test('-s')
def test_secured_webserver_heartbleed_vulnerability(self):
return self._run_test('-H')
def test_secured_webserver_css_injection_vulnerability(self):
return self._run_test('-I')
def test_secured_webserver_ticketbleed_vulnerability(self):
return self._run_test('-T')
def test_secured_webserver_renegotiation_vulnerabilities(self):
return self._run_test('-R')
def test_secured_webserver_crime_vulnerability(self):
return self._run_test('-C')
def test_secured_webserver_breach_vulnerability(self):
return self._run_test('-B')
def test_secured_webserver_poodle_vulnerability(self):
return self._run_test('-O')
def test_secured_webserver_tls_fallback_scsv_mitigation_vulnerability(self):
return self._run_test('-Z')
def test_secured_webserver_sweet32_vulnerability(self):
return self._run_test('-W')
def test_secured_webserver_beast_vulnerability(self):
return self._run_test('-A')
def test_secured_webserver_lucky13_vulnerability(self):
return self._run_test('-L')
def test_secured_webserver_freak_vulnerability(self):
return self._run_test('-F')
def test_secured_webserver_logjam_vulnerability(self):
return self._run_test('-J')
def test_secured_webserver_drown_vulnerability(self):
return self._run_test('-D')
def test_secured_webserver_forward_secrecy_settings(self):
return self._run_test('-f')
def test_secured_webserver_rc4_ciphers(self):
return self._run_test('-4')
def test_secured_webserver_preference(self):
return self._run_test('-P')
def test_secured_webserver_headers(self):
return self._run_test('-h')
def test_secured_webserver_ciphers(self):
return self._run_test('-e')
@pytest.mark.security
class DaemonSecurityTestCase(BaseTestCase, DaemonBase, SecurityBaseTestCase):
if windows_check():
skip = 'windows can\'t start_core not enough arguments for format string'
def __init__(self, testname):
super(DaemonSecurityTestCase, self).__init__(testname)
DaemonBase.__init__(self)
SecurityBaseTestCase.__init__(self)
def setUp(self):
skip = False
for not_http_test in ('breach', 'headers', 'ticketbleed'):
if not_http_test in self.id().split('.')[-1]:
self.skipTest(SecurityBaseTestCase.http_err)
skip = True
if not skip:
super(DaemonSecurityTestCase, self).setUp()
def set_up(self):
d = self.common_set_up()
self.port = self.listen_port
d.addCallback(self.start_core)
d.addErrback(self.terminate_core)
return d
def tear_down(self):
d = component.shutdown()
d.addCallback(self.terminate_core)
return d
@pytest.mark.security
class WebUISecurityTestBase(WebServerTestBase, SecurityBaseTestCase):
def __init__(self, testname):
super(WebUISecurityTestBase, self).__init__(testname)
SecurityBaseTestCase.__init__(self)
def start_webapi(self, arg):
self.port = self.webserver_listen_port = 8999
config_defaults = deluge.ui.web.server.CONFIG_DEFAULTS.copy()
config_defaults['port'] = self.webserver_listen_port
config_defaults['https'] = True
self.config = configmanager.ConfigManager('web.conf', config_defaults)
self.deluge_web = DelugeWeb(daemon=False)
host = list(self.deluge_web.web_api.hostlist.config['hosts'][0])
host[2] = self.listen_port
self.deluge_web.web_api.hostlist.config['hosts'][0] = tuple(host)
self.host_id = host[0]
self.deluge_web.start()