Web
banshee/web-collection/network/tls/
certificate
The certificate a server presents on a TLS port: days before expiry, trust, chain, name, key, SHA-1, old protocols and weak ciphers accepted.
by banshee1 installation2 versions
// The certificate a server presents on a port, and what it accepts in protocols and ciphers: the probe
// "certificate" (0043). It ignores the TLS profile of the probe, since its role is to test: each handshake
// says what it offers. Nothing is sent but handshakes, and with "Remote Desktop" the 19 bytes of an RDP
// connection request before them.
need('host:address');
$port = variable('certificate:port', 'integer', array(
'label' => 'Port',
'help' => 'The TLS port of the service: 443, 636, 465, 3389, 5986.',
'default' => 443,
));
$name = variable('certificate:name', 'text', array(
'label' => 'Expected name',
'help' => 'The name the certificate must bear, when it differs from the address of the host. It is also the name announced to the server.',
'required' => false,
));
$authorities = variable('certificate:authorities', 'multiline', array(
'label' => 'Added authorities',
'help' => 'The certificates, in PEM blocks, of the authorities that issue for this service beside those of the system: the authority of the company. They count for "Certificate trusted" only.',
'required' => false,
));
$rdp = variable('certificate:rdp', 'boolean', array(
'label' => 'Remote Desktop',
'help' => 'Check for port 3389: the handshake follows an RDP negotiation.',
'default' => false,
));
output('days-left', 'number', array('label' => 'Days before expiry', 'unit' => 'd'));
output('trusted', 'text', array('label' => 'Certificate trusted'));
output('chain', 'text', array('label' => 'Chain'));
output('name', 'text', array('label' => 'Name matches'));
output('self-signed', 'text', array('label' => 'Self-signed'));
output('weak-key', 'text', array('label' => 'Key too short'));
output('sha1', 'text', array('label' => 'SHA-1 signature'));
output('old-protocols', 'text', array('label' => 'Old protocols accepted'));
output('weak-suites', 'text', array('label' => 'Weak ciphers accepted'));
output('tls13', 'text', array('label' => 'TLS 1.3 accepted'));
watch.below('expiring', 'Expiry close', 'days-left', array('warning' => 30, 'critical' => 7));
watch.state('untrusted', 'Certificate not trusted', 'trusted', array('warning' => array('no'), 'enabled' => false));
watch.expect('chain-incomplete', 'Incomplete chain', 'chain', array('warning' => array('complete')));
watch.expect('name-mismatch', 'Name does not match', 'name', array('critical' => array('yes')));
watch.state('self-signed', 'Self-signed certificate', 'self-signed', array('warning' => array('yes'), 'enabled' => false));
watch.state('weak-key', 'Key too short', 'weak-key', array('warning' => array('yes')));
watch.state('sha1', 'SHA-1 signature', 'sha1', array('warning' => array('yes')));
watch.expect('old-protocols', 'Old protocol accepted', 'old-protocols', array('warning' => array('none')));
watch.expect('weak-suites', 'Weak cipher accepted', 'weak-suites', array('warning' => array('none')));
watch.expect('tls13', 'Recent protocol refused', 'tls13', array('warning' => array('yes'), 'enabled' => false));
// An empty name, list of authorities or preamble counts as absent (0043).
$preamble = '';
if ($rdp) {
$preamble = 'rdp';
}
// A handshake that offers everything Go speaks, and reads the certificate without refusing it.
$found = tls.inspect($port, name=$name, authorities=$authorities, preamble=$preamble);
$leaf = $found['certificates'][0];
value('days-left', number.round($leaf['days_left'], 1));
$trusted = 'no';
if ($found['trusted']) {
$trusted = 'yes';
}
value('trusted', $trusted);
value('chain', $found['chain']);
$name_ok = 'yes';
if (!$found['name_matches']) {
$name_ok = 'no';
}
value('name', $name_ok);
$self = 'no';
if ($leaf['self_issued']) {
$self = 'yes';
}
value('self-signed', $self);
// An RSA key under 2048 bits, or a curve under 256 bits.
$weak_key = 'no';
if (($leaf['key'] == 'rsa' && $leaf['key_bits'] < 2048) || ($leaf['key'] == 'ecdsa' && $leaf['key_bits'] < 256)) {
$weak_key = 'yes';
}
value('weak-key', $weak_key);
// SHA-1 in the chain, outside the signature a root authority bears on itself.
$sha1 = 'no';
foreach ($found['certificates'] as $certificate) {
if (!$certificate['self_issued'] && text.contains($certificate['signature'], 'SHA1')) {
$sha1 = 'yes';
}
}
value('sha1', $sha1);
// Each version once, offering all its ciphers: the server accepts it or refuses it.
$accepted = array();
foreach (array('1.0', '1.1', '1.2', '1.3') as $version) {
$try = tls.try($port, $version, name=$name, preamble=$preamble);
$accepted[$version] = $try['accepted'];
}
$old = array();
foreach (array('1.0', '1.1') as $version) {
if ($accepted[$version]) {
$old = list.add($old, 'TLS ' . $version);
}
}
$old_text = 'none';
if (list.count($old) > 0) {
$old_text = text.join($old, ', ');
}
value('old-protocols', $old_text);
// The five weak ciphers, each offered alone at the highest version under TLS 1.3 the server accepts.
$highest = '';
foreach (array('1.2', '1.1', '1.0') as $version) {
if ($highest == '' && $accepted[$version]) {
$highest = $version;
}
}
$weak = array();
if ($highest != '') {
$suites = array('TLS_ECDHE_ECDSA_WITH_RC4_128_SHA', 'TLS_ECDHE_RSA_WITH_RC4_128_SHA', 'TLS_RSA_WITH_RC4_128_SHA', 'TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA', 'TLS_RSA_WITH_3DES_EDE_CBC_SHA');
foreach ($suites as $suite) {
$try = tls.try($port, $highest, $suite, name=$name, preamble=$preamble);
if ($try['accepted']) {
$weak = list.add($weak, $suite);
}
}
}
$weak_text = 'none';
if (list.count($weak) > 0) {
$weak_text = text.join($weak, ', ');
}
value('weak-suites', $weak_text);
$tls13 = 'no';
if ($accepted['1.3']) {
$tls13 = 'yes';
}
value('tls13', $tls13);