Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. require 'httparty'
  2. require 'nokogiri'
  3. module SSLCheck
  4. module SSLLabs
  5. class Error < StandardError; end
  6. class WaitingError < Error; end
  7. class ServerError < Error; end
  8. class NoEncryptionError < Error; end
  9. class API
  10. include HTTParty
  11. #debug_output $stdout
  12. base_uri 'https://www.ssllabs.com/ssltest'
  13. attr_reader :hostname, :ip, :rank, :ssl, :tls, :bits, :rc4, :pfs, :hsts
  14. def initialize(hostname, debug: false)
  15. @debug = debug
  16. @hostname = hostname
  17. @ip = nil
  18. html = content hostname
  19. @rank = html.css('#rating div')[1].text.strip
  20. parse_configuration html
  21. end
  22. private
  23. def content(hostname, ip=nil)
  24. #puts "host: #{hostname}, ip: #{ip}"
  25. options = {query: {d: hostname}}
  26. options[:query][:s] = ip unless ip.nil?
  27. html = nil
  28. loop do
  29. response = self.class.get '/analyze.html', options
  30. raise ServerError, response.code unless response.success?
  31. html = Nokogiri::HTML response.body
  32. File.write File.join('html', hostname), html if @debug
  33. break if not resolving_domain? html
  34. end
  35. waiting? html
  36. html = resolve_multiple_servers html
  37. encrypted? html
  38. @hostname = html.at_css('.url').content.strip
  39. ip = html.at_css '.ip'
  40. unless ip.nil?
  41. @ip = ip.content.strip.gsub /[()]/, ''
  42. else
  43. @ip = ''
  44. end
  45. html
  46. end
  47. def waiting?(html)
  48. warning = html.at_css '#warningBox'
  49. raise WaitingError if not warning.nil? and warning.content.include? 'Please wait...'
  50. end
  51. def encrypted?(html)
  52. warning = html.at_css '#warningBox'
  53. raise NoEncryptionError if not warning.nil? and \
  54. warning.content.include? 'Assessment failed: Unable to connect to server'
  55. end
  56. def resolving_domain?(html)
  57. warning = html.at_css('#warningBox')
  58. not warning.nil? and warning.content.strip == 'Please wait... (Resolving domain names)'
  59. end
  60. def resolve_multiple_servers(html)
  61. servers = html.at_css('#multiTable')
  62. return html if servers.nil?
  63. servers.css('tr').each do |server|
  64. td = server.css('td')[4]
  65. next if td.nil?
  66. rank = td.content.strip
  67. unless rank == '-'
  68. ip = server.at_css('.ip').content
  69. html = content hostname, ip
  70. waiting? html
  71. return html
  72. end
  73. end
  74. raise NoEncryptionError
  75. end
  76. def parse_configuration(html)
  77. configuration = html.css('.reportSection')[2]
  78. parse_protocols configuration
  79. parse_handshakes configuration
  80. parse_details configuration
  81. end
  82. def parse_protocols(configuration)
  83. protocols = configuration.css('.reportTable')[0].css('tr.tableRow')
  84. @tls = true
  85. @ssl = false
  86. protocols.each do |row|
  87. cells = row.css 'td'
  88. next unless cells.size >= 2
  89. name = cells[0].content.strip
  90. value = cells[1].content.strip
  91. case name
  92. when /^TLS 1.2/ then
  93. @tls = value == 'Yes'
  94. when /^SSL 2/ then
  95. @ssl |= value != 'No'
  96. when /^SSL 3/ then
  97. @ssl |= value != 'No'
  98. end
  99. end
  100. end
  101. def parse_handshakes(configuration)
  102. @bits = nil
  103. handshakes = configuration.css('.reportTable')[2].css('td.tableRight')
  104. handshakes.each do |cell|
  105. value = cell.content.strip
  106. begin
  107. i = Integer value
  108. @bits = @bits.nil? ? i : [@bits, i].min
  109. rescue
  110. end
  111. end
  112. end
  113. def parse_details(configuration)
  114. @rc4 = @pfs = @hsts = nil
  115. details = configuration.css('.reportTable')[3].css('tr.tableRow')
  116. details.each do |row|
  117. cells = row.css 'td'
  118. name = cells[0].content.strip
  119. value = cells[1].content.strip
  120. case name
  121. when 'RC4' then
  122. @rc4 = value != 'No'
  123. when 'Forward Secrecy' then
  124. @pfs = value == 'Yes (with most browsers)   ROBUST (more info)'
  125. when 'Strict Transport Security (HSTS)' then
  126. @hsts = value.start_with? 'Yes'
  127. end
  128. end
  129. end
  130. end
  131. end
  132. end