Browse Source

Handle SSL/TLS timeout

master
Nicolas Vinot 4 years ago
parent
commit
b62ef5192b
14 changed files with 311 additions and 235 deletions
  1. 5
    1
      .gitignore
  2. 2
    0
      Gemfile
  3. 38
    0
      Makefile
  4. 8
    2
      hosts.yml
  5. 54
    1
      lib/sslcheck.rb
  6. 11
    1
      lib/sslcheck/grade.rb
  7. 103
    64
      lib/sslcheck/server.rb
  8. 51
    27
      output/sslcheck.erb
  9. 0
    0
      output/ssllabs.erb
  10. 10
    0
      patch
  11. 4
    4
      sslcheck
  12. 19
    0
      sslcheck-alexa
  13. 6
    56
      sslcheck-all
  14. 0
    79
      test.rb

+ 5
- 1
.gitignore View File

@@ -4,4 +4,8 @@ Gemfile.lock
/html/
/.rspec
/rakefile
/output/index.html
/output/*.html
/db/*.sqlite3
/db/schema.rb
/lib/*.so
/lib/*.so.1.0.0

+ 2
- 0
Gemfile View File

@@ -7,6 +7,8 @@ gem 'tcp_timeout'
gem 'parallel'
gem 'ruby-progressbar'
gem 'logging'
gem 'activerecord'
gem 'sqlite3'

group :test do
gem 'rspec'

+ 38
- 0
Makefile View File

@@ -0,0 +1,38 @@
PWD = $(shell pwd)
export CPATH = $(PWD)/openssl/include
export LIBRARY_PATH = $(PWD)/openssl
OPENSSL_VERSION = OpenSSL_1_0_1j
RUBY_VERSION = 2.1.5
RUBY_OPENSSL_EXT_DIR = ruby-$(RUBY_VERSION)/ext/openssl

all: lib/libssl.so.1.0.0 lib/libcrypto.so.1.0.0 lib/openssl.so

clean:
rm -rf ruby-$(RUBY_VERSION) openssl

openssl:
git clone https://github.com/openssl/openssl -b $(OPENSSL_VERSION)

openssl/Makefile: openssl
cd openssl; ./config shared

openssl/libssl.so: openssl/Makefile
cd openssl; $(MAKE) depend all

lib/%.so.1.0.0: openssl/%.so
cp $^ $@

ruby-$(RUBY_VERSION):
wget http://cache.ruby-lang.org/pub/ruby/2.1/ruby-$(RUBY_VERSION).tar.gz
tar xf ruby-$(RUBY_VERSION).tar.gz
rm -f ruby-$(RUBY_VERSION).tar.gz

$(RUBY_OPENSSL_EXT_DIR)/Makefile: ruby-$(RUBY_VERSION)
cd $(RUBY_OPENSSL_EXT_DIR); ruby extconf.rb
patch $@ patch

$(RUBY_OPENSSL_EXT_DIR)/openssl.so: $(RUBY_OPENSSL_EXT_DIR)/Makefile
cd $(RUBY_OPENSSL_EXT_DIR); $(MAKE); $(MAKE) install

lib/openssl.so: $(RUBY_OPENSSL_EXT_DIR)/openssl.so
cp $< $@

+ 8
- 2
hosts.yml View File

@@ -15,11 +15,13 @@
- description: Banques en ligne
hostnames:
- www.labanquepostale.fr
- www.labanquepostale-cartesprepayees.fr
- www.secure.bnpparibas.net
- www.axabanque.fr
- www.fortuneo.fr
- www.ca-paris.fr
- www.credit-cooperatif.coop
- www.coopanet.com
- www.cic.fr
- particuliers.societegenerale.fr
- espaceclient.groupama.fr
@@ -36,7 +38,7 @@
- www.ca-paris.fr
- www.ca-cotesdarmor.fr
- secure.ingdirect.fr
- banque-accord.fr
- www.banque-accord.fr
- espace-client-secure.banque-casino.fr
- bforbank.com
- hellobank.fr
@@ -46,9 +48,9 @@
- zimbra.free.fr
- webmail.numericable.fr
- webmail.orange-business.com
- messagerie.orange.fr
- webmail.gandi.net
- messagerie.sfr.fr
- webmail.aliceadsl.fr
- webmail.online.net
- wmail.amen.fr
- webmail.ovh.com
@@ -67,6 +69,10 @@
- mon.service-public.fr
- www.correspondants.cnil.fr
- sso.quechoisir.org
- connexion.mon.service-public.fr
- mon.rsi.fr
- jedeclare.com
- net-entreprises.fr
- description: Sites de commerce en ligne
hostnames:
- signin.ebay.fr

+ 54
- 1
lib/sslcheck.rb View File

@@ -1,6 +1,59 @@
require 'sslcheck/ssllabs/api'
require 'erb'
require 'logging'
require 'parallel'
require 'thread'

module SSLCheck
module SSLLabs
autoload :API, 'sslcheck/ssllabs/api'
end
autoload :Server, 'sslcheck/server'
autoload :Grade, 'sslcheck/grade'

@@log = Logging.logger[SSLCheck]

def self.grade(hostname, port=443)
timeout 600 do
Grade.new Server.new hostname, port
end
rescue Exception => e
@@log.error { "Error during #{hostname}:#{port} analysis : #{e}" }
NoSslTlsGrade.new NoSslTlsServer.new hostname, port
end

def self.analyze(hosts, output)
results = {}
semaphore = Mutex.new
Parallel.each hosts, progress: 'Testing', in_threads: 10 do |description, host|
result = SSLCheck.grade host.strip
semaphore.synchronize do
if results.include? description
results[description] << result
else
results[description] = [result]
end
end
end

results.each do |d, _|
results[d].sort! do |a, b|
cmp = score(a) <=> score(b)
if cmp == 0
cmp = a.score <=> b.score
if cmp == 0
cmp = a.server.hostname <=> b.server.hostname
end
end
cmp
end
end

File.write "output/#{output}.html", ERB.new(File.read('output/sslcheck.erb')).result(binding)
end

private
SCORES = %w(A+ A A- B C D E F T M X)
def self.score(a)
SCORES.index a.grade
end
end

+ 11
- 1
lib/sslcheck/grade.rb View File

@@ -1,6 +1,16 @@
require 'timeout'

module SSLCheck
class NoSslTlsGrade
attr_reader :server, :score, :grade

def initialize(server)
@server, @score, @grade = server, -1, 'X'
end
end

class Grade
attr_reader :server, :score, :grade, :warning, :good
attr_reader :server, :score, :grade, :warning, :success

def initialize(server)
@server = server

+ 103
- 64
lib/sslcheck/server.rb View File

@@ -5,12 +5,23 @@ require 'parallel'
require 'tcp_timeout'

module SSLCheck
class NoSslTlsServer
attr_reader :hostname, :port

def initialize(hostname, port=443)
@hostname, @port = hostname, port
end
end

class Server
TCP_TIMEOUT = 60
SSL_TIMEOUT = 2*TCP_TIMEOUT
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
class Timeout < Exception; end
class ConnectionError < Exception; end

attr_reader :hostname, :port, :prefered_ciphers, :cert, :hsts

@@ -19,12 +30,12 @@ module SSLCheck
@hostname = hostname
@port = port
@methods = methods
@log.error { "Check for #{hostname} (#{port})"}

@log.error { "Begin analysis" }
extract_cert
fetch_prefered_ciphers
check_supported_cipher
fetch_hsts
@log.error { "End analysis" }
end

def supported_methods
@@ -33,18 +44,32 @@ module SSLCheck
{worst: worst, best: best}
end

def key_size
def key
key = @cert.public_key
case key
when OpenSSL::PKey::RSA then
key.n.num_bits
[:rsa, key.n.num_bits]
when OpenSSL::PKey::DSA then
key.p.num_bits
[:dsa, key.p.num_bits]
when OpenSSL::PKey::EC then
key.group.degree
[:ecc, key.group.degree]
end
end

def key_size
type, size = self.key
if type == :ecc
size = case size
when 160 then 1024
when 224 then 2048
when 256 then 3072
when 384 then 7680
when 521 then 15360
end
end
size
end

def cipher_size
cipher_strengths = supported_ciphers.collect { |c| c[2] }.uniq.sort
worst, best = cipher_strengths.first, cipher_strengths.last
@@ -60,9 +85,9 @@ module SSLCheck
end

{
md2: %w(md2WithRSAEncryption),
md5: %w(md5WithRSAEncryption md5WithRSA),
sha1: %w(sha1WithRSAEncryption sha1WithRSA dsaWithSHA1 dsaWithSHA1_2 ecdsa_with_SHA1)
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?
@@ -72,12 +97,12 @@ module SSLCheck
end

{
md5: %w(MD5),
sha1: %w(SHA),
md5: %w(MD5),
sha1: %w(SHA),

rc4: %w(RC4),
des3: %w(3DES DES-CBC3),
des: %w(DES-CBC)
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}?
@@ -129,8 +154,52 @@ module SSLCheck
end

private
def ssl_client(method = nil, ciphers = nil, &block)
ssl_context = method.nil? ? OpenSSL::SSL::SSLContext.new : OpenSSL::SSL::SSLContext.new(method)
def connect(family, host, port, &block)
socket = Socket.new family, Socket::SOCK_STREAM
sockaddr = Socket.sockaddr_in port, host
@log.debug { "Connecting to #{host}:#{port}" }
begin
status = socket.connect_nonblock sockaddr
@log.debug { "Connecting to #{host}:#{port} status : #{status}" }
raise ConnectionError.new status unless status == 0
@log.debug { "Connected to #{host}:#{port}" }
block_given? ? block.call(socket) : nil
rescue IO::WaitReadable
@log.debug { "Waiting for read to #{host}:#{port}" }
raise Timeout.new unless IO.select [socket], nil, nil, TCP_TIMEOUT
retry
rescue IO::WaitWritable
@log.debug { "Waiting for write to #{host}:#{port}" }
raise Timeout.new unless IO.select nil, [socket], nil, TCP_TIMEOUT
retry
ensure
socket.close
end
end

def ssl_connect(socket, context, method, &block)
ssl_socket = OpenSSL::SSL::SSLSocket.new socket, context
ssl_socket.hostname = @hostname unless method == :SSLv2
@log.debug { "SSL connecting to #{@hostname}:#{@port}" }
begin
ssl_socket.connect_nonblock
@log.debug { "SSL connected to #{@hostname}:#{@port}" }
return block_given? ? block.call(ssl_socket) : nil
rescue IO::WaitReadable
@log.debug { "Waiting for SSL read to #{@hostname}:#{@port}" }
raise Timeout.new unless IO.select [socket], nil, nil, SSL_TIMEOUT
retry
rescue IO::WaitWritable
@log.debug { "Waiting for SSL write to #{@hostname}:#{@port}" }
raise Timeout.new unless IO.select nil, [socket], nil, SSL_TIMEOUT
retry
ensure
ssl_socket.close
end
end

def ssl_client(method, ciphers = nil, &block)
ssl_context = OpenSSL::SSL::SSLContext.new method
ssl_context.ciphers = ciphers if ciphers
@log.debug { "Try #{method} connection with #{ciphers}" }

@@ -144,47 +213,15 @@ module SSLCheck
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}" }
connect family, addr[3], @port do |socket|
ssl_connect socket, ssl_context, method do |ssl_socket|
return block_given? ? block.call(ssl_socket) : nil
end
ensure
@log.debug { "Closing connection to #{addr}:#{@port}" }
socket.close
end
end
end

@log.debug { "No TLS available on #{@hostname}" }
@log.debug { "No SSL available on #{@hostname}" }
raise CipherNotAvailable.new
end

@@ -193,9 +230,10 @@ module SSLCheck
next unless SUPPORTED_METHODS.include? method
begin
@cert = ssl_client(method) { |s| s.peer_cert }
@log.warn { "Certificate #{@cert.subject}"}
@log.warn { "Certificate #{@cert.subject}" }
break
rescue CipherNotAvailable
rescue Exception => e
@log.info { "Method #{method} not supported : #{e}" }
end
end
raise TLSNotAvailableException.new unless @cert
@@ -203,10 +241,10 @@ module SSLCheck

def prefered_cipher(method)
cipher = ssl_client(method, %w(ALL:COMPLEMENTOFALL)) { |s| s.cipher }
@log.warn { "Prefered cipher for #{method} : #{cipher[0]}"}
@log.warn { "Prefered cipher for #{method} : #{cipher[0]}" }
cipher
rescue CipherNotAvailable => e
@log.info { "Method #{method} not supported : #{e}"}
rescue Exception => e
@log.info { "Method #{method} not supported : #{e}" }
nil
end

@@ -216,6 +254,7 @@ module SSLCheck
next unless SUPPORTED_METHODS.include? method
@prefered_ciphers[method] = prefered_cipher method
end
raise TLSNotAvailableException.new unless @prefered_ciphers.any? { |_, c| !c.nil? }
end

def available_ciphers(method)
@@ -224,10 +263,10 @@ module SSLCheck

def supported_cipher?(method, cipher)
ssl_client method, [cipher]
@log.warn { "Verify #{method} / #{cipher[0]} : OK"}
@log.warn { "Verify #{method} / #{cipher[0]} : OK" }
true
rescue TLSNotAvailableException, CipherNotAvailable => e
@log.debug { "Verify #{method} / #{cipher[0]} : NOK (#{e}"}
rescue Exception => e
@log.info { "Verify #{method} / #{cipher[0]} : NOK (#{e})" }
false
end

@@ -247,10 +286,10 @@ module SSLCheck
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}
response = HTTParty.head "https://#{@hostname}#{port}/", {follow_redirects: false, verify: false, ssl_version: method, timeout: SSL_TIMEOUT}
break
rescue
@log.debug { "#{method} not supported" }
rescue Exception => e
@log.debug { "#{method} not supported : #{e}" }
end
end


index2.erb → output/sslcheck.erb View File

@@ -14,6 +14,15 @@
td {
text-align: center;
}

.critical {
background-color: #000;
color: #fff;
}

td.critical:hover {
background-color: #333 !important;
}
</style>
</head>
<body>
@@ -28,21 +37,22 @@
unless first
%>
<tr>
<th colspan="16">&nbsp;</th>
<th colspan="17">&nbsp;</th>
</tr>
<%
end
first = false
%>
<tr>
<th colspan="16" id="<%= r[0] %>"><%= r[0] %></th>
<th colspan="17" 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="danger">SSL v2</td>
<td class="danger">SSL v3</td>
<td class="success">TLS</td>
<td class="success">TLS 1.2</td>
<td class="success">TLS only</td>
@@ -57,12 +67,6 @@
</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 %>">
@@ -70,66 +74,85 @@
<%= s.hostname %>
</a>
</th>
<% if s.is_a? SSLCheck::NoSslTlsServer %>
<td class="critical" colspan="16">
No SSL/TLS
</td>
<%
else
rank_color = case n.grade
when 'A+' then :info
when 'A', 'A-' then :success
when 'B', 'C' then :warning
else :danger
end
%>
<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 ? '☹' : '☺' %>)
<% type, size = s.key %>
<%= "#{size} (#{type.to_s.upcase})" %>
<span class="sr-only">(<%= s.key_size < 2048 ? '☹' : '☺' %>)</span>
</td>
<% cipher_size = s.cipher_size[:worst] %>
<td class="<%= cipher_size < 112 ? :danger : cipher_size < 128 ? :warning : :success %>">
<%= cipher_size %>
(<%= cipher_size < 128 ? '☹' : '☺' %>)
<span class="sr-only">(<%= cipher_size < 128 ? '☹' : '☺' %>)</span>
</td>
<td class="<%= s.sslv2? ? :danger : :success %>">
<%= s.sslv2? ? '✓' : '✗' %>
<span class="sr-only">(<%= s.sslv2? ? '☹' : '☺' %>)</span>
</td>
<td class="<%= s.ssl? ? :danger : :success %>">
<%= s.ssl? ? '✓' : '✗' %>
(<%= s.ssl? ? '☹' : '☺' %>)
<td class="<%= s.sslv3? ? :danger : :success %>">
<%= s.sslv3? ? '✓' : '✗' %>
<span class="sr-only">(<%= s.sslv3? ? '☹' : '☺' %>)</span>
</td>
<td class="<%= s.tls? ? :success : :danger %>">
<%= s.tls? ? '✓' : '✗' %>
(<%= s.tls? ? '☺' : '☹' %>)
<span class="sr-only">(<%= s.tls? ? '☺' : '☹' %>)</span>
</td>
<td class="<%= s.tlsv1_2? ? :success : :danger %>">
<%= s.tlsv1_2? ? '✓' : '✗' %>
(<%= s.tlsv1_2? ? '☺' : '☹' %>)
<span class="sr-only">(<%= s.tlsv1_2? ? '☺' : '☹' %>)</span>
</td>
<td class="<%= s.tls_only? ? :success : :danger %>">
<%= s.tls_only? ? '✓' : '✗' %>
(<%= s.tls_only? ? '☺' : '☹' %>)
<span class="sr-only">(<%= s.tls_only? ? '☺' : '☹' %>)</span>
</td>
<td class="<%= s.sha1_sig? ? :danger : :success %>">
<%= s.sha1_sig? ? '✓' : '✗' %>
(<%= s.sha1_sig? ? '☹' : '☺' %>)
<span class="sr-only">(<%= s.sha1_sig? ? '☹' : '☺' %>)</span>
</td>
<td class="<%= s.rc4? ? :danger : :success %>">
<%= s.rc4? ? '✓' : '✗' %>
(<%= s.rc4? ? '☹' : '☺' %>)
<span class="sr-only">(<%= s.rc4? ? '☹' : '☺' %>)</span>
</td>
<td class="<%= s.any_des? ? :danger : :success %>">
<%= s.any_des? ? '✓' : '✗' %>
(<%= s.any_des? ? '☹' : '☺' %>)
<span class="sr-only">(<%= s.any_des? ? '☹' : '☺' %>)</span>
</td>
<td class="<%= s.md5? ? :danger : :success %>">
<%= s.md5? ? '✓' : '✗' %>
(<%= s.md5? ? '☹' : '☺' %>)
<span class="sr-only">(<%= s.md5? ? '☹' : '☺' %>)</span>
</td>
<td class="<%= s.pfs? ? :success : :danger %>">
<%= s.pfs? ? '✓' : '✗' %>
(<%= s.pfs? ? '☺' : '☹' %>)
<span class="sr-only">(<%= s.pfs? ? '☺' : '☹' %>)</span>
</td>
<td class="<%= s.pfs_only? ? :success : :danger %>">
<%= s.pfs_only? ? '✓' : '✗' %>
(<%= s.pfs_only? ? '☺' : '☹' %>)
<span class="sr-only">(<%= s.pfs_only? ? '☺' : '☹' %>)</span>
</td>
<td class="<%= s.hsts? ? :success : :danger %>">
<%= s.hsts? ? '✓' : '✗' %>
(<%= s.hsts? ? '☺' : '☹' %>)
<span class="sr-only">(<%= s.hsts? ? '☺' : '☹' %>)</span>
</td>
<td class="<%= s.hsts_long? ? :success : :danger %>">
<%= s.hsts_long? ? '✓' : '✗' %>
(<%= s.hsts_long? ? '☺' : '☹' %>)
<span class="sr-only">(<%= s.hsts_long? ? '☺' : '☹' %>)</span>
</td>
<% end %>
</tr>
<% end %>
<tr>
@@ -137,7 +160,8 @@
<td>Rang</td>
<td>Clef (bits)</td>
<td>Chiff. (bits)</td>
<td class="danger">SSL</td>
<td class="danger">SSL v2</td>
<td class="danger">SSL v3</td>
<td class="success">TLS</td>
<td class="success">TLS 1.2</td>
<td class="success">TLS only</td>

index.erb → output/ssllabs.erb View File


+ 10
- 0
patch View File

@@ -0,0 +1,10 @@
--- Makefile 2014-12-13 01:20:15.025576957 +0100
+++ Makefile 2014-12-13 01:26:44.801203932 +0100
@@ -60,6 +60,7 @@
sbindir = $(exec_prefix)/sbin
bindir = $(exec_prefix)/bin
archdir = $(rubyarchdir)
+top_srcdir = ../..
CC = gcc

+ 4
- 4
sslcheck View File

@@ -1,11 +1,11 @@
#!/usr/bin/env ruby
#ENV['LD_LIBRARY_PATH'] = '/home/aeris/Workspace/external/sslscan/openssl'
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 = :info
Logging.logger.root.level = :warn

server = SSLCheck::Server.new ARGV[0]
p SSLCheck::Grade.new server
p server = SSLCheck::Server.new(ARGV[0])
p grade = SSLCheck::Grade.new(server)

+ 19
- 0
sslcheck-alexa View File

@@ -0,0 +1,19 @@
#!/usr/bin/env ruby
require 'yaml'
$:.unshift 'lib'
require 'sslcheck'

Logging.logger.root.appenders = Logging.appenders.stdout
Logging.logger.root.level = :error

hosts = []
File.open('top-1m.csv', 'r') do |file|
i = 0
while line = file.gets
hosts << ['Top 100 Alexa', line.strip.split(',')[1]]
i += 1
break if i == 100
end
end

SSLCheck.analyze hosts, 'alexa'

+ 6
- 56
sslcheck-all View File

@@ -1,66 +1,16 @@
#!/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
Logging.logger.root.appenders = Logging.appenders.stdout
Logging.logger.root.level = :error

config = YAML.load_file 'hosts.yml'
results = Hash[config.collect { |c| [c['description'], []] }]


tests = []
hosts = []
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
d, hs = c['description'], c['hostnames']
hs.each { |host| hosts << [d, host] }
end

puts 'Generate results'
File.write 'output/index.html', ERB.new(File.read('index2.erb')).result(binding)
SSLCheck.analyze hosts, 'results'

+ 0
- 79
test.rb View File

@@ -1,79 +0,0 @@
#!/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…
Cancel
Save