@@ -2,9 +2,15 @@ source 'https://rubygems.org' | |||
gem 'httparty' | |||
gem 'nokogiri' | |||
gem 'net-scp' | |||
gem 'net-sftp' | |||
gem 'tcp_timeout' | |||
gem 'parallel' | |||
gem 'ruby-progressbar' | |||
gem 'logging' | |||
group :test do | |||
gem 'rspec' | |||
gem 'webmock' | |||
end | |||
gem 'debase' |
@@ -2,12 +2,16 @@ | |||
hostnames: | |||
- imirhil.fr | |||
- libwalk.so | |||
- keltia.net | |||
- www.keltia.net | |||
- quentin.demouliere.eu | |||
- rss.decornulier.eu | |||
- fralef.me | |||
- jeekajoo.eu | |||
- status.jbfavre.org | |||
- rosset.net | |||
- owc.h.arysthaar.pw | |||
- crifo.org | |||
- matlink.fr | |||
- description: Banques en ligne | |||
hostnames: | |||
- www.labanquepostale.fr | |||
@@ -31,6 +35,11 @@ | |||
- www.cmb.fr | |||
- www.ca-paris.fr | |||
- www.ca-cotesdarmor.fr | |||
- secure.ingdirect.fr | |||
- banque-accord.fr | |||
- espace-client-secure.banque-casino.fr | |||
- bforbank.com | |||
- hellobank.fr | |||
- description: Webmails | |||
hostnames: | |||
- webmail.mailden.fr | |||
@@ -56,6 +65,8 @@ | |||
- www.cjn.justice.gouv.fr | |||
- www.interieur.gouv.fr | |||
- mon.service-public.fr | |||
- www.correspondants.cnil.fr | |||
- sso.quechoisir.org | |||
- description: Sites de commerce en ligne | |||
hostnames: | |||
- signin.ebay.fr | |||
@@ -63,9 +74,14 @@ | |||
- grosbill.com | |||
- secure.darty.com | |||
- secure.boulanger.fr | |||
- capitainetrain.com | |||
- www.capitainetrain.com | |||
- espace-client.voyages-sncf.com | |||
- www.pixmania.fr | |||
- clients.cdiscount.com | |||
- secure.ikea.com | |||
- secure.fnac.com | |||
- www.laredoute.fr | |||
- online.carrefour.fr | |||
- description: Divers | |||
hostnames: | |||
- www.mailden.net | |||
@@ -0,0 +1,160 @@ | |||
<!DOCTYPE html> | |||
<html lang="fr"> | |||
<head> | |||
<meta charset="utf-8"> | |||
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |||
<meta name="viewport" content="width=device-width, initial-scale=1"> | |||
<title>Status SSL/TLS banque & commerce en ligne</title> | |||
<link rel="stylesheet" href="bootstrap.min.css"> | |||
<style> | |||
body { | |||
margin-top: 10px; | |||
} | |||
td { | |||
text-align: center; | |||
} | |||
</style> | |||
</head> | |||
<body> | |||
<div class="container-fluid"> | |||
<div class="row"> | |||
<div class="col-md-12"> | |||
<table class="table table-bordered table-hover table-condensed"> | |||
<tbody> | |||
<% | |||
first = true | |||
results.each do |r| | |||
unless first | |||
%> | |||
<tr> | |||
<th colspan="16"> </th> | |||
</tr> | |||
<% | |||
end | |||
first = false | |||
%> | |||
<tr> | |||
<th colspan="16" id="<%= r[0] %>"><%= r[0] %></th> | |||
</tr> | |||
<tr> | |||
<th>Site (IP)</th> | |||
<td>Rang</td> | |||
<td>Clef (bits)</td> | |||
<td>Chiff. (bits)</td> | |||
<td class="danger">SSL</td> | |||
<td class="success">TLS</td> | |||
<td class="success">TLS 1.2</td> | |||
<td class="success">TLS only</td> | |||
<td class="danger">SHA1 sig</td> | |||
<td class="danger">RC4</td> | |||
<td class="danger">DES/3DES</td> | |||
<td class="danger">MD5</td> | |||
<td class="success">PFS</td> | |||
<td class="success">PFS only</td> | |||
<td class="success">HSTS</td> | |||
<td class="success">HSTS long</td> | |||
</tr> | |||
<% r[1].each do |n| | |||
s = n.server | |||
rank_color = case n.grade | |||
when 'A+' then :info | |||
when 'A', 'A-' then :success | |||
when 'B', 'C' then :warning | |||
else :danger | |||
end | |||
%> | |||
<tr> | |||
<th id="<%= s.hostname %>"> | |||
<a href="https://www.ssllabs.com/ssltest/analyze.html?d=<%= s.hostname %>" target="_blank"> | |||
<%= s.hostname %> | |||
</a> | |||
</th> | |||
<td class="<%= rank_color %>"> | |||
<%= n.grade %> | |||
</td> | |||
<td class="<%= s.key_size < 2048 ? :danger : s.key_size < 4096 ? :warning : :success %>"> | |||
<%= s.key_size %> | |||
(<%= s.key_size < 2048 ? '☹' : '☺' %>) | |||
</td> | |||
<% cipher_size = s.cipher_size[:worst] %> | |||
<td class="<%= cipher_size < 112 ? :danger : cipher_size < 128 ? :warning : :success %>"> | |||
<%= cipher_size %> | |||
(<%= cipher_size < 128 ? '☹' : '☺' %>) | |||
</td> | |||
<td class="<%= s.ssl? ? :danger : :success %>"> | |||
<%= s.ssl? ? '✓' : '✗' %> | |||
(<%= s.ssl? ? '☹' : '☺' %>) | |||
</td> | |||
<td class="<%= s.tls? ? :success : :danger %>"> | |||
<%= s.tls? ? '✓' : '✗' %> | |||
(<%= s.tls? ? '☺' : '☹' %>) | |||
</td> | |||
<td class="<%= s.tlsv1_2? ? :success : :danger %>"> | |||
<%= s.tlsv1_2? ? '✓' : '✗' %> | |||
(<%= s.tlsv1_2? ? '☺' : '☹' %>) | |||
</td> | |||
<td class="<%= s.tls_only? ? :success : :danger %>"> | |||
<%= s.tls_only? ? '✓' : '✗' %> | |||
(<%= s.tls_only? ? '☺' : '☹' %>) | |||
</td> | |||
<td class="<%= s.sha1_sig? ? :danger : :success %>"> | |||
<%= s.sha1_sig? ? '✓' : '✗' %> | |||
(<%= s.sha1_sig? ? '☹' : '☺' %>) | |||
</td> | |||
<td class="<%= s.rc4? ? :danger : :success %>"> | |||
<%= s.rc4? ? '✓' : '✗' %> | |||
(<%= s.rc4? ? '☹' : '☺' %>) | |||
</td> | |||
<td class="<%= s.any_des? ? :danger : :success %>"> | |||
<%= s.any_des? ? '✓' : '✗' %> | |||
(<%= s.any_des? ? '☹' : '☺' %>) | |||
</td> | |||
<td class="<%= s.md5? ? :danger : :success %>"> | |||
<%= s.md5? ? '✓' : '✗' %> | |||
(<%= s.md5? ? '☹' : '☺' %>) | |||
</td> | |||
<td class="<%= s.pfs? ? :success : :danger %>"> | |||
<%= s.pfs? ? '✓' : '✗' %> | |||
(<%= s.pfs? ? '☺' : '☹' %>) | |||
</td> | |||
<td class="<%= s.pfs_only? ? :success : :danger %>"> | |||
<%= s.pfs_only? ? '✓' : '✗' %> | |||
(<%= s.pfs_only? ? '☺' : '☹' %>) | |||
</td> | |||
<td class="<%= s.hsts? ? :success : :danger %>"> | |||
<%= s.hsts? ? '✓' : '✗' %> | |||
(<%= s.hsts? ? '☺' : '☹' %>) | |||
</td> | |||
<td class="<%= s.hsts_long? ? :success : :danger %>"> | |||
<%= s.hsts_long? ? '✓' : '✗' %> | |||
(<%= s.hsts_long? ? '☺' : '☹' %>) | |||
</td> | |||
</tr> | |||
<% end %> | |||
<tr> | |||
<th>Site</th> | |||
<td>Rang</td> | |||
<td>Clef (bits)</td> | |||
<td>Chiff. (bits)</td> | |||
<td class="danger">SSL</td> | |||
<td class="success">TLS</td> | |||
<td class="success">TLS 1.2</td> | |||
<td class="success">TLS only</td> | |||
<td class="danger">SHA1 sig</td> | |||
<td class="danger">RC4</td> | |||
<td class="danger">DES/3DES</td> | |||
<td class="danger">MD5</td> | |||
<td class="success">PFS</td> | |||
<td class="success">PFS only</td> | |||
<td class="success">HSTS</td> | |||
<td class="success">HSTS long</td> | |||
</tr> | |||
<% end %> | |||
</tbody> | |||
</table> | |||
</div> | |||
</div> | |||
</div> | |||
</body> | |||
</html> |
@@ -1 +1,6 @@ | |||
require 'sslcheck/ssllabs/api' | |||
module SSLCheck | |||
autoload :Server, 'sslcheck/server' | |||
autoload :Grade, 'sslcheck/grade' | |||
end |
@@ -0,0 +1,93 @@ | |||
module SSLCheck | |||
class Grade | |||
attr_reader :server, :score, :grade, :warning, :good | |||
def initialize(server) | |||
@server = server | |||
protocol_score | |||
key_exchange_score | |||
cipher_strengths_score | |||
@score = @protocol_score*0.3 + @key_exchange_score*0.3 + @cipher_strengths_score*0.4 | |||
calculate_grade | |||
warning | |||
success | |||
perfect | |||
end | |||
private | |||
def calculate_grade | |||
@grade = case @score | |||
when 0...20 then 'F' | |||
when 20...35 then 'E' | |||
when 35...50 then 'D' | |||
when 50...65 then 'C' | |||
when 65...80 then 'B' | |||
else 'A' | |||
end | |||
@grade = [@grade, 'B'].max if !@server.tlsv1_2? or @server.key_size < 2048 | |||
@grade = [@grade, 'D'].max if @server.rc4? | |||
@grade = [@grade, 'E'].max if @server.des3? | |||
@grade = [@grade, 'F'].max if @server.ssl? or @server.key_size < 1024 | |||
end | |||
def warning | |||
@warning = [] | |||
@warning << :md5_sig if @server.md5_sig? | |||
@warning << :sha1_sig if @server.sha1_sig? | |||
@warning << :md5 if @server.md5? | |||
#@warning << :sha1 if @server.sha1? | |||
@warning << :rc4 if @server.rc4? | |||
@warning << :des if @server.des? | |||
@warning << :des3 if @server.des3? | |||
end | |||
def success | |||
@success = [] | |||
@success << :pfs if @server.pfs_only? | |||
@success << :hsts if @server.hsts? | |||
@success << :hsts_long if @server.hsts_long? | |||
end | |||
ALL_SUCCESS = %i(pfs hsts hsts_long) | |||
def perfect | |||
@grade = 'A+' if @grade == 'A' and @warning.empty? and (ALL_SUCCESS & @success) == ALL_SUCCESS | |||
end | |||
METHODS_SCORES = { SSLv2: 0, SSLv3: 80, TLSv1: 90, TLSv1_1: 95, TLSv1_2: 100 } | |||
def protocol_score | |||
methods = @server.supported_methods | |||
worst, best = methods[:worst], methods[:best] | |||
@protocol_score = (METHODS_SCORES[worst] + METHODS_SCORES[best]) / 2 | |||
end | |||
def key_exchange_score | |||
@key_exchange_score = case @server.key_size | |||
when 0 then 0 | |||
when 0...512 then 20 | |||
when 512...1024 then 40 | |||
when 1024...2048 then 80 | |||
when 2048...4096 then 90 | |||
else 100 | |||
end | |||
end | |||
def cipher_strength_score(cipher_strength) | |||
case cipher_strength | |||
when 0 then 0 | |||
when 0...128 then 20 | |||
when 128...256 then 80 | |||
else 100 | |||
end | |||
end | |||
def cipher_strengths_score | |||
strength = @server.cipher_size | |||
worst, best = strength[:min], strength[:max] | |||
@cipher_strengths_score = (cipher_strength_score(worst) + cipher_strength_score(best)) / 2 | |||
end | |||
end | |||
end |
@@ -0,0 +1,270 @@ | |||
require 'socket' | |||
require 'openssl' | |||
require 'httparty' | |||
require 'parallel' | |||
require 'tcp_timeout' | |||
module SSLCheck | |||
class Server | |||
EXISTING_METHODS = %i(TLSv1_2 TLSv1_1 TLSv1 SSLv3 SSLv2) | |||
SUPPORTED_METHODS = OpenSSL::SSL::SSLContext::METHODS | |||
TIMEOUT = 5 | |||
class TLSNotAvailableException < Exception; end | |||
class CipherNotAvailable < Exception; end | |||
attr_reader :hostname, :port, :prefered_ciphers, :cert, :hsts | |||
def initialize(hostname, port=443, methods: EXISTING_METHODS) | |||
@log = Logging.logger[hostname] | |||
@hostname = hostname | |||
@port = port | |||
@methods = methods | |||
@log.error { "Check for #{hostname} (#{port})"} | |||
extract_cert | |||
fetch_prefered_ciphers | |||
check_supported_cipher | |||
fetch_hsts | |||
end | |||
def supported_methods | |||
worst = EXISTING_METHODS.find { |method| !@prefered_ciphers[method].nil? } | |||
best = EXISTING_METHODS.reverse.find { |method| !@prefered_ciphers[method].nil? } | |||
{worst: worst, best: best} | |||
end | |||
def key_size | |||
key = @cert.public_key | |||
case key | |||
when OpenSSL::PKey::RSA then | |||
key.n.num_bits | |||
when OpenSSL::PKey::DSA then | |||
key.p.num_bits | |||
when OpenSSL::PKey::EC then | |||
key.group.degree | |||
end | |||
end | |||
def cipher_size | |||
cipher_strengths = supported_ciphers.collect { |c| c[2] }.uniq.sort | |||
worst, best = cipher_strengths.first, cipher_strengths.last | |||
{worst: worst, best: best} | |||
end | |||
EXISTING_METHODS.each do |method| | |||
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 | |||
def #{method.to_s.downcase}? | |||
!prefered_ciphers[:#{method}].nil? | |||
end | |||
RUBY_EVAL | |||
end | |||
{ | |||
md2: %w(md2WithRSAEncryption), | |||
md5: %w(md5WithRSAEncryption md5WithRSA), | |||
sha1: %w(sha1WithRSAEncryption sha1WithRSA dsaWithSHA1 dsaWithSHA1_2 ecdsa_with_SHA1) | |||
}.each do |name, signature| | |||
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 | |||
def #{name}_sig? | |||
#{signature}.include? @cert.signature_algorithm | |||
end | |||
RUBY_EVAL | |||
end | |||
{ | |||
md5: %w(MD5), | |||
sha1: %w(SHA), | |||
rc4: %w(RC4), | |||
des3: %w(3DES DES-CBC3), | |||
des: %w(DES-CBC) | |||
}.each do |name, ciphers| | |||
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 | |||
def #{name}? | |||
supported_ciphers.any? { |supported| #{ciphers}.any? { |available| /(^|-)#\{available\}(-|$)/ =~ supported[0] } } | |||
end | |||
RUBY_EVAL | |||
end | |||
def any_des? | |||
des? or des3? | |||
end | |||
def ssl? | |||
sslv2? or sslv3? | |||
end | |||
def tls? | |||
tlsv1? or tlsv1_1? or tlsv1_2? | |||
end | |||
def tls_only? | |||
tls? and !ssl? | |||
end | |||
PFS_CIPHERS = [/^DHE-RSA-/, /^DHE-DSS-/, /^ECDHE-RSA-/, /^ECDHE-ECDSA-/] | |||
def pfs? | |||
supported_ciphers.any? { |cipher| PFS_CIPHERS.any? { |pc| pc =~ cipher[0] } } | |||
end | |||
def pfs_only? | |||
supported_ciphers.all? { |cipher| PFS_CIPHERS.any? { |pc| pc =~ cipher[0] } } | |||
end | |||
def supported_ciphers | |||
@supported_ciphers.values.flatten(1).uniq | |||
end | |||
def supported_ciphers_by_method | |||
@supported_ciphers | |||
end | |||
def hsts? | |||
!@hsts.nil? | |||
end | |||
def hsts_long? | |||
hsts? and @hsts >= 6*30*24*60*60 | |||
end | |||
private | |||
def ssl_client(method = nil, ciphers = nil, &block) | |||
ssl_context = method.nil? ? OpenSSL::SSL::SSLContext.new : OpenSSL::SSL::SSLContext.new(method) | |||
ssl_context.ciphers = ciphers if ciphers | |||
@log.debug { "Try #{method} connection with #{ciphers}" } | |||
[Socket::AF_INET, Socket::AF_INET6].each do |family| | |||
@log.debug { "Try connection for family #{family}" } | |||
addrs = begin | |||
Socket.getaddrinfo @hostname, nil, family, :STREAM | |||
rescue SocketError => e | |||
@log.debug { "Unable to resolv #{@hostname} : #{e}" } | |||
next | |||
end | |||
addrs.each do |addr| | |||
addr = addr[3] | |||
sockaddr = Socket.sockaddr_in @port, addr | |||
socket = Socket.new family, Socket::SOCK_STREAM | |||
begin | |||
@log.debug { "Connecting to #{addr}:#{@port}" } | |||
socket.connect_nonblock sockaddr | |||
rescue IO::WaitWritable | |||
@log.debug { "Waiting for connection to #{addr}:#{@port}" } | |||
if IO.select nil, [socket], nil, TIMEOUT | |||
begin | |||
if socket.connect_nonblock(sockaddr) == 0 | |||
@log.debug { "Connected to #{addr}:#{@port}" } | |||
ssl_socket = OpenSSL::SSL::SSLSocket.new socket, ssl_context | |||
ssl_socket.hostname = @hostname | |||
begin | |||
@log.debug { "TLS connection to #{addr}:#{@port}" } | |||
ssl_socket.connect | |||
return block_given? ? block.call(ssl_socket) : nil | |||
rescue OpenSSL::SSL::SSLError => e | |||
@log.debug { "Cipher not supported #{addr}:#{@port} : #{e}" } | |||
raise CipherNotAvailable.new e | |||
ensure | |||
@log.debug { "Closing TLS connection to #{addr}:#{@port}" } | |||
ssl_socket.close | |||
end | |||
end | |||
rescue Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::EHOSTUNREACH => e | |||
@log.debug { "Connection failure to #{addr}:#{@port} : #{e}" } | |||
end | |||
else | |||
@log.debug { "Connection timeout to #{addr}:#{@port}" } | |||
end | |||
ensure | |||
@log.debug { "Closing connection to #{addr}:#{@port}" } | |||
socket.close | |||
end | |||
end | |||
end | |||
@log.debug { "No TLS available on #{@hostname}" } | |||
raise CipherNotAvailable.new | |||
end | |||
def extract_cert | |||
@methods.each do |method| | |||
next unless SUPPORTED_METHODS.include? method | |||
begin | |||
@cert = ssl_client(method) { |s| s.peer_cert } | |||
@log.warn { "Certificate #{@cert.subject}"} | |||
break | |||
rescue CipherNotAvailable | |||
end | |||
end | |||
raise TLSNotAvailableException.new unless @cert | |||
end | |||
def prefered_cipher(method) | |||
cipher = ssl_client(method, %w(ALL:COMPLEMENTOFALL)) { |s| s.cipher } | |||
@log.warn { "Prefered cipher for #{method} : #{cipher[0]}"} | |||
cipher | |||
rescue CipherNotAvailable => e | |||
@log.info { "Method #{method} not supported : #{e}"} | |||
nil | |||
end | |||
def fetch_prefered_ciphers | |||
@prefered_ciphers = {} | |||
@methods.each do |method| | |||
next unless SUPPORTED_METHODS.include? method | |||
@prefered_ciphers[method] = prefered_cipher method | |||
end | |||
end | |||
def available_ciphers(method) | |||
OpenSSL::SSL::SSLContext.new(method).ciphers | |||
end | |||
def supported_cipher?(method, cipher) | |||
ssl_client method, [cipher] | |||
@log.warn { "Verify #{method} / #{cipher[0]} : OK"} | |||
true | |||
rescue TLSNotAvailableException, CipherNotAvailable => e | |||
@log.debug { "Verify #{method} / #{cipher[0]} : NOK (#{e}"} | |||
false | |||
end | |||
def check_supported_cipher | |||
@supported_ciphers = {} | |||
@methods.each do |method| | |||
next unless SUPPORTED_METHODS.include? method and @prefered_ciphers[method] | |||
@supported_ciphers[method] = available_ciphers(method).select { |cipher| supported_cipher? method, cipher } | |||
end | |||
end | |||
def fetch_hsts | |||
port = @port == 443 ? '' : ":#{@port}" | |||
response = nil | |||
@methods.each do |method| | |||
begin | |||
next unless SUPPORTED_METHODS.include? method | |||
@log.debug { "Check HSTS with #{method}" } | |||
response = HTTParty.head "https://#{@hostname}#{port}/", {follow_redirects: false, verify: false, ssl_version: method, timeout: TIMEOUT} | |||
break | |||
rescue | |||
@log.debug { "#{method} not supported" } | |||
end | |||
end | |||
if response and header = response.headers['strict-transport-security'] | |||
name, value = header.split '=' | |||
if name == 'max-age' | |||
@hsts = value.to_i | |||
@log.info { "HSTS : #{@hsts}" } | |||
return | |||
end | |||
end | |||
@log.info { 'No HSTS' } | |||
@hsts = nil | |||
end | |||
end | |||
end |
@@ -1,55 +1,11 @@ | |||
#!/usr/bin/env ruby | |||
require 'erb' | |||
require 'yaml' | |||
#ENV['LD_LIBRARY_PATH'] = '/home/aeris/Workspace/external/sslscan/openssl' | |||
require 'logging' | |||
$:.unshift 'lib' | |||
require 'sslcheck' | |||
SCORES = %w(A+ A A- B C D E F T M) | |||
def score(a); SCORES.index a.rank; end | |||
Logging.logger.root.appenders = Logging.appenders.stdout | |||
Logging.logger.root.level = :info | |||
def check(hostname) | |||
hostname.strip! | |||
print ' ', hostname, ' : ' | |||
begin | |||
result = SSLCheck::SSLLabs::API.new hostname | |||
puts result.rank | |||
result | |||
rescue SSLCheck::SSLLabs::NoEncryptionError | |||
puts 'no encryption' | |||
raise | |||
rescue => e | |||
puts e | |||
raise | |||
end | |||
end | |||
config = YAML.load_file 'hosts.yml' | |||
results = Hash[config.collect { |c| [c['description'], []] }] | |||
loop do | |||
waiting = false | |||
config.each do |c| | |||
description, hosts = c['description'], c['hostnames'] | |||
puts description | |||
hosts.clone.each do |host| | |||
begin | |||
results[description] << check(host) | |||
hosts.delete host | |||
rescue SSLCheck::SSLLabs::WaitingError | |||
waiting = true | |||
rescue SSLCheck::SSLLabs::Error | |||
rescue => e | |||
p e.backtrace | |||
end | |||
end | |||
end | |||
break if not waiting | |||
puts 'Waiting end of analyze' | |||
sleep 1*60 | |||
end | |||
results.each { |d, _| results[d].sort! { |a, b| score(a) <=> score(b) } } | |||
puts 'Generate results' | |||
File.write 'output/index.html', ERB.new(File.read('index.erb')).result(binding) | |||
server = SSLCheck::Server.new ARGV[0] | |||
p SSLCheck::Grade.new server |
@@ -0,0 +1,66 @@ | |||
#!/usr/bin/env ruby | |||
require 'erb' | |||
require 'yaml' | |||
require 'thread' | |||
require 'parallel' | |||
require 'logging' | |||
$:.unshift 'lib' | |||
require 'sslcheck' | |||
#Logging.logger.root.appenders = Logging.appenders.stdout | |||
#Logging.logger.root.level = :info | |||
SCORES = %w(A+ A A- B C D E F T M) | |||
def score(a); SCORES.index a.grade; end | |||
def check(hostname) | |||
hostname.strip! | |||
#print ' ', hostname, ' : ' | |||
begin | |||
server = SSLCheck::Server.new hostname | |||
note = SSLCheck::Grade.new server | |||
#puts note.grade | |||
note | |||
rescue => e | |||
puts e | |||
raise | |||
end | |||
end | |||
config = YAML.load_file 'hosts.yml' | |||
results = Hash[config.collect { |c| [c['description'], []] }] | |||
tests = [] | |||
config.each do |c| | |||
description, hosts = c['description'], c['hostnames'] | |||
hosts.each { |host| tests << [description, host] } | |||
end | |||
# tests.each do |description, host| | |||
# results[description] << check(host) | |||
# end | |||
semaphore = Mutex.new | |||
Parallel.each tests, progress: 'Testing', in_threads: 8 do |description, host| | |||
begin | |||
result = check host | |||
semaphore.synchronize do | |||
results[description] << result | |||
end | |||
rescue SSLCheck::Server::TLSNotAvailableException | |||
rescue Exception => e | |||
p host, e | |||
raise | |||
end | |||
end | |||
results.each do |d, _| | |||
results[d].sort! do |a, b| | |||
cmp = score(a) <=> score(b) | |||
cmp != 0 ? cmp : a.server.hostname <=> b.server.hostname | |||
end | |||
end | |||
puts 'Generate results' | |||
File.write 'output/index.html', ERB.new(File.read('index2.erb')).result(binding) |
@@ -0,0 +1,60 @@ | |||
#!/usr/bin/env ruby | |||
require 'erb' | |||
require 'yaml' | |||
$:.unshift 'lib' | |||
require 'sslcheck' | |||
SCORES = %w(A+ A A- B C D E F T M) | |||
def score(a); SCORES.index a.rank; end | |||
def check(hostname) | |||
hostname.strip! | |||
print ' ', hostname, ' : ' | |||
begin | |||
result = SSLCheck::SSLLabs::API.new hostname | |||
puts result.rank | |||
result | |||
rescue SSLCheck::SSLLabs::NoEncryptionError | |||
puts 'no encryption' | |||
raise | |||
rescue => e | |||
puts e | |||
raise | |||
end | |||
end | |||
config = YAML.load_file 'hosts.yml' | |||
results = Hash[config.collect { |c| [c['description'], []] }] | |||
loop do | |||
waiting = false | |||
config.each do |c| | |||
description, hosts = c['description'], c['hostnames'] | |||
puts description | |||
hosts.clone.each do |host| | |||
begin | |||
results[description] << check(host) | |||
hosts.delete host | |||
rescue SSLCheck::SSLLabs::WaitingError | |||
waiting = true | |||
rescue SSLCheck::SSLLabs::Error | |||
rescue => e | |||
p e.backtrace | |||
end | |||
end | |||
end | |||
break if not waiting | |||
puts 'Waiting end of analyze' | |||
sleep 1*60 | |||
end | |||
results.each do |d, _| | |||
results[d].sort! do |a, b| | |||
cmp = score(a) <=> score(b) | |||
cmp != 0 ? cmp : a.hostname <=> b.hostname | |||
end | |||
end | |||
puts 'Generate results' | |||
File.write 'output/index.html', ERB.new(File.read('index.erb')).result(binding) |
@@ -0,0 +1,79 @@ | |||
#!/usr/bin/env ruby | |||
#ENV['LD_LIBRARY_PATH'] = '/home/aeris/Workspace/external/sslscan/openssl' | |||
require 'logging' | |||
$:.unshift 'lib' | |||
require 'sslcheck' | |||
Logging.logger.root.appenders = Logging.appenders.stdout | |||
Logging.logger.root.level = :debug | |||
# Server = Class.new SSLCheck::Server do | |||
# def initialize | |||
# @key = OpenSSL::PKey::RSA.new 2048 | |||
# name = OpenSSL::X509::Name.parse 'CN=nobody/DC=example' | |||
# @cert = OpenSSL::X509::Certificate.new | |||
# @cert.version = 3 | |||
# @cert.serial = 0 | |||
# @cert.not_before = Time.now | |||
# @cert.not_after = Time.now + 3600 | |||
# @cert.public_key = @key.public_key | |||
# @cert.subject = name | |||
# | |||
# @supported_ciphers = | |||
# {SSLv3: [], TLSv1: [['ECDHE-RSA-AES256-SHA', 'TLSv1/SSLv3', 256, 256], ['DHE-RSA-AES256-SHA', 'TLSv1/SSLv3', 256, 256], ['ECDHE-RSA-AES128-SHA', 'TLSv1/SSLv3', 128, 128], ['DHE-RSA-AES128-SHA', 'TLSv1/SSLv3', 128, 128]], TLSv1_1: [['ECDHE-RSA-AES256-SHA', 'TLSv1/SSLv3', 256, 256], ['DHE-RSA-AES256-SHA', 'TLSv1/SSLv3', 256, 256], ['ECDHE-RSA-AES128-SHA', 'TLSv1/SSLv3', 128, 128], ['DHE-RSA-AES128-SHA', 'TLSv1/SSLv3', 128, 128]], TLSv1_2: [['ECDHE-RSA-AES256-GCM-SHA384', 'TLSv1/SSLv3', 256, 256], ['ECDHE-RSA-AES256-SHA384', 'TLSv1/SSLv3', 256, 256], ['ECDHE-RSA-AES256-SHA', 'TLSv1/SSLv3', 256, 256], ['DHE-RSA-AES256-GCM-SHA384', 'TLSv1/SSLv3', 256, 256], ['DHE-RSA-AES256-SHA256', 'TLSv1/SSLv3', 256, 256], ['DHE-RSA-AES256-SHA', 'TLSv1/SSLv3', 256, 256], ['ECDHE-RSA-AES128-GCM-SHA256', 'TLSv1/SSLv3', 128, 128], ['ECDHE-RSA-AES128-SHA256', 'TLSv1/SSLv3', 128, 128], ['ECDHE-RSA-AES128-SHA', 'TLSv1/SSLv3', 128, 128], ['DHE-RSA-AES128-GCM-SHA256', 'TLSv1/SSLv3', 128, 128], ['DHE-RSA-AES128-SHA256', 'TLSv1/SSLv3', 128, 128], ['DHE-RSA-AES128-SHA', 'TLSv1/SSLv3', 128, 128]]} | |||
# @prefered_ciphers = {SSLv3: nil, TLSv1: ['ECDHE-RSA-AES128-SHA', 'TLSv1/SSLv3', 128, 128], TLSv1_1: ['ECDHE-RSA-AES128-SHA', 'TLSv1/SSLv3', 128, 128], TLSv1_2: ['ECDHE-RSA-AES128-GCM-SHA256', 'TLSv1/SSLv3', 128, 128]} | |||
# | |||
# @hsts = 31536000 | |||
# end | |||
# end | |||
#server = Server.new | |||
#server = SSLCheck::Server.new 'www.cjn.justice.gouv.fr' | |||
#server = SSLCheck::Server.new 'www.capitainetrain.com' | |||
server = SSLCheck::Server.new 'matlink.fr' | |||
p SSLCheck::Grade.new server | |||
exit | |||
hostname, port = ['www.cjn.justice.gouv.fr', 443] | |||
tcp_client = TCPSocket.new hostname, port | |||
ssl_client = OpenSSL::SSL::SSLSocket.new tcp_client | |||
ssl_client.hostname = hostname | |||
p ssl_client.connect | |||
#hostname = 'provaping.com' | |||
#compressions = {} | |||
# existing_methods.each do |method| | |||
# next unless supported_methods.include? method | |||
# socket_context = OpenSSL::SSL::SSLContext.new method | |||
# socket_context.ciphers = %w(ALL:COMPLEMENTOFALL) | |||
# tcp_client = TCPSocket.new hostname, port | |||
# ssl_client = OpenSSL::SSL::SSLSocket.new tcp_client, socket_context | |||
# ssl_client.hostname = hostname | |||
# begin | |||
# ssl = ssl_client.connect | |||
# data = OpenSSL::ASN1.decode(ssl.session.to_der).value.find { |a| a.tag == 11 } | |||
# compression = !data.nil? | |||
# compressions[method] = compression | |||
# rescue OpenSSL::SSL::SSLError => e | |||
# end | |||
# end | |||
#p "Compressions", compressions | |||
#hostname = 'espaceclient.groupama.fr' # not supported | |||
# hostname = 'ameli.moncompte.mobi' | |||
# renegociations = {} | |||
# existing_methods.each do |method| | |||
# next unless supported_methods.include? method | |||
# socket_context = OpenSSL::SSL::SSLContext.new method | |||
# socket_context.ciphers = %w(ALL:COMPLEMENTOFALL) | |||
# tcp_client = TCPSocket.new hostname, port | |||
# ssl_client = OpenSSL::SSL::SSLSocket.new tcp_client, socket_context | |||
# ssl_client.hostname = hostname | |||
# begin | |||
# ssl = ssl_client.connect | |||
# p ssl | |||
# #data = OpenSSL::ASN1.decode(ssl.session.to_der).value.find { |a| a.tag == 11 } | |||
# rescue OpenSSL::SSL::SSLError => e | |||
# end | |||
# end | |||
# p "Renegociations", renegociations |