You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
148 lines
3.8 KiB
148 lines
3.8 KiB
require 'httparty'
|
|
require 'nokogiri'
|
|
|
|
module SSLCheck
|
|
module SSLLabs
|
|
class Error < StandardError; end
|
|
class WaitingError < Error; end
|
|
class ServerError < Error; end
|
|
class NoEncryptionError < Error; end
|
|
|
|
class API
|
|
include HTTParty
|
|
#debug_output $stdout
|
|
base_uri 'https://www.ssllabs.com/ssltest'
|
|
|
|
attr_reader :hostname, :ip, :rank, :ssl, :tls, :bits, :rc4, :pfs, :hsts
|
|
|
|
def initialize(hostname, debug: false)
|
|
@debug = debug
|
|
@hostname = hostname
|
|
@ip = nil
|
|
html = content hostname
|
|
@rank = html.css('#rating div')[1].text.strip
|
|
parse_configuration html
|
|
end
|
|
|
|
private
|
|
def content(hostname, ip=nil)
|
|
#puts "host: #{hostname}, ip: #{ip}"
|
|
options = {query: {d: hostname}}
|
|
options[:query][:s] = ip unless ip.nil?
|
|
|
|
html = nil
|
|
loop do
|
|
response = self.class.get '/analyze.html', options
|
|
raise ServerError, response.code unless response.success?
|
|
html = Nokogiri::HTML response.body
|
|
File.write File.join('html', hostname), html if @debug
|
|
break if not resolving_domain? html
|
|
end
|
|
waiting? html
|
|
|
|
html = resolve_multiple_servers html
|
|
encrypted? html
|
|
|
|
@hostname = html.at_css('.url').content.strip
|
|
ip = html.at_css '.ip'
|
|
unless ip.nil?
|
|
@ip = ip.content.strip.gsub /[()]/, ''
|
|
else
|
|
@ip = ''
|
|
end
|
|
html
|
|
end
|
|
|
|
def waiting?(html)
|
|
warning = html.at_css '#warningBox'
|
|
raise WaitingError if not warning.nil? and warning.content.include? 'Please wait...'
|
|
end
|
|
|
|
def encrypted?(html)
|
|
warning = html.at_css '#warningBox'
|
|
raise NoEncryptionError if not warning.nil? and \
|
|
warning.content.include? 'Assessment failed: Unable to connect to server'
|
|
end
|
|
|
|
def resolving_domain?(html)
|
|
warning = html.at_css('#warningBox')
|
|
not warning.nil? and warning.content.strip == 'Please wait... (Resolving domain names)'
|
|
end
|
|
|
|
def resolve_multiple_servers(html)
|
|
servers = html.at_css('#multiTable')
|
|
return html if servers.nil?
|
|
servers.css('tr').each do |server|
|
|
td = server.css('td')[4]
|
|
next if td.nil?
|
|
rank = td.content.strip
|
|
unless rank == '-'
|
|
ip = server.at_css('.ip').content
|
|
html = content hostname, ip
|
|
waiting? html
|
|
return html
|
|
end
|
|
end
|
|
raise NoEncryptionError
|
|
end
|
|
|
|
def parse_configuration(html)
|
|
configuration = html.css('.reportSection')[2]
|
|
parse_protocols configuration
|
|
parse_handshakes configuration
|
|
parse_details configuration
|
|
end
|
|
|
|
def parse_protocols(configuration)
|
|
protocols = configuration.css('.reportTable')[0].css('tr.tableRow')
|
|
@tls = true
|
|
@ssl = false
|
|
protocols.each do |row|
|
|
cells = row.css 'td'
|
|
next unless cells.size >= 2
|
|
name = cells[0].content.strip
|
|
value = cells[1].content.strip
|
|
case name
|
|
when /^TLS 1.2/ then
|
|
@tls = value == 'Yes'
|
|
when /^SSL 2/ then
|
|
@ssl |= value != 'No'
|
|
when /^SSL 3/ then
|
|
@ssl |= value != 'No'
|
|
end
|
|
end
|
|
end
|
|
|
|
def parse_handshakes(configuration)
|
|
@bits = nil
|
|
handshakes = configuration.css('.reportTable')[2].css('td.tableRight')
|
|
handshakes.each do |cell|
|
|
value = cell.content.strip
|
|
begin
|
|
i = Integer value
|
|
@bits = @bits.nil? ? i : [@bits, i].min
|
|
rescue
|
|
end
|
|
end
|
|
end
|
|
|
|
def parse_details(configuration)
|
|
@rc4 = @pfs = @hsts = nil
|
|
details = configuration.css('.reportTable')[3].css('tr.tableRow')
|
|
details.each do |row|
|
|
cells = row.css 'td'
|
|
name = cells[0].content.strip
|
|
value = cells[1].content.strip
|
|
case name
|
|
when 'RC4' then
|
|
@rc4 = value != 'No'
|
|
when 'Forward Secrecy' then
|
|
@pfs = value == 'Yes (with most browsers) ROBUST (more info)'
|
|
when 'Strict Transport Security (HSTS)' then
|
|
@hsts = value.start_with? 'Yes'
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|