@@ -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 |
@@ -7,6 +7,8 @@ gem 'tcp_timeout' | |||
gem 'parallel' | |||
gem 'ruby-progressbar' | |||
gem 'logging' | |||
gem 'activerecord' | |||
gem 'sqlite3' | |||
group :test do | |||
gem 'rspec' | |||
@@ -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 $< $@ |
@@ -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 | |||
@@ -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 |
@@ -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 | |||
@@ -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 | |||
@@ -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"> </th> | |||
<th colspan="17"> </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> |
@@ -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 |
@@ -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) |
@@ -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' |
@@ -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' |
@@ -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 |