mirror of
https://github.com/mozilla/cipherscan.git
synced 2024-11-22 14:23:41 +01:00
analyze.py refactoring to use online recommendations
This commit is contained in:
parent
18b0d1b952
commit
639bc45bf7
220
analyze.py
220
analyze.py
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
import sys, os, json, subprocess, logging, argparse, platform
|
import sys, os, json, subprocess, logging, argparse, platform, urllib2
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
@ -22,14 +22,14 @@ def str_compat(data):
|
|||||||
# if `must_match` is True, the exact values are expected, if not
|
# if `must_match` is True, the exact values are expected, if not
|
||||||
# larger pfs values than the targets are acceptable
|
# larger pfs values than the targets are acceptable
|
||||||
def has_good_pfs(pfs, target_dh, target_ecc, must_match=False):
|
def has_good_pfs(pfs, target_dh, target_ecc, must_match=False):
|
||||||
if 'ECDH,' in pfs:
|
if target_ecc and 'ECDH,' in pfs:
|
||||||
# split string, expected format is 'ECDH,P-256,256bits'
|
# split string, expected format is 'ECDH,P-256,256bits'
|
||||||
ecc = pfs.split(',')[2].split('b')[0]
|
ecc = pfs.split(',')[2].split('b')[0]
|
||||||
if int(ecc) < target_ecc:
|
if int(ecc) < target_ecc:
|
||||||
return False
|
return False
|
||||||
if must_match and int(ecc) != target_ecc:
|
if must_match and int(ecc) != target_ecc:
|
||||||
return False
|
return False
|
||||||
elif 'DH,' in pfs:
|
elif target_dh and 'DH,' in pfs:
|
||||||
dhparam = pfs.split(',')[1].split('b')[0]
|
dhparam = pfs.split(',')[1].split('b')[0]
|
||||||
if int(dhparam) < target_dh:
|
if int(dhparam) < target_dh:
|
||||||
return False
|
return False
|
||||||
@ -41,16 +41,17 @@ def has_good_pfs(pfs, target_dh, target_ecc, must_match=False):
|
|||||||
# and looks for reasons to think otherwise. it will return True if
|
# and looks for reasons to think otherwise. it will return True if
|
||||||
# it finds one of these reason
|
# it finds one of these reason
|
||||||
def is_fubar(results):
|
def is_fubar(results):
|
||||||
|
logging.debug('entering fubar evaluation')
|
||||||
lvl = 'fubar'
|
lvl = 'fubar'
|
||||||
fubar = False
|
fubar = False
|
||||||
has_ssl2 = False
|
has_ssl2 = False
|
||||||
has_wrong_pubkey = False
|
has_wrong_pubkey = False
|
||||||
has_md5_sig = False
|
has_bad_sig = False
|
||||||
has_untrust_cert = False
|
has_untrust_cert = False
|
||||||
has_wrong_pfs = False
|
has_wrong_pfs = False
|
||||||
fubar_ciphers = set(all_ciphers) - set(old_ciphers)
|
|
||||||
for conn in results['ciphersuite']:
|
for conn in results['ciphersuite']:
|
||||||
if conn['cipher'] in fubar_ciphers:
|
logging.debug('testing connection %s' % conn)
|
||||||
|
if conn['cipher'] not in (set(old["ciphersuites"]) | set(inter["ciphersuites"]) | set(modern["ciphersuites"])):
|
||||||
failures[lvl].append("remove cipher " + conn['cipher'])
|
failures[lvl].append("remove cipher " + conn['cipher'])
|
||||||
logging.debug(conn['cipher'] + ' is in the list of fubar ciphers')
|
logging.debug(conn['cipher'] + ' is in the list of fubar ciphers')
|
||||||
fubar = True
|
fubar = True
|
||||||
@ -67,18 +68,18 @@ def is_fubar(results):
|
|||||||
logging.debug(conn['pfs']+ ' is a fubar PFS parameters')
|
logging.debug(conn['pfs']+ ' is a fubar PFS parameters')
|
||||||
fubar = True
|
fubar = True
|
||||||
has_wrong_pfs = True
|
has_wrong_pfs = True
|
||||||
if 'md5WithRSAEncryption' in conn['sigalg']:
|
for sigalg in conn['sigalg']:
|
||||||
has_md5_sig = True
|
if sigalg not in (set(old["certificate_signatures"]) | set(inter["certificate_signatures"]) | set(modern["certificate_signatures"])):
|
||||||
logging.debug(conn['sigalg'][0] + ' is a fubar cert signature')
|
logging.debug(sigalg + ' is a fubar cert signature')
|
||||||
fubar = True
|
fubar = True
|
||||||
if conn['trusted'] == 'False':
|
if conn['trusted'] == 'False':
|
||||||
has_untrust_cert = True
|
has_untrust_cert = True
|
||||||
logging.debug('The certificate is not trusted, which is quite fubar')
|
logging.debug('The certificate is not trusted, which is quite fubar')
|
||||||
fubar = True
|
fubar = True
|
||||||
if has_ssl2:
|
if has_ssl2:
|
||||||
failures[lvl].append("disable SSLv2")
|
failures[lvl].append("disable SSLv2")
|
||||||
if has_md5_sig:
|
if has_bad_sig:
|
||||||
failures[lvl].append("don't use a cert with a MD5 signature")
|
failures[lvl].append("don't use a cert with a bad signature algorithm")
|
||||||
if has_wrong_pubkey:
|
if has_wrong_pubkey:
|
||||||
failures[lvl].append("don't use a public key smaller than 2048 bits")
|
failures[lvl].append("don't use a public key smaller than 2048 bits")
|
||||||
if has_untrust_cert:
|
if has_untrust_cert:
|
||||||
@ -91,86 +92,83 @@ def is_fubar(results):
|
|||||||
# the parameters of an old configuration are not found. Those parameters
|
# the parameters of an old configuration are not found. Those parameters
|
||||||
# are defined in https://wiki.mozilla.org/Security/Server_Side_TLS#Old_backward_compatibility
|
# are defined in https://wiki.mozilla.org/Security/Server_Side_TLS#Old_backward_compatibility
|
||||||
def is_old(results):
|
def is_old(results):
|
||||||
|
logging.debug('entering old evaluation')
|
||||||
lvl = 'old'
|
lvl = 'old'
|
||||||
old = True
|
isold = True
|
||||||
has_sslv3 = False
|
|
||||||
has_3des = False
|
has_3des = False
|
||||||
has_sha1 = True
|
has_sha1 = True
|
||||||
has_pfs = True
|
has_pfs = True
|
||||||
has_ocsp = True
|
has_ocsp = True
|
||||||
all_proto = []
|
all_proto = []
|
||||||
for conn in results['ciphersuite']:
|
for conn in results['ciphersuite']:
|
||||||
|
logging.debug('testing connection %s' % conn)
|
||||||
# flag unwanted ciphers
|
# flag unwanted ciphers
|
||||||
if conn['cipher'] not in old_ciphers:
|
if conn['cipher'] not in old["ciphersuites"]:
|
||||||
logging.debug(conn['cipher'] + ' is not in the list of old ciphers')
|
logging.debug(conn['cipher'] + ' is not in the list of old ciphers')
|
||||||
failures[lvl].append("remove cipher " + conn['cipher'])
|
failures[lvl].append("remove cipher " + conn['cipher'])
|
||||||
old = False
|
isold = False
|
||||||
# verify required 3des cipher is present
|
# verify required 3des cipher is present
|
||||||
if conn['cipher'] == 'DES-CBC3-SHA':
|
if conn['cipher'] == 'DES-CBC3-SHA':
|
||||||
has_3des = True
|
has_3des = True
|
||||||
# verify required ssl3 protocol is present
|
|
||||||
if 'SSLv3' in conn['protocols']:
|
|
||||||
has_sslv3 = True
|
|
||||||
for proto in conn['protocols']:
|
for proto in conn['protocols']:
|
||||||
if proto not in all_proto:
|
if proto not in all_proto:
|
||||||
all_proto.append(proto)
|
all_proto.append(proto)
|
||||||
# verify required sha1 signature is used
|
# verify required sha1 signature is used
|
||||||
if 'sha1WithRSAEncryption' not in conn['sigalg']:
|
if 'sha1WithRSAEncryption' not in conn['sigalg']:
|
||||||
logging.debug(conn['sigalg'][0] + ' is a not an old signature')
|
logging.debug(conn['sigalg'][0] + ' is a not an old signature')
|
||||||
old = False
|
|
||||||
has_sha1 = False
|
has_sha1 = False
|
||||||
# verify required pfs parameter is used
|
# verify required pfs parameter is used
|
||||||
if conn['pfs'] != 'None':
|
if conn['pfs'] != 'None':
|
||||||
if not has_good_pfs(conn['pfs'], 1024, 256, True):
|
if not has_good_pfs(conn['pfs'], old["dh_param_size"], old["ecdh_param_size"], True):
|
||||||
logging.debug(conn['pfs']+ ' is not a good PFS parameter for the old configuration')
|
logging.debug(conn['pfs']+ ' is not a good PFS parameter for the old configuration')
|
||||||
old = False
|
|
||||||
has_pfs = False
|
has_pfs = False
|
||||||
if conn['ocsp_stapling'] == 'False':
|
if conn['ocsp_stapling'] == 'False':
|
||||||
has_ocsp = False
|
has_ocsp = False
|
||||||
extra_proto = set(all_proto) - set(['SSLv3', 'TLSv1', 'TLSv1.1', 'TLSv1.2'])
|
extra_proto = set(all_proto) - set(old["tls_versions"])
|
||||||
for proto in extra_proto:
|
for proto in extra_proto:
|
||||||
logging.debug("found protocol not wanted in the old configuration:" + proto)
|
logging.debug("found protocol not wanted in the old configuration:" + proto)
|
||||||
failures[lvl].append('disable ' + proto)
|
failures[lvl].append('disable ' + proto)
|
||||||
modern = False
|
isold = False
|
||||||
missing_proto = set(['SSLv3', 'TLSv1', 'TLSv1.1', 'TLSv1.2']) - set(all_proto)
|
missing_proto = set(old["tls_versions"]) - set(all_proto)
|
||||||
for proto in missing_proto:
|
for proto in missing_proto:
|
||||||
logging.debug("missing protocol wanted in the old configuration:" + proto)
|
logging.debug("missing protocol wanted in the old configuration:" + proto)
|
||||||
failures[lvl].append('consider enabling ' + proto)
|
failures[lvl].append('enable ' + proto)
|
||||||
if not has_sslv3:
|
isold = False
|
||||||
logging.debug("SSLv3 is not supported and required by the old configuration")
|
|
||||||
old = False
|
|
||||||
if not has_3des:
|
if not has_3des:
|
||||||
logging.debug("DES-CBC3-SHA is not supported and required by the old configuration")
|
logging.debug("DES-CBC3-SHA is not supported and required by the old configuration")
|
||||||
failures[lvl].append("add cipher DES-CBC3-SHA")
|
failures[lvl].append("add cipher DES-CBC3-SHA")
|
||||||
old = False
|
isold = False
|
||||||
if not has_sha1:
|
if not has_sha1:
|
||||||
failures[lvl].append("use a certificate with sha1WithRSAEncryption signature")
|
failures[lvl].append("use a certificate with sha1WithRSAEncryption signature")
|
||||||
old = False
|
isold = False
|
||||||
if not has_pfs:
|
if not has_pfs:
|
||||||
failures[lvl].append("use DHE of 1024bits and ECC of 256bits")
|
failures[lvl].append("use DHE of {dhe}bits and ECC of {ecdhe}bits".format(
|
||||||
old = False
|
dhe=old["dh_param_size"], ecdhe=old["ecdh_param_size"]))
|
||||||
|
isold = False
|
||||||
if not has_ocsp:
|
if not has_ocsp:
|
||||||
failures[lvl].append("consider enabling OCSP Stapling")
|
failures[lvl].append("consider enabling OCSP Stapling")
|
||||||
if results['serverside'] != 'True':
|
if results['serverside'] != 'True':
|
||||||
failures[lvl].append("enforce server side ordering")
|
failures[lvl].append("enforce server side ordering")
|
||||||
return old
|
return isold
|
||||||
|
|
||||||
# is_intermediate is similar to is_old but for intermediate configuration from
|
# is_intermediate is similar to is_old but for intermediate configuration from
|
||||||
# https://wiki.mozilla.org/Security/Server_Side_TLS#Intermediate_compatibility_.28default.29
|
# https://wiki.mozilla.org/Security/Server_Side_TLS#Intermediate_compatibility_.28default.29
|
||||||
def is_intermediate(results):
|
def is_intermediate(results):
|
||||||
|
logging.debug('entering intermediate evaluation')
|
||||||
lvl = 'intermediate'
|
lvl = 'intermediate'
|
||||||
inter = True
|
isinter = True
|
||||||
has_tls1 = False
|
has_tls1 = False
|
||||||
has_aes = False
|
has_aes = False
|
||||||
has_pfs = True
|
has_pfs = True
|
||||||
has_sha256 = True
|
has_sigalg = True
|
||||||
has_ocsp = True
|
has_ocsp = True
|
||||||
all_proto = []
|
all_proto = []
|
||||||
for conn in results['ciphersuite']:
|
for conn in results['ciphersuite']:
|
||||||
if conn['cipher'] not in intermediate_ciphers:
|
logging.debug('testing connection %s' % conn)
|
||||||
|
if conn['cipher'] not in inter["ciphersuites"]:
|
||||||
logging.debug(conn['cipher'] + ' is not in the list of intermediate ciphers')
|
logging.debug(conn['cipher'] + ' is not in the list of intermediate ciphers')
|
||||||
failures[lvl].append("remove cipher " + conn['cipher'])
|
failures[lvl].append("remove cipher " + conn['cipher'])
|
||||||
inter = False
|
isinter = False
|
||||||
if conn['cipher'] == 'AES128-SHA':
|
if conn['cipher'] == 'AES128-SHA':
|
||||||
has_aes = True
|
has_aes = True
|
||||||
for proto in conn['protocols']:
|
for proto in conn['protocols']:
|
||||||
@ -178,89 +176,91 @@ def is_intermediate(results):
|
|||||||
all_proto.append(proto)
|
all_proto.append(proto)
|
||||||
if 'TLSv1' in conn['protocols']:
|
if 'TLSv1' in conn['protocols']:
|
||||||
has_tls1 = True
|
has_tls1 = True
|
||||||
if conn['sigalg'][0] not in ['sha256WithRSAEncryption', 'sha384WithRSAEncryption', 'sha512WithRSAEncryption']:
|
if conn['sigalg'][0] not in inter["certificate_signatures"]:
|
||||||
logging.debug(conn['sigalg'][0] + ' is a not an intermediate signature')
|
logging.debug(conn['sigalg'][0] + ' is a not an intermediate signature')
|
||||||
has_sha256 = False
|
has_sigalg = False
|
||||||
if conn['pfs'] != 'None':
|
if conn['pfs'] != 'None':
|
||||||
if not has_good_pfs(conn['pfs'], 2048, 256):
|
if not has_good_pfs(conn['pfs'], inter["dh_param_size"], inter["ecdh_param_size"], True):
|
||||||
logging.debug(conn['pfs']+ ' is not a good PFS parameter for the intermediate configuration')
|
logging.debug(conn['pfs']+ ' is not a good PFS parameter for the intermediate configuration')
|
||||||
has_pfs = False
|
has_pfs = False
|
||||||
if conn['ocsp_stapling'] == 'False':
|
if conn['ocsp_stapling'] == 'False':
|
||||||
has_ocsp = False
|
has_ocsp = False
|
||||||
extra_proto = set(all_proto) - set(['TLSv1', 'TLSv1.1', 'TLSv1.2'])
|
extra_proto = set(all_proto) - set(inter["tls_versions"])
|
||||||
for proto in extra_proto:
|
for proto in extra_proto:
|
||||||
logging.debug("found protocol not wanted in the intermediate configuration:" + proto)
|
logging.debug("found protocol not wanted in the intermediate configuration:" + proto)
|
||||||
failures[lvl].append('disable ' + proto)
|
failures[lvl].append('disable ' + proto)
|
||||||
inter = False
|
isinter = False
|
||||||
missing_proto = set(['TLSv1', 'TLSv1.1', 'TLSv1.2']) - set(all_proto)
|
missing_proto = set(inter["tls_versions"]) - set(all_proto)
|
||||||
for proto in missing_proto:
|
for proto in missing_proto:
|
||||||
logging.debug("missing protocol wanted in the intermediate configuration:" + proto)
|
logging.debug("missing protocol wanted in the intermediate configuration:" + proto)
|
||||||
failures[lvl].append('consider enabling ' + proto)
|
failures[lvl].append('consider enabling ' + proto)
|
||||||
if not has_tls1:
|
if not has_tls1:
|
||||||
logging.debug("TLSv1 is not supported and required by the old configuration")
|
logging.debug("TLSv1 is not supported and required by the old configuration")
|
||||||
inter = False
|
isinter = False
|
||||||
if not has_aes:
|
if not has_aes:
|
||||||
logging.debug("AES128-SHA is not supported and required by the intermediate configuration")
|
logging.debug("AES128-SHA is not supported and required by the intermediate configuration")
|
||||||
failures[lvl].append("add cipher AES128-SHA")
|
failures[lvl].append("add cipher AES128-SHA")
|
||||||
inter = False
|
isinter = False
|
||||||
if not has_sha256:
|
if not has_sigalg:
|
||||||
failures[lvl].append("consider using a SHA-256 certificate")
|
failures[lvl].append("use a certificate signed with %s" % " or ".join(inter["certificate_signatures"]))
|
||||||
|
isinter = False
|
||||||
if not has_pfs:
|
if not has_pfs:
|
||||||
failures[lvl].append("consider using DHE of at least 2048bits and ECC of at least 256bits")
|
failures[lvl].append("consider using DHE of at least 2048bits and ECC of at least 256bits")
|
||||||
if not has_ocsp:
|
if not has_ocsp:
|
||||||
failures[lvl].append("consider enabling OCSP Stapling")
|
failures[lvl].append("consider enabling OCSP Stapling")
|
||||||
if results['serverside'] != 'True':
|
if results['serverside'] != 'True':
|
||||||
failures[lvl].append("enforce server side ordering")
|
failures[lvl].append("enforce server side ordering")
|
||||||
return inter
|
return isinter
|
||||||
|
|
||||||
# is_modern is similar to is_old but for modern configuration from
|
# is_modern is similar to is_old but for modern configuration from
|
||||||
# https://wiki.mozilla.org/Security/Server_Side_TLS#Modern_compatibility
|
# https://wiki.mozilla.org/Security/Server_Side_TLS#Modern_compatibility
|
||||||
def is_modern(results):
|
def is_modern(results):
|
||||||
|
logging.debug('entering modern evaluation')
|
||||||
lvl = 'modern'
|
lvl = 'modern'
|
||||||
modern = True
|
ismodern = True
|
||||||
has_pfs = True
|
has_pfs = True
|
||||||
has_sha256 = True
|
has_sigalg = True
|
||||||
has_ocsp = True
|
has_ocsp = True
|
||||||
all_proto = []
|
all_proto = []
|
||||||
for conn in results['ciphersuite']:
|
for conn in results['ciphersuite']:
|
||||||
if conn['cipher'] not in modern_ciphers:
|
logging.debug('testing connection %s' % conn)
|
||||||
|
if conn['cipher'] not in modern["ciphersuites"]:
|
||||||
logging.debug(conn['cipher'] + ' is not in the list of modern ciphers')
|
logging.debug(conn['cipher'] + ' is not in the list of modern ciphers')
|
||||||
failures[lvl].append("remove cipher " + conn['cipher'])
|
failures[lvl].append("remove cipher " + conn['cipher'])
|
||||||
modern = False
|
ismodern = False
|
||||||
for proto in conn['protocols']:
|
for proto in conn['protocols']:
|
||||||
if proto not in all_proto:
|
if proto not in all_proto:
|
||||||
all_proto.append(proto)
|
all_proto.append(proto)
|
||||||
if conn['sigalg'][0] not in ['sha256WithRSAEncryption', 'sha384WithRSAEncryption', 'sha512WithRSAEncryption']:
|
if conn['sigalg'][0] not in modern["certificate_signatures"]:
|
||||||
logging.debug(conn['sigalg'][0] + ' is a not an modern signature')
|
logging.debug(conn['sigalg'][0] + ' is a not an modern signature')
|
||||||
modern = False
|
has_sigalg = False
|
||||||
has_sha256 = False
|
|
||||||
if conn['pfs'] != 'None':
|
if conn['pfs'] != 'None':
|
||||||
if not has_good_pfs(conn['pfs'], 2048, 256):
|
if not has_good_pfs(conn['pfs'], modern["dh_param_size"], modern["ecdh_param_size"], True):
|
||||||
logging.debug(conn['pfs']+ ' is not a good PFS parameter for the modern configuration')
|
logging.debug(conn['pfs']+ ' is not a good PFS parameter for the modern configuration')
|
||||||
modern = False
|
ismodern = False
|
||||||
has_pfs = False
|
has_pfs = False
|
||||||
if conn['ocsp_stapling'] == 'False':
|
if conn['ocsp_stapling'] == 'False':
|
||||||
has_ocsp = False
|
has_ocsp = False
|
||||||
extra_proto = set(all_proto) - set(['TLSv1.1', 'TLSv1.2'])
|
extra_proto = set(all_proto) - set(modern["tls_versions"])
|
||||||
for proto in extra_proto:
|
for proto in extra_proto:
|
||||||
logging.debug("found protocol not wanted in the modern configuration:" + proto)
|
logging.debug("found protocol not wanted in the modern configuration:" + proto)
|
||||||
failures[lvl].append('disable ' + proto)
|
failures[lvl].append('disable ' + proto)
|
||||||
modern = False
|
ismodern = False
|
||||||
missing_proto = set(['TLSv1.1', 'TLSv1.2']) - set(all_proto)
|
missing_proto = set(modern["tls_versions"]) - set(all_proto)
|
||||||
for proto in missing_proto:
|
for proto in missing_proto:
|
||||||
logging.debug("missing protocol wanted in the modern configuration:" + proto)
|
logging.debug("missing protocol wanted in the modern configuration:" + proto)
|
||||||
failures[lvl].append('consider enabling ' + proto)
|
failures[lvl].append('consider enabling ' + proto)
|
||||||
if not has_sha256:
|
if not has_sigalg:
|
||||||
failures[lvl].append("use a SHA-256 certificate")
|
failures[lvl].append("use a certificate signed with %s" % " or ".join(modern["certificate_signatures"]))
|
||||||
modern = False
|
ismodern = False
|
||||||
if not has_pfs:
|
if not has_pfs:
|
||||||
failures[lvl].append("use DHE of at least 2048bits and ECC of at least 256bits")
|
failures[lvl].append("use DHE of at least 2048bits and ECC of at least 256bits")
|
||||||
modern = False
|
ismodern = False
|
||||||
if not has_ocsp:
|
if not has_ocsp:
|
||||||
failures[lvl].append("consider enabling OCSP Stapling")
|
failures[lvl].append("consider enabling OCSP Stapling")
|
||||||
if results['serverside'] != 'True':
|
if results['serverside'] != 'True':
|
||||||
failures[lvl].append("enforce server side ordering")
|
failures[lvl].append("enforce server side ordering")
|
||||||
return modern
|
return ismodern
|
||||||
|
|
||||||
def is_ordered(results, ref_ciphersuite, lvl):
|
def is_ordered(results, ref_ciphersuite, lvl):
|
||||||
ordered = True
|
ordered = True
|
||||||
@ -293,17 +293,17 @@ def evaluate_all(results):
|
|||||||
|
|
||||||
if is_old(results):
|
if is_old(results):
|
||||||
status = "old"
|
status = "old"
|
||||||
if not is_ordered(results, old_ciphers, "old"):
|
if not is_ordered(results, old["ciphersuites"], "old"):
|
||||||
status = "old with bad ordering"
|
status = "old with bad ordering"
|
||||||
|
|
||||||
if is_intermediate(results):
|
if is_intermediate(results):
|
||||||
status = "intermediate"
|
status = "intermediate"
|
||||||
if not is_ordered(results, intermediate_ciphers, "intermediate"):
|
if not is_ordered(results, inter["ciphersuites"], "intermediate"):
|
||||||
status = "intermediate with bad ordering"
|
status = "intermediate with bad ordering"
|
||||||
|
|
||||||
if is_modern(results):
|
if is_modern(results):
|
||||||
status = "modern"
|
status = "modern"
|
||||||
if not is_ordered(results, modern_ciphers, "modern"):
|
if not is_ordered(results, modern["ciphersuites"], "modern"):
|
||||||
status = "modern with bad ordering"
|
status = "modern with bad ordering"
|
||||||
|
|
||||||
if is_fubar(results):
|
if is_fubar(results):
|
||||||
@ -312,6 +312,7 @@ def evaluate_all(results):
|
|||||||
return status
|
return status
|
||||||
|
|
||||||
def process_results(data, level=None, do_json=False, do_nagios=False):
|
def process_results(data, level=None, do_json=False, do_nagios=False):
|
||||||
|
logging.debug('processing results on %s' % data)
|
||||||
exit_status = 0
|
exit_status = 0
|
||||||
results = dict()
|
results = dict()
|
||||||
# initialize the failures struct
|
# initialize the failures struct
|
||||||
@ -383,77 +384,28 @@ def process_results(data, level=None, do_json=False, do_nagios=False):
|
|||||||
exit_status = 1
|
exit_status = 1
|
||||||
return exit_status
|
return exit_status
|
||||||
|
|
||||||
def build_ciphers_lists(opensslbin):
|
def build_ciphers_lists():
|
||||||
global all_ciphers, old_ciphers, intermediate_ciphers, modern_ciphers, errors
|
sstlsurl = "https://statics.tls.security.mozilla.org/server-side-tls-conf.json"
|
||||||
# from https://wiki.mozilla.org/Security/Server_Side_TLS
|
conf = dict()
|
||||||
allC = 'ALL:COMPLEMENTOFALL:+aRSA'
|
try:
|
||||||
oldC = 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-S' \
|
raw = urllib2.urlopen(sstlsurl).read()
|
||||||
'HA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM' \
|
conf = json.loads(raw)
|
||||||
'-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-' \
|
logging.debug('retrieving online server side tls recommendations from %s' % sstlsurl)
|
||||||
'AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA' \
|
except:
|
||||||
'384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AE' \
|
print("failed to retrieve JSON configurations from %s" % sstlsurl)
|
||||||
'S128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-' \
|
sys.exit(23)
|
||||||
'AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES' \
|
|
||||||
'256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:DES-CBC3-SH' \
|
|
||||||
'A:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!E' \
|
|
||||||
'DH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA'
|
|
||||||
intC = 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-S' \
|
|
||||||
'HA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM' \
|
|
||||||
'-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-' \
|
|
||||||
'AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA' \
|
|
||||||
'384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AE' \
|
|
||||||
'S128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-' \
|
|
||||||
'AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES' \
|
|
||||||
'256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DE' \
|
|
||||||
'S-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SH' \
|
|
||||||
'A:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA'
|
|
||||||
modernC = 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-S' \
|
|
||||||
'HA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM' \
|
|
||||||
'-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-' \
|
|
||||||
'AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA' \
|
|
||||||
'384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AE' \
|
|
||||||
'S128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-' \
|
|
||||||
'AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK'
|
|
||||||
blackhole = open(os.devnull, 'w')
|
|
||||||
|
|
||||||
# use system openssl if not on linux 64
|
global old, inter, modern
|
||||||
if not opensslbin:
|
old = conf["configurations"]["old"]
|
||||||
if platform.system() == 'Linux' and platform.architecture()[0] == '64bit':
|
inter = conf["configurations"]["intermediate"]
|
||||||
opensslbin = mypath + '/openssl'
|
modern = conf["configurations"]["modern"]
|
||||||
elif platform.system() == 'Darwin' and platform.architecture()[0] == '64bit':
|
|
||||||
opensslbin = mypath + '/openssl-darwin64'
|
|
||||||
else:
|
|
||||||
opensslbin='openssl'
|
|
||||||
print("warning: analyze.py is using system's openssl, which may limit the tested ciphers and recommendations")
|
|
||||||
|
|
||||||
logging.debug('Loading all ciphers: ' + allC)
|
|
||||||
all_ciphers = subprocess.Popen([opensslbin, 'ciphers', allC],
|
|
||||||
stderr=blackhole, stdout=subprocess.PIPE).communicate()[0].rstrip()
|
|
||||||
all_ciphers = str_compat(all_ciphers)
|
|
||||||
all_ciphers = str(all_ciphers).split(":")
|
|
||||||
logging.debug('Loading old ciphers: ' + oldC)
|
|
||||||
old_ciphers = subprocess.Popen([opensslbin, 'ciphers', oldC],
|
|
||||||
stderr=blackhole, stdout=subprocess.PIPE).communicate()[0].rstrip()
|
|
||||||
old_ciphers = str_compat(old_ciphers)
|
|
||||||
old_ciphers = str(old_ciphers).split(':')
|
|
||||||
logging.debug('Loading intermediate ciphers: ' + intC)
|
|
||||||
intermediate_ciphers = subprocess.Popen([opensslbin, 'ciphers', intC],
|
|
||||||
stderr=blackhole, stdout=subprocess.PIPE).communicate()[0].rstrip()
|
|
||||||
intermediate_ciphers = str_compat(intermediate_ciphers)
|
|
||||||
intermediate_ciphers = str(intermediate_ciphers).split(':')
|
|
||||||
logging.debug('Loading modern ciphers: ' + modernC)
|
|
||||||
modern_ciphers = subprocess.Popen([opensslbin, 'ciphers', modernC],
|
|
||||||
stderr=blackhole, stdout=subprocess.PIPE).communicate()[0].rstrip()
|
|
||||||
modern_ciphers = str_compat(modern_ciphers)
|
|
||||||
modern_ciphers = str(modern_ciphers).split(':')
|
|
||||||
blackhole.close()
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description='Analyze cipherscan results and provides guidelines to improve configuration.',
|
description='Analyze cipherscan results and provides guidelines to improve configuration.',
|
||||||
usage='\n* Analyze a single target, invokes cipherscan: $ ./analyze.py -t [target]' \
|
usage='\n* Analyze a single target, invokes cipherscan: $ ./analyze.py -t [target]' \
|
||||||
'\n* Evaluate json results passed through stdin: $ python analyze.py < target_results.json' \
|
'\n* Evaluate json results passed through stdin: $ python analyze.py < target_results.json' \
|
||||||
'\nexample: ./analyze.py mozilla.org',
|
'\nexample: ./analyze.py -t mozilla.org',
|
||||||
epilog='Julien Vehent [:ulfr] - 2014')
|
epilog='Julien Vehent [:ulfr] - 2014')
|
||||||
parser.add_argument('-d', dest='debug', action='store_true',
|
parser.add_argument('-d', dest='debug', action='store_true',
|
||||||
help='debug output')
|
help='debug output')
|
||||||
@ -488,7 +440,7 @@ def main():
|
|||||||
if args.operator:
|
if args.operator:
|
||||||
operator=args.operator
|
operator=args.operator
|
||||||
|
|
||||||
build_ciphers_lists(args.openssl)
|
build_ciphers_lists()
|
||||||
|
|
||||||
if args.target:
|
if args.target:
|
||||||
# evaluate target specified as argument
|
# evaluate target specified as argument
|
||||||
|
Loading…
Reference in New Issue
Block a user