UNPKG

@passmarked/ssl

Version:

Rules that relate to checking the SSL configuration of each individual resolved server from the domain to ensure locked down config with the broadest compatibility

153 lines (104 loc) 3.61 kB
const tls = require('tls'); const net = require('net'); const url = require('url'); const _ = require('underscore'); const moment = require('moment'); const async = require('async'); const S = require('string'); /** * Check the host of a certificate **/ module.exports = exports = function(payload, options, fn) { // pull out the params we can use var address = options.address; var algorithm = options.algorithm; var client = options.client; var socket = options.client; // get the data var data = payload.getData(); // parse the url var uri = url.parse(data.url); // sanity check if(!client) return fn(null); // get the certificate var cert = client.getPeerCertificate(false); // just in case the peer does not provide a certificate ... if(!cert) return fn(null); // extract a few params to use var subject = cert.subject || {}; var commonName = subject.CN || subject.cn || cert.commonName || null; var org = subject.O || subject.o || cert.organization || null; var altnames = (cert.subjectaltname || '').split(','); var domains = []; // must be a array if(Array.isArray(altnames) === true) { // loop and add all the domains for(var i = 0; i < altnames.length; i++) { // get a local reference to work with var altname = S( altnames[i] || '' ).trim().s; // check that the altname was not empty if( S( altname || '' ).isEmpty() === true ) continue; // check if this is a DNS entry if(altname.toLowerCase().indexOf('dns:') != 0 && altname.toLowerCase().indexOf('ip:') != 0) continue; // get the domain variable we will be adding var domain = altname.split(':')[1].toLowerCase(); // add to the list then if(domains.indexOf(domain) === -1) { // add the unique name domains.push( domain ); } } } // and then add the common name if( S(commonName).isEmpty() !== true ) { // get the domain to add var domain = commonName.toLowerCase(); // should not be in list already if(domains.indexOf(domain) === -1) { // add our unique domain domains.push( domain ); } } // the altname we are using for this request var currentAltName = null; var sections = uri.hostname.split('.'); // check if the given hostname validates according to certificate for(var i = 0; i < domains.length; i++) { // local reference var patterns = domains[i].split('.'); // flag if we had any failures ? var success = true; // split the domain for(var a = 0; a < sections.length; a++) { // check if there is a item in that pattern slot if(patterns[a] !== '*' && patterns[a] != sections[a]) { // mark as false ... success = false; // stop ! break; } } // check if the regex matches if(success == true) { // found it ! currentAltName = domains[i]; // break the loop break; } } if( currentAltName === null ) { // add the rule payload.addRule({ message: 'Certificate did not match hostname', type: 'error', key: 'host' }, { message: 'Found $ valid hostnames from $ but $ was not one of them for $ certificate supplied. Valid hosts only include: $', identifiers: [ domains.length, address, uri.hostname, algorithm, domains.join(', ') ] }); } // finish strong fn(null); };