parent
f9f6645aa6
commit
ffa44a0043
@ -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 |
Loading…
Reference in new issue