Kaynağa Gözat

Prepare for stuff outside HTTPS

master
Nicolas Vinot 4 yıl önce
ebeveyn
işleme
777d220b28

+ 19
- 0
bin/check_https Dosyayı Görüntüle

@@ -0,0 +1,19 @@
1
+#!/usr/bin/env ruby
2
+$:.unshift 'lib'
3
+require 'logging'
4
+require 'cryptcheck'
5
+
6
+name = ARGV[0] || 'index'
7
+file = ::File.join 'output', "#{name}.yml"
8
+
9
+if ::File.exist? file
10
+	::CryptCheck::Tls::Https.analyze_from_file "output/#{name}.yml", "output/#{name}.html"
11
+else
12
+	::Logging.logger.root.appenders = ::Logging.appenders.stdout
13
+	::Logging.logger.root.level = :warn
14
+
15
+	server = ::CryptCheck::Tls::Https::Server.new(ARGV[0], ARGV[1] || 443)
16
+	p grade = ::CryptCheck::Tls::Https::Grade.new(server)
17
+end
18
+
19
+

+ 19
- 0
bin/check_https_alexa Dosyayı Görüntüle

@@ -0,0 +1,19 @@
1
+#!/usr/bin/env ruby
2
+$:.unshift 'lib'
3
+require 'logging'
4
+require 'cryptcheck'
5
+
6
+::Logging.logger.root.appenders = ::Logging.appenders.stdout
7
+::Logging.logger.root.level = :error
8
+
9
+hosts = []
10
+::File.open('top-1m.csv', 'r') do |file|
11
+	i = 0
12
+	while line = file.gets
13
+		hosts << ['Top 100 Alexa', line.strip.split(',')[1]]
14
+		i += 1
15
+		break if i == 100
16
+	end
17
+end
18
+
19
+::CryptCheck::Tls::Https.analyze hosts, 'output/alexa.html'

+ 12
- 0
lib/cryptcheck.rb Dosyayı Görüntüle

@@ -0,0 +1,12 @@
1
+module CryptCheck
2
+	module Tls
3
+		autoload :Server, 'cryptcheck/tls/server'
4
+		autoload :Grade, 'cryptcheck/tls/grade'
5
+		autoload :Https, 'cryptcheck/tls/https'
6
+
7
+		module Https
8
+			autoload :Server, 'cryptcheck/tls/https/server'
9
+			autoload :Grade, 'cryptcheck/tls/https/grade'
10
+		end
11
+	end
12
+end

+ 111
- 0
lib/cryptcheck/tls/grade.rb Dosyayı Görüntüle

@@ -0,0 +1,111 @@
1
+module CryptCheck
2
+	module Tls
3
+		class TlsNotSupportedGrade
4
+			attr_reader :server, :score, :grade
5
+			def initialize(server)
6
+				@server, @score, @grade = server, -1, 'X'
7
+			end
8
+		end
9
+
10
+		class Grade
11
+			attr_reader :server, :score, :grade, :warning, :success
12
+
13
+			def initialize(server)
14
+				@server = server
15
+				protocol_score
16
+				key_exchange_score
17
+				cipher_strengths_score
18
+				@score = @protocol_score*0.3 +  @key_exchange_score*0.3 + @cipher_strengths_score*0.4
19
+				calculate_grade
20
+				warning
21
+				success
22
+				perfect
23
+			end
24
+
25
+			private
26
+			def calculate_grade
27
+				@grade = case @score
28
+					         when 0...20 then 'F'
29
+					         when 20...35 then 'E'
30
+					         when 35...50 then 'D'
31
+					         when 50...65 then 'C'
32
+					         when 65...80 then 'B'
33
+					         else 'A'
34
+				         end
35
+
36
+				@grade = [@grade, 'B'].max if !@server.tlsv1_2? or @server.key_size < 2048
37
+				@grade = [@grade, 'C'].max if @server.des3?
38
+				@grade = [@grade, 'E'].max if @server.rc4? or @server.des?
39
+				@grade = [@grade, 'F'].max if @server.ssl? or @server.key_size < 1024
40
+
41
+				@grade = 'M' unless @server.cert_valid
42
+				@grade = 'T' unless @server.cert_trusted
43
+			end
44
+
45
+			def warning
46
+				@warning = []
47
+
48
+				@warning << :md5_sig if @server.md5_sig?
49
+				@warning << :sha1_sig if @server.sha1_sig?
50
+
51
+				@warning << :md5 if @server.md5?
52
+				#@warning << :sha1 if @server.sha1?
53
+
54
+				@warning << :rc4 if @server.rc4?
55
+				@warning << :des if @server.des?
56
+				@warning << :des3 if @server.des3?
57
+			end
58
+
59
+			def success
60
+				@success = []
61
+				@success << :pfs if @server.pfs_only?
62
+			end
63
+
64
+			ALL_WARNING = %i(md5_sig md5 rc4 des)
65
+			def all_warning
66
+				ALL_WARNING
67
+			end
68
+			ALL_SUCCESS = %i(pfs hsts hsts_long)
69
+			def all_success
70
+				ALL_SUCCESS
71
+			end
72
+
73
+			def perfect
74
+				@grade = 'A+' if @grade == 'A' and (all_warning & @warning).empty? and (all_success & @success) == all_success
75
+			end
76
+
77
+			METHODS_SCORES = { SSLv2: 0, SSLv3: 80, TLSv1: 90, TLSv1_1: 95, TLSv1_2: 100 }
78
+			def protocol_score
79
+				methods = @server.supported_methods
80
+				worst, best = methods[:worst], methods[:best]
81
+				@protocol_score = (METHODS_SCORES[worst] + METHODS_SCORES[best]) / 2
82
+			end
83
+
84
+			def key_exchange_score
85
+				@key_exchange_score = case @server.key_size
86
+					                      when 0 then 0
87
+					                      when 0...512 then 20
88
+					                      when 512...1024 then 40
89
+					                      when 1024...2048 then 80
90
+					                      when 2048...4096 then 90
91
+					                      else 100
92
+				                      end
93
+			end
94
+
95
+			def cipher_strength_score(cipher_strength)
96
+				case cipher_strength
97
+					when 0 then 0
98
+					when 0...128 then 20
99
+					when 128...256 then 80
100
+					else 100
101
+				end
102
+			end
103
+
104
+			def cipher_strengths_score
105
+				strength = @server.cipher_size
106
+				worst, best = strength[:min], strength[:max]
107
+				@cipher_strengths_score = (cipher_strength_score(worst) + cipher_strength_score(best)) / 2
108
+			end
109
+		end
110
+	end
111
+end

+ 73
- 0
lib/cryptcheck/tls/https.rb Dosyayı Görüntüle

@@ -0,0 +1,73 @@
1
+require 'erb'
2
+require 'logging'
3
+require 'parallel'
4
+
5
+module CryptCheck
6
+	module Tls
7
+		module Https
8
+			MAX_ANALYSIS_DURATION = 600
9
+			PARALLEL_ANALYSIS = 10
10
+			@@log = ::Logging.logger[Https]
11
+
12
+			def self.grade(hostname, port=443)
13
+				timeout MAX_ANALYSIS_DURATION do
14
+					Grade.new Server.new hostname, port
15
+				end
16
+			rescue ::Exception => e
17
+				@@log.error { "Error during #{hostname}:#{port} analysis : #{e}" }
18
+				TlsNotSupportedGrade.new TlsNotSupportedServer.new hostname, port
19
+			end
20
+
21
+			def self.analyze(hosts, output, groups = nil)
22
+				results = {}
23
+				semaphore = ::Mutex.new
24
+				::Parallel.each hosts, progress: 'Analysing', in_threads: PARALLEL_ANALYSIS,
25
+				              finish: lambda { |item, _, _| puts item[1] } do |description, host|
26
+					              result = grade host.strip
27
+					              semaphore.synchronize do
28
+						              if results.include? description
29
+							              results[description] << result
30
+						              else
31
+							              results[description] = [result]
32
+						              end
33
+					              end
34
+				              end
35
+
36
+				results = ::Hash[groups.collect { |g| [g, results[g]] }] if groups
37
+
38
+				results.each do |d, _|
39
+					results[d].sort! do |a, b|
40
+						cmp = score(a) <=> score(b)
41
+						if cmp == 0
42
+							cmp = b.score <=> a.score
43
+							if cmp == 0
44
+								cmp = a.server.hostname <=> b.server.hostname
45
+							end
46
+						end
47
+						cmp
48
+					end
49
+				end
50
+
51
+				::File.write output, ::ERB.new(::File.read('output/https.erb')).result(binding)
52
+			end
53
+
54
+			def self.analyze_from_file(file, output)
55
+				config = ::YAML.load_file file
56
+				hosts = []
57
+				groups = []
58
+				config.each do |c|
59
+					d, hs = c['description'], c['hostnames']
60
+					groups << d
61
+					hs.each { |host| hosts << [d, host] }
62
+				end
63
+				self.analyze hosts, output, groups
64
+			end
65
+
66
+			private
67
+			SCORES = %w(A+ A A- B C D E F T M X)
68
+			def self.score(a)
69
+				SCORES.index a.grade
70
+			end
71
+		end
72
+	end
73
+end

+ 18
- 0
lib/cryptcheck/tls/https/grade.rb Dosyayı Görüntüle

@@ -0,0 +1,18 @@
1
+module CryptCheck
2
+	module Tls
3
+		module Https
4
+			class Grade < Tls::Grade
5
+				private
6
+				def success
7
+					super
8
+					@success << :hsts if @server.hsts?
9
+					@success << :hsts_long if @server.hsts_long?
10
+				end
11
+
12
+				def all_success
13
+					super + %i(hsts hsts_long)
14
+				end
15
+			end
16
+		end
17
+	end
18
+end

+ 54
- 0
lib/cryptcheck/tls/https/server.rb Dosyayı Görüntüle

@@ -0,0 +1,54 @@
1
+require 'socket'
2
+require 'openssl'
3
+require 'httparty'
4
+
5
+module CryptCheck
6
+	module Tls
7
+		module Https
8
+			class Server < Tls::Server
9
+				attr_reader :hsts
10
+
11
+				def initialize(hostname, port=443, methods: EXISTING_METHODS)
12
+					super
13
+					fetch_hsts
14
+				end
15
+
16
+				def fetch_hsts
17
+					port = @port == 443 ? '' : ":#{@port}"
18
+
19
+					response = nil
20
+					@methods.each do |method|
21
+						begin
22
+							next unless SUPPORTED_METHODS.include? method
23
+							@log.debug { "Check HSTS with #{method}" }
24
+							response = ::HTTParty.head "https://#{@hostname}#{port}/", { follow_redirects: false, verify: false, ssl_version: method, timeout: SSL_TIMEOUT }
25
+							break
26
+						rescue Exception => e
27
+							@log.debug { "#{method} not supported : #{e}" }
28
+						end
29
+					end
30
+
31
+					if response and header = response.headers['strict-transport-security']
32
+						name, value = header.split '='
33
+						if name == 'max-age'
34
+							@hsts = value.to_i
35
+							@log.info { "HSTS : #{@hsts}" }
36
+							return
37
+						end
38
+					end
39
+
40
+					@log.info { 'No HSTS' }
41
+					@hsts = nil
42
+				end
43
+
44
+				def hsts?
45
+					!@hsts.nil?
46
+				end
47
+
48
+				def hsts_long?
49
+					hsts? and @hsts >= 6*30*24*60*60
50
+				end
51
+			end
52
+		end
53
+	end
54
+end

+ 309
- 0
lib/cryptcheck/tls/server.rb Dosyayı Görüntüle

@@ -0,0 +1,309 @@
1
+require 'socket'
2
+require 'openssl'
3
+require 'httparty'
4
+
5
+module CryptCheck
6
+	module Tls
7
+		class TlsNotSupportedServer
8
+			attr_reader :hostname, :port
9
+
10
+			def initialize(hostname, port)
11
+				@hostname, @port = hostname, port
12
+			end
13
+		end
14
+
15
+		class Server
16
+			TCP_TIMEOUT = 10
17
+			SSL_TIMEOUT = 2*TCP_TIMEOUT
18
+			EXISTING_METHODS = %i(TLSv1_2 TLSv1_1 TLSv1 SSLv3 SSLv2)
19
+			SUPPORTED_METHODS = ::OpenSSL::SSL::SSLContext::METHODS
20
+			class TLSException < ::Exception
21
+			end
22
+			class TLSNotAvailableException < TLSException
23
+			end
24
+			class CipherNotAvailable < TLSException
25
+			end
26
+			class Timeout < TLSException
27
+			end
28
+			class TLSTimeout < TLSException
29
+			end
30
+			class ConnectionError < TLSException
31
+			end
32
+
33
+			attr_reader :hostname, :port, :prefered_ciphers, :cert, :cert_valid, :cert_trusted
34
+
35
+			def initialize(hostname, port, methods: EXISTING_METHODS)
36
+				@log = Logging.logger[hostname]
37
+				@hostname = hostname
38
+				@port = port
39
+				@methods = methods
40
+				@log.error { "Begin analysis" }
41
+				extract_cert
42
+				fetch_prefered_ciphers
43
+				check_supported_cipher
44
+				fetch_hsts
45
+				@log.error { "End analysis" }
46
+			end
47
+
48
+			def supported_methods
49
+				worst = EXISTING_METHODS.find { |method| !@prefered_ciphers[method].nil? }
50
+				best = EXISTING_METHODS.reverse.find { |method| !@prefered_ciphers[method].nil? }
51
+				{ worst: worst, best: best }
52
+			end
53
+
54
+			def key
55
+				key = @cert.public_key
56
+				case key
57
+					when ::OpenSSL::PKey::RSA then
58
+						[:rsa, key.n.num_bits]
59
+					when ::OpenSSL::PKey::DSA then
60
+						[:dsa, key.p.num_bits]
61
+					when ::OpenSSL::PKey::EC then
62
+						[:ecc, key.group.degree]
63
+				end
64
+			end
65
+
66
+			def key_size
67
+				type, size = self.key
68
+				if type == :ecc
69
+					size = case size
70
+						       when 160 then
71
+							       1024
72
+						       when 224 then
73
+							       2048
74
+						       when 256 then
75
+							       3072
76
+						       when 384 then
77
+							       7680
78
+						       when 521 then
79
+							       15360
80
+					       end
81
+				end
82
+				size
83
+			end
84
+
85
+			def cipher_size
86
+				cipher_strengths = supported_ciphers.collect { |c| c[2] }.uniq.sort
87
+				worst, best = cipher_strengths.first, cipher_strengths.last
88
+				{ worst: worst, best: best }
89
+			end
90
+
91
+			EXISTING_METHODS.each do |method|
92
+				class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
93
+					def #{method.to_s.downcase}?
94
+						!prefered_ciphers[:#{method}].nil?
95
+					end
96
+				RUBY_EVAL
97
+			end
98
+
99
+			{
100
+					md2: %w(md2WithRSAEncryption),
101
+					md5: %w(md5WithRSAEncryption md5WithRSA),
102
+					sha1: %w(sha1WithRSAEncryption sha1WithRSA dsaWithSHA1 dsaWithSHA1_2 ecdsa_with_SHA1)
103
+			}.each do |name, signature|
104
+				class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
105
+				def #{name}_sig?
106
+					#{signature}.include? @cert.signature_algorithm
107
+				end
108
+				RUBY_EVAL
109
+			end
110
+
111
+			{
112
+					md5: %w(MD5),
113
+					sha1: %w(SHA),
114
+
115
+					rc4: %w(RC4),
116
+					des3: %w(3DES DES-CBC3),
117
+					des: %w(DES-CBC)
118
+			}.each do |name, ciphers|
119
+				class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
120
+				def #{name}?
121
+					supported_ciphers.any? { |supported| #{ciphers}.any? { |available| /(^|-)#\{available\}(-|$)/ =~ supported[0] } }
122
+				end
123
+				RUBY_EVAL
124
+			end
125
+
126
+			def ssl?
127
+				sslv2? or sslv3?
128
+			end
129
+
130
+			def tls?
131
+				tlsv1? or tlsv1_1? or tlsv1_2?
132
+			end
133
+
134
+			def tls_only?
135
+				tls? and !ssl?
136
+			end
137
+
138
+			PFS_CIPHERS = [/^DHE-RSA-/, /^DHE-DSS-/, /^ECDHE-RSA-/, /^ECDHE-ECDSA-/]
139
+
140
+			def pfs?
141
+				supported_ciphers.any? { |cipher| PFS_CIPHERS.any? { |pc| pc =~ cipher[0] } }
142
+			end
143
+
144
+			def pfs_only?
145
+				supported_ciphers.all? { |cipher| PFS_CIPHERS.any? { |pc| pc =~ cipher[0] } }
146
+			end
147
+
148
+			def supported_ciphers
149
+				@supported_ciphers.values.flatten(1).uniq
150
+			end
151
+
152
+			def supported_ciphers_by_method
153
+				@supported_ciphers
154
+			end
155
+
156
+			private
157
+			def connect(family, host, port, &block)
158
+				socket = ::Socket.new family, ::Socket::SOCK_STREAM
159
+				sockaddr = ::Socket.sockaddr_in port, host
160
+				@log.debug { "Connecting to #{host}:#{port}" }
161
+				begin
162
+					status = socket.connect_nonblock sockaddr
163
+					@log.debug { "Connecting to #{host}:#{port} status : #{status}" }
164
+					raise ConnectionError, status unless status == 0
165
+					@log.debug { "Connected to #{host}:#{port}" }
166
+					block_given? ? block.call(socket) : nil
167
+				rescue ::IO::WaitReadable
168
+					@log.debug { "Waiting for read to #{host}:#{port}" }
169
+					raise Timeout unless IO.select [socket], nil, nil, TCP_TIMEOUT
170
+					retry
171
+				rescue ::IO::WaitWritable
172
+					@log.debug { "Waiting for write to #{host}:#{port}" }
173
+					raise Timeout unless IO.select nil, [socket], nil, TCP_TIMEOUT
174
+					retry
175
+				ensure
176
+					socket.close
177
+				end
178
+			end
179
+
180
+			def ssl_connect(socket, context, method, &block)
181
+				ssl_socket = ::OpenSSL::SSL::SSLSocket.new socket, context
182
+				ssl_socket.hostname = @hostname unless method == :SSLv2
183
+				@log.debug { "SSL connecting to #{@hostname}:#{@port}" }
184
+				begin
185
+					ssl_socket.connect_nonblock
186
+					@log.debug { "SSL connected to #{@hostname}:#{@port}" }
187
+					return block_given? ? block.call(ssl_socket) : nil
188
+				rescue ::IO::WaitReadable
189
+					@log.debug { "Waiting for SSL read to #{@hostname}:#{@port}" }
190
+					raise TLSTimeout unless IO.select [socket], nil, nil, SSL_TIMEOUT
191
+					retry
192
+				rescue ::IO::WaitWritable
193
+					@log.debug { "Waiting for SSL write to #{@hostname}:#{@port}" }
194
+					raise TLSTimeout unless IO.select nil, [socket], nil, SSL_TIMEOUT
195
+					retry
196
+				rescue ::OpenSSL::SSL::SSLError => e
197
+					raise TLSException, e
198
+				ensure
199
+					ssl_socket.close
200
+				end
201
+			end
202
+
203
+			def ssl_client(method, ciphers = nil, &block)
204
+				ssl_context = ::OpenSSL::SSL::SSLContext.new method
205
+				ssl_context.ciphers = ciphers if ciphers
206
+				@log.debug { "Try #{method} connection with #{ciphers}" }
207
+
208
+				[::Socket::AF_INET, ::Socket::AF_INET6].each do |family|
209
+					@log.debug { "Try connection for family #{family}" }
210
+					addrs = begin
211
+						::Socket.getaddrinfo @hostname, nil, family, :STREAM
212
+					rescue ::SocketError => e
213
+						@log.debug { "Unable to resolv #{@hostname} : #{e}" }
214
+						next
215
+					end
216
+
217
+					addrs.each do |addr|
218
+						connect family, addr[3], @port do |socket|
219
+							ssl_connect socket, ssl_context, method do |ssl_socket|
220
+								return block_given? ? block.call(ssl_socket) : nil
221
+							end
222
+						end
223
+					end
224
+				end
225
+
226
+				@log.debug { "No SSL available on #{@hostname}" }
227
+				raise CipherNotAvailable
228
+			end
229
+
230
+			def extract_cert
231
+				@methods.each do |method|
232
+					next unless SUPPORTED_METHODS.include? method
233
+					begin
234
+						@cert, @chain = ssl_client(method) { |s| [s.peer_cert, s.peer_cert_chain] }
235
+						@log.warn { "Certificate #{@cert.subject}" }
236
+						break
237
+					rescue TLSException => e
238
+						@log.info { "Method #{method} not supported : #{e}" }
239
+					end
240
+				end
241
+				raise TLSNotAvailableException unless @cert
242
+				@cert_valid = ::OpenSSL::SSL.verify_certificate_identity @cert, @hostname
243
+				@cert_trusted = verify_trust @chain, @cert
244
+			end
245
+
246
+			def prefered_cipher(method)
247
+				cipher = ssl_client(method, %w(ALL:COMPLEMENTOFALL)) { |s| s.cipher }
248
+				@log.warn { "Prefered cipher for #{method} : #{cipher[0]}" }
249
+				cipher
250
+			rescue Exception => e
251
+				@log.info { "Method #{method} not supported : #{e}" }
252
+				nil
253
+			end
254
+
255
+			def fetch_prefered_ciphers
256
+				@prefered_ciphers = {}
257
+				@methods.each do |method|
258
+					next unless SUPPORTED_METHODS.include? method
259
+					@prefered_ciphers[method] = prefered_cipher method
260
+				end
261
+				raise TLSNotAvailableException.new unless @prefered_ciphers.any? { |_, c| !c.nil? }
262
+			end
263
+
264
+			def available_ciphers(method)
265
+				::OpenSSL::SSL::SSLContext.new(method).ciphers
266
+			end
267
+
268
+			def supported_cipher?(method, cipher)
269
+				ssl_client method, [cipher]
270
+				@log.warn { "Verify #{method} / #{cipher[0]} : OK" }
271
+				true
272
+			rescue TLSException => e
273
+				@log.info { "Verify #{method} / #{cipher[0]} : NOK (#{e})" }
274
+				false
275
+			end
276
+
277
+			def check_supported_cipher
278
+				@supported_ciphers = {}
279
+				@methods.each do |method|
280
+					next unless SUPPORTED_METHODS.include? method and @prefered_ciphers[method]
281
+					@supported_ciphers[method] = available_ciphers(method).select { |cipher| supported_cipher? method, cipher }
282
+				end
283
+			end
284
+
285
+			def verify_trust(chain, cert)
286
+				store = ::OpenSSL::X509::Store.new
287
+				store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT
288
+				%w(mozilla cacert).each do |directory|
289
+					::Dir.glob(::File.join '/usr/share/ca-certificates', directory, '*').each do |file|
290
+						::File.open file, 'r' do |file|
291
+							cert = ::OpenSSL::X509::Certificate.new file.read
292
+							begin
293
+								store.add_cert cert
294
+							rescue ::OpenSSL::X509::StoreError
295
+							end
296
+						end
297
+					end
298
+				end
299
+				chain.each do |cert|
300
+					begin
301
+						store.add_cert cert
302
+					rescue ::OpenSSL::X509::StoreError
303
+					end
304
+				end
305
+				store.verify cert
306
+			end
307
+		end
308
+	end
309
+end

+ 0
- 77
lib/sslcheck.rb Dosyayı Görüntüle

@@ -1,77 +0,0 @@
1
-require 'erb'
2
-require 'logging'
3
-require 'parallel'
4
-require 'thread'
5
-require 'yaml'
6
-
7
-module SSLCheck
8
-	module SSLLabs
9
-		autoload :API, 'sslcheck/ssllabs/api'
10
-	end
11
-	autoload :Server, 'sslcheck/server'
12
-	autoload :Grade, 'sslcheck/grade'
13
-
14
-	PARALLEL_ANALYSIS = 20
15
-	SYN_TIMEOUT = 600
16
-	@@log = Logging.logger[SSLCheck]
17
-
18
-	def self.grade(hostname, port=443)
19
-		timeout SYN_TIMEOUT do
20
-			Grade.new Server.new hostname, port
21
-		end
22
-	rescue Exception => e
23
-		@@log.error { "Error during #{hostname}:#{port} analysis : #{e}" }
24
-		NoSslTlsGrade.new NoSslTlsServer.new hostname, port
25
-	end
26
-
27
-	def self.analyze(hosts, output, groups = nil)
28
-		results = {}
29
-		semaphore = Mutex.new
30
-		Parallel.each hosts, progress: 'Analysing', in_threads: PARALLEL_ANALYSIS,
31
-			finish: lambda { |item, _, _| puts item[1] } do |description, host|
32
-			result = SSLCheck.grade host.strip
33
-			semaphore.synchronize do
34
-				if results.include? description
35
-					results[description] << result
36
-				else
37
-					results[description] = [result]
38
-				end
39
-			end
40
-		end
41
-
42
-		results = Hash[groups.collect { |g| [g, results[g]] }] if groups
43
-
44
-		results.each do |d, _|
45
-			results[d].sort! do |a, b|
46
-				cmp = score(a) <=> score(b)
47
-				if cmp == 0
48
-					cmp = a.score <=> b.score
49
-					if cmp == 0
50
-						cmp = a.server.hostname <=> b.server.hostname
51
-					end
52
-				end
53
-				cmp
54
-			end
55
-		end
56
-
57
-		File.write output, ERB.new(File.read('output/sslcheck.erb')).result(binding)
58
-	end
59
-
60
-	def self.analyze_from_file(file, output)
61
-		config = YAML.load_file file
62
-		hosts = []
63
-		groups = []
64
-		config.each do |c|
65
-			d, hs = c['description'], c['hostnames']
66
-			groups << d
67
-			hs.each { |host| hosts << [d, host] }
68
-		end
69
-		self.analyze hosts, output, groups
70
-	end
71
-
72
-	private
73
-	SCORES = %w(A+ A A- B C D E F T M X)
74
-	def self.score(a)
75
-		SCORES.index a.grade
76
-	end
77
-end

+ 0
- 107
lib/sslcheck/grade.rb Dosyayı Görüntüle

@@ -1,107 +0,0 @@
1
-require 'timeout'
2
-
3
-module SSLCheck
4
-	class NoSslTlsGrade
5
-		attr_reader :server, :score, :grade
6
-
7
-		def initialize(server)
8
-			@server, @score, @grade = server, -1, 'X'
9
-		end
10
-	end
11
-
12
-	class Grade
13
-		attr_reader :server, :score, :grade, :warning, :success
14
-
15
-		def initialize(server)
16
-			@server = server
17
-			protocol_score
18
-			key_exchange_score
19
-			cipher_strengths_score
20
-			@score = @protocol_score*0.3 +  @key_exchange_score*0.3 + @cipher_strengths_score*0.4
21
-			calculate_grade
22
-			warning
23
-			success
24
-			perfect
25
-		end
26
-
27
-		private
28
-		def calculate_grade
29
-			@grade = case @score
30
-				when 0...20 then 'F'
31
-				when 20...35 then 'E'
32
-				when 35...50 then 'D'
33
-				when 50...65 then 'C'
34
-				when 65...80 then 'B'
35
-				else 'A'
36
-			end
37
-
38
-			@grade = [@grade, 'B'].max if !@server.tlsv1_2? or @server.key_size < 2048
39
-			@grade = [@grade, 'C'].max if @server.des3?
40
-			@grade = [@grade, 'E'].max if @server.rc4? or @server.des?
41
-			@grade = [@grade, 'F'].max if @server.ssl? or @server.key_size < 1024
42
-
43
-			@grade = 'M' unless @server.cert_valid
44
-			@grade = 'T' unless @server.cert_trusted
45
-		end
46
-
47
-		def warning
48
-			@warning = []
49
-
50
-			@warning << :md5_sig if @server.md5_sig?
51
-			@warning << :sha1_sig if @server.sha1_sig?
52
-
53
-			@warning << :md5 if @server.md5?
54
-			#@warning << :sha1 if @server.sha1?
55
-
56
-			@warning << :rc4 if @server.rc4?
57
-			@warning << :des if @server.des?
58
-			@warning << :des3 if @server.des3?
59
-		end
60
-
61
-		def success
62
-			@success = []
63
-			@success << :pfs if @server.pfs_only?
64
-			@success << :hsts if @server.hsts?
65
-			@success << :hsts_long if @server.hsts_long?
66
-		end
67
-
68
-		ALL_WARNING = %i(md5_sig md5 rc4 des)
69
-		ALL_SUCCESS = %i(pfs hsts hsts_long)
70
-		def perfect
71
-			@grade = 'A+' if @grade == 'A' and (ALL_WARNING & @warning).empty? and (ALL_SUCCESS & @success) == ALL_SUCCESS
72
-		end
73
-
74
-		METHODS_SCORES = { SSLv2: 0, SSLv3: 80, TLSv1: 90, TLSv1_1: 95, TLSv1_2: 100 }
75
-		def protocol_score
76
-			methods = @server.supported_methods
77
-			worst, best = methods[:worst], methods[:best]
78
-			@protocol_score = (METHODS_SCORES[worst] + METHODS_SCORES[best]) / 2
79
-		end
80
-
81
-		def key_exchange_score
82
-			@key_exchange_score = case @server.key_size
83
-				when 0 then 0
84
-				when 0...512 then 20
85
-				when 512...1024 then 40
86
-				when 1024...2048 then 80
87
-				when 2048...4096 then 90
88
-				else 100
89
-			end
90
-		end
91
-
92
-		def cipher_strength_score(cipher_strength)
93
-			case cipher_strength
94
-				when 0 then 0
95
-				when 0...128 then 20
96
-				when 128...256 then 80
97
-				else 100
98
-			end
99
-		end
100
-
101
-		def cipher_strengths_score
102
-			strength = @server.cipher_size
103
-			worst, best = strength[:min], strength[:max]
104
-			@cipher_strengths_score = (cipher_strength_score(worst) + cipher_strength_score(best)) / 2
105
-		end
106
-	end
107
-end

+ 0
- 334
lib/sslcheck/server.rb Dosyayı Görüntüle

@@ -1,334 +0,0 @@
1
-require 'socket'
2
-require 'openssl'
3
-require 'httparty'
4
-require 'parallel'
5
-require 'tcp_timeout'
6
-
7
-module SSLCheck
8
-	class NoSslTlsServer
9
-		attr_reader :hostname, :port
10
-
11
-		def initialize(hostname, port=443)
12
-			@hostname, @port = hostname, port
13
-		end
14
-	end
15
-
16
-	class Server
17
-		TCP_TIMEOUT = 10
18
-		SSL_TIMEOUT = 2*TCP_TIMEOUT
19
-		EXISTING_METHODS = %i(TLSv1_2 TLSv1_1 TLSv1 SSLv3 SSLv2)
20
-		SUPPORTED_METHODS = OpenSSL::SSL::SSLContext::METHODS
21
-		class TLSException < Exception; end
22
-		class TLSNotAvailableException < TLSException; end
23
-		class CipherNotAvailable < TLSException; end
24
-		class Timeout < TLSException; end
25
-		class TLSTimeout < TLSException; end
26
-		class ConnectionError < TLSException; end
27
-
28
-		attr_reader :hostname, :port, :prefered_ciphers, :cert, :hsts, :cert_valid, :cert_trusted
29
-
30
-		def initialize(hostname, port=443, methods: EXISTING_METHODS)
31
-			@log = Logging.logger[hostname]
32
-			@hostname = hostname
33
-			@port = port
34
-			@methods = methods
35
-			@log.error { "Begin analysis" }
36
-			extract_cert
37
-			fetch_prefered_ciphers
38
-			check_supported_cipher
39
-			fetch_hsts
40
-			@log.error { "End analysis" }
41
-		end
42
-
43
-		def supported_methods
44
-			worst = EXISTING_METHODS.find { |method| !@prefered_ciphers[method].nil? }
45
-			best = EXISTING_METHODS.reverse.find { |method| !@prefered_ciphers[method].nil? }
46
-			{worst: worst, best: best}
47
-		end
48
-
49
-		def key
50
-			key = @cert.public_key
51
-			case key
52
-				when OpenSSL::PKey::RSA then
53
-					[:rsa, key.n.num_bits]
54
-				when OpenSSL::PKey::DSA then
55
-					[:dsa, key.p.num_bits]
56
-				when OpenSSL::PKey::EC then
57
-					[:ecc, key.group.degree]
58
-			end
59
-		end
60
-
61
-		def key_size
62
-			type, size = self.key
63
-			if type == :ecc
64
-				size = case size
65
-					when 160 then 1024
66
-					when 224 then 2048
67
-					when 256 then 3072
68
-					when 384 then 7680
69
-					when 521 then 15360
70
-				end
71
-			end
72
-			size
73
-		end
74
-
75
-		def cipher_size
76
-			cipher_strengths = supported_ciphers.collect { |c| c[2] }.uniq.sort
77
-			worst, best = cipher_strengths.first, cipher_strengths.last
78
-			{worst: worst, best: best}
79
-		end
80
-
81
-		EXISTING_METHODS.each do |method|
82
-			class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
83
-					def #{method.to_s.downcase}?
84
-						!prefered_ciphers[:#{method}].nil?
85
-					end
86
-			RUBY_EVAL
87
-		end
88
-
89
-		{
90
-				  md2: %w(md2WithRSAEncryption),
91
-				  md5: %w(md5WithRSAEncryption md5WithRSA),
92
-				  sha1: %w(sha1WithRSAEncryption sha1WithRSA dsaWithSHA1 dsaWithSHA1_2 ecdsa_with_SHA1)
93
-		}.each do |name, signature|
94
-			class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
95
-				def #{name}_sig?
96
-					#{signature}.include? @cert.signature_algorithm
97
-				end
98
-			RUBY_EVAL
99
-		end
100
-
101
-		{
102
-				  md5: %w(MD5),
103
-				  sha1: %w(SHA),
104
-
105
-				  rc4: %w(RC4),
106
-				  des3: %w(3DES DES-CBC3),
107
-				  des: %w(DES-CBC)
108
-		}.each do |name, ciphers|
109
-			class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
110
-				def #{name}?
111
-					supported_ciphers.any? { |supported| #{ciphers}.any? { |available| /(^|-)#\{available\}(-|$)/ =~ supported[0] } }
112
-				end
113
-			RUBY_EVAL
114
-		end
115
-
116
-		def ssl?
117
-			sslv2? or sslv3?
118
-		end
119
-
120
-		def tls?
121
-			tlsv1? or tlsv1_1? or tlsv1_2?
122
-		end
123
-
124
-		def tls_only?
125
-			tls? and !ssl?
126
-		end
127
-
128
-		PFS_CIPHERS = [/^DHE-RSA-/, /^DHE-DSS-/, /^ECDHE-RSA-/, /^ECDHE-ECDSA-/]
129
-
130
-		def pfs?
131
-			supported_ciphers.any? { |cipher| PFS_CIPHERS.any? { |pc| pc =~ cipher[0] } }
132
-		end
133
-
134
-		def pfs_only?
135
-			supported_ciphers.all? { |cipher| PFS_CIPHERS.any? { |pc| pc =~ cipher[0] } }
136
-		end
137
-
138
-		def supported_ciphers
139
-			@supported_ciphers.values.flatten(1).uniq
140
-		end
141
-
142
-		def supported_ciphers_by_method
143
-			@supported_ciphers
144
-		end
145
-
146
-		def hsts?
147
-			!@hsts.nil?
148
-		end
149
-
150
-		def hsts_long?
151
-			hsts? and @hsts >= 6*30*24*60*60
152
-		end
153
-
154
-		private
155
-		def connect(family, host, port, &block)
156
-			socket = Socket.new family, Socket::SOCK_STREAM
157
-			sockaddr = Socket.sockaddr_in port, host
158
-			@log.debug { "Connecting to #{host}:#{port}" }
159
-			begin
160
-				status = socket.connect_nonblock sockaddr
161
-				@log.debug { "Connecting to #{host}:#{port} status : #{status}" }
162
-				raise ConnectionError.new status unless status == 0
163
-				@log.debug { "Connected to #{host}:#{port}" }
164
-				block_given? ? block.call(socket) : nil
165
-			rescue IO::WaitReadable
166
-				@log.debug { "Waiting for read to #{host}:#{port}" }
167
-				raise Timeout.new unless IO.select [socket], nil, nil, TCP_TIMEOUT
168
-				retry
169
-			rescue IO::WaitWritable
170
-				@log.debug { "Waiting for write to #{host}:#{port}" }
171
-				raise Timeout.new unless IO.select nil, [socket], nil, TCP_TIMEOUT
172
-				retry
173
-			ensure
174
-				socket.close
175
-			end
176
-		end
177
-
178
-		def ssl_connect(socket, context, method, &block)
179
-			ssl_socket = OpenSSL::SSL::SSLSocket.new socket, context
180
-			ssl_socket.hostname = @hostname unless method == :SSLv2
181
-			@log.debug { "SSL connecting to #{@hostname}:#{@port}" }
182
-			begin
183
-				ssl_socket.connect_nonblock
184
-				@log.debug { "SSL connected to #{@hostname}:#{@port}" }
185
-				return block_given? ? block.call(ssl_socket) : nil
186
-			rescue IO::WaitReadable
187
-				@log.debug { "Waiting for SSL read to #{@hostname}:#{@port}" }
188
-				raise TLSTimeout.new unless IO.select [socket], nil, nil, SSL_TIMEOUT
189
-				retry
190
-			rescue IO::WaitWritable
191
-				@log.debug { "Waiting for SSL write to #{@hostname}:#{@port}" }
192
-				raise TLSTimeout.new unless IO.select nil, [socket], nil, SSL_TIMEOUT
193
-				retry
194
-			rescue OpenSSL::SSL::SSLError => e
195
-				raise TLSException, e
196
-			ensure
197
-				ssl_socket.close
198
-			end
199
-		end
200
-
201
-		def ssl_client(method, ciphers = nil, &block)
202
-			ssl_context = OpenSSL::SSL::SSLContext.new method
203
-			ssl_context.ciphers = ciphers if ciphers
204
-			@log.debug { "Try #{method} connection with #{ciphers}" }
205
-
206
-			[Socket::AF_INET, Socket::AF_INET6].each do |family|
207
-				@log.debug { "Try connection for family #{family}" }
208
-				addrs = begin
209
-					Socket.getaddrinfo @hostname, nil, family, :STREAM
210
-				rescue SocketError => e
211
-					@log.debug { "Unable to resolv #{@hostname} : #{e}" }
212
-					next
213
-				end
214
-
215
-				addrs.each do |addr|
216
-					connect family, addr[3], @port do |socket|
217
-						ssl_connect socket, ssl_context, method do |ssl_socket|
218
-							return block_given? ? block.call(ssl_socket) : nil
219
-						end
220
-					end
221
-				end
222
-			end
223
-
224
-			@log.debug { "No SSL available on #{@hostname}" }
225
-			raise CipherNotAvailable.new
226
-		end
227
-
228
-		def extract_cert
229
-			@methods.each do |method|
230
-				next unless SUPPORTED_METHODS.include? method
231
-				begin
232
-					@cert, @chain = ssl_client(method) { |s| [s.peer_cert, s.peer_cert_chain] }
233
-					@log.warn { "Certificate #{@cert.subject}" }
234
-					break
235
-				rescue TLSException => e
236
-					@log.info { "Method #{method} not supported : #{e}" }
237
-				end
238
-			end
239
-			raise TLSNotAvailableException.new unless @cert
240
-			@cert_valid = OpenSSL::SSL.verify_certificate_identity @cert, @hostname
241
-			@cert_trusted = verify_trust @chain, @cert
242
-		end
243
-
244
-		def prefered_cipher(method)
245
-			cipher = ssl_client(method, %w(ALL:COMPLEMENTOFALL)) { |s| s.cipher }
246
-			@log.warn { "Prefered cipher for #{method} : #{cipher[0]}" }
247
-			cipher
248
-		rescue Exception => e
249
-			@log.info { "Method #{method} not supported : #{e}" }
250
-			nil
251
-		end
252
-
253
-		def fetch_prefered_ciphers
254
-			@prefered_ciphers = {}
255
-			@methods.each do |method|
256
-				next unless SUPPORTED_METHODS.include? method
257
-				@prefered_ciphers[method] = prefered_cipher method
258
-			end
259
-			raise TLSNotAvailableException.new unless @prefered_ciphers.any? { |_, c| !c.nil? }
260
-		end
261
-
262
-		def available_ciphers(method)
263
-			OpenSSL::SSL::SSLContext.new(method).ciphers
264
-		end
265
-
266
-		def supported_cipher?(method, cipher)
267
-			ssl_client method, [cipher]
268
-			@log.warn { "Verify #{method} / #{cipher[0]} : OK" }
269
-			true
270
-		rescue TLSException => e
271
-			@log.info { "Verify #{method} / #{cipher[0]} : NOK (#{e})" }
272
-			false
273
-		end
274
-
275
-		def check_supported_cipher
276
-			@supported_ciphers = {}
277
-			@methods.each do |method|
278
-				next unless SUPPORTED_METHODS.include? method and @prefered_ciphers[method]
279
-				@supported_ciphers[method] = available_ciphers(method).select { |cipher| supported_cipher? method, cipher }
280
-			end
281
-		end
282
-
283
-		def fetch_hsts
284
-			port = @port == 443 ? '' : ":#{@port}"
285
-
286
-			response = nil
287
-			@methods.each do |method|
288
-				begin
289
-					next unless SUPPORTED_METHODS.include? method
290
-					@log.debug { "Check HSTS with #{method}" }
291
-					response = HTTParty.head "https://#{@hostname}#{port}/", {follow_redirects: false, verify: false, ssl_version: method, timeout: SSL_TIMEOUT}
292
-					break
293
-				rescue Exception => e
294
-					@log.debug { "#{method} not supported : #{e}" }
295
-				end
296
-			end
297
-
298
-			if response and header = response.headers['strict-transport-security']
299
-				name, value = header.split '='
300
-				if name == 'max-age'
301
-					@hsts = value.to_i
302
-					@log.info { "HSTS : #{@hsts}" }
303
-					return
304
-				end
305
-			end
306
-
307
-			@log.info { 'No HSTS' }
308
-			@hsts = nil
309
-		end
310
-
311
-		def verify_trust(chain, cert)
312
-			store = OpenSSL::X509::Store.new
313
-			#store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT
314
-			%w(mozilla cacert).each do |directory|
315
-				Dir.glob(File.join '/usr/share/ca-certificates', directory, '*').each do |file|
316
-					File.open file, 'r' do |file|
317
-						cert = OpenSSL::X509::Certificate.new file.read
318
-						begin
319
-							store.add_cert cert
320
-						rescue OpenSSL::X509::StoreError
321
-						end
322
-					end
323
-				end
324
-			end
325
-			chain.each do |cert|
326
-				begin
327
-					store.add_cert cert
328
-				rescue OpenSSL::X509::StoreError
329
-				end
330
-			end
331
-			store.verify cert
332
-		end
333
-	end
334
-end

+ 0
- 148
lib/sslcheck/ssllabs/api.rb Dosyayı Görüntüle

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

output/sslcheck.erb → output/https.erb Dosyayı Görüntüle

@@ -84,7 +84,7 @@
84 84
 										<%= s.hostname %>
85 85
 									</a>
86 86
 								</th>
87
-								<% if s.is_a? SSLCheck::NoSslTlsServer %>
87
+								<% if s.is_a? SSLCheck::TlsNotSupportedServer %>
88 88
 								<td class="critical" colspan="16">
89 89
 									No SSL/TLS
90 90
 								</td>
@@ -94,7 +94,7 @@
94 94
 											when 'A+' then :info
95 95
 											when 'A', 'A-' then :success
96 96
 											when 'B', 'C' then :warning
97
-											when 'T', 'X' then :critical
97
+											when 'T', 'M' then :critical
98 98
 											else :danger
99 99
 										end
100 100
 								%>

+ 2
- 3
output/index.yml Dosyayı Görüntüle

@@ -21,13 +21,13 @@
21 21
   - www.laquadrature.net
22 22
   - fsf.org
23 23
   - ubuntu-paris.org
24
-  - parinux.org
24
+  - www.parinux.org
25 25
   - aful.org
26 26
   - rmll.info
27 27
   - ubuntu-fr.org
28 28
   - linuxfr.org
29 29
   - lea-linux.org
30
-  - framasoft.net
30
+  - framasoft.org
31 31
   - gnu.org
32 32
 - description: Banques en ligne
33 33
   hostnames:
@@ -197,7 +197,6 @@
197 197
   - spideroak.com
198 198
   - hubic.com
199 199
   - box.com
200
-  - mega.co.nz
201 200
 - description: Divers
202 201
   hostnames:
203 202
   - www.mailden.net

+ 0
- 110
output/ssllabs.erb Dosyayı Görüntüle

@@ -1,110 +0,0 @@
1
-<!DOCTYPE html>
2
-<html lang="fr">
3
-	<head>
4
-		<meta charset="utf-8">
5
-		<meta http-equiv="X-UA-Compatible" content="IE=edge">
6
-		<meta name="viewport" content="width=device-width, initial-scale=1">
7
-		<title>Status SSL/TLS banque &amp; commerce en ligne</title>
8
-		<link rel="stylesheet" href="bootstrap.min.css">
9
-		<style>
10
-			body {
11
-				margin-top: 10px;
12
-			}
13
-
14
-			td {
15
-				text-align: center;
16
-			}
17
-		</style>
18
-	</head>
19
-	<body>
20
-		<div class="container-fluid">
21
-			<div class="row">
22
-				<div class="col-md-12">
23
-					<table class="table table-bordered table-hover table-condensed">
24
-						<tbody>
25
-							<%
26
-								first = true
27
-								results.each do |r|
28
-									unless first
29
-							%>
30
-							<tr>
31
-								<th colspan="8">&nbsp;</th>
32
-							</tr>
33
-							<%
34
-									end
35
-									first = false
36
-							%>
37
-							<tr>
38
-								<th colspan="8" id="<%= r[0] %>"><%= r[0] %></th>
39
-							</tr>
40
-							<tr>
41
-								<th>Site (IP)</th>
42
-								<td>Rang</td>
43
-								<td>Sécurité (bits)</td>
44
-								<td class="danger">SSL 2/3 (obsolète)</td>
45
-								<td class="success">TLS 1.2 (actuel)</td>
46
-								<td class="danger">RC4</td>
47
-								<td class="info">PFS</td>
48
-								<td class="info">HSTS</td>
49
-							</tr>
50
-							<% r[1].each do |s|
51
-							   rank_color = case s.rank
52
-											   when 'A+' then :info
53
-											   when 'A', 'A-' then :success
54
-											   when 'B', 'C' then :warning
55
-											   else :danger
56
-											end
57
-							%>
58
-							<tr>
59
-								<th id="<%= s.hostname %>">
60
-									<a href="https://www.ssllabs.com/ssltest/analyze.html?d=<%= s.hostname %>&s=<%= s.ip %>" target="_blank">
61
-										<%= s.hostname %> (<%= s.ip %>)
62
-									</a>
63
-								</th>
64
-								<td class="<%= rank_color %>">
65
-									<%= s.rank %>
66
-								</td>
67
-								<td class="<%= s.bits < 128 ? :danger : :success %>">
68
-									<%= s.bits %>
69
-									(<%= s.bits < 128 ? '☹' : '☺' %>)
70
-								</td>
71
-								<td class="<%= s.ssl ? :danger : :success %>">
72
-									<%= s.ssl ? '✓' : '✗' %>
73
-									(<%= s.ssl ? '☹' : '☺' %>)
74
-								</td>
75
-								<td class="<%= s.tls ? :success : :danger %>">
76
-									<%= s.tls ? '✓' : '✗' %>
77
-									(<%= s.tls ? '☺' : '☹' %>)
78
-								</td>
79
-								<td class="<%= s.rc4 ? :danger : :success %>">
80
-									<%= s.rc4 ? '✓' : '✗' %>
81
-									(<%= s.rc4 ? '☹' : '☺' %>)
82
-								</td>
83
-								<td class="<%= s.pfs ? :success : :danger %>">
84
-									<%= s.pfs ? '✓' : '✗' %>
85
-									(<%= s.pfs ? '☺' : '☹' %>)
86
-								</td>
87
-								<td class="<%= s.hsts ? :success : :danger %>">
88
-									<%= s.hsts ? '✓' : '✗' %>
89
-									(<%= s.hsts ? '☺' : '☹' %>)
90
-								</td>
91
-							</tr>
92
-							<% end %>
93
-							<tr>
94
-								<th>Site (IP)</th>
95
-								<td>Rang</td>
96
-								<td>Sécurité (bits)</td>
97
-								<td class="danger">SSL 2/3 (obsolète)</td>
98
-								<td class="success">TLS 1.2 (actuel)</td>
99
-								<td class="danger">RC4</td>
100
-								<td class="success">PFS</td>
101
-								<td class="success">HSTS</td>
102
-							</tr>
103
-							<% end %>
104
-						</tbody>
105
-					</table>
106
-				</div>
107
-			</div>
108
-		</div>
109
-	</body>
110
-</html>

+ 0
- 51
spec/api_spec.rb Dosyayı Görüntüle

@@ -1,51 +0,0 @@
1
-require 'sslcheck'
2
-
3
-module SSLCheck::SSLLabs
4
-	describe API do
5
-		URL = 'https://www.ssllabs.com/ssltest/analyze.html'
6
-
7
-		it 'error' do
8
-			stub_request(:get, URL).with(query: { d: 'imirhil.fr'})
9
-				.to_return(status: 500)
10
-			expect { API.new 'imirhil.fr' } .to  raise_error ServerError, '500'
11
-		end
12
-
13
-		it 'waiting' do
14
-			stub_request(:get, URL).with(query: { d: 'imirhil.fr'})
15
-				.to_return(status: 200, body: File.read('spec/html/waiting.html'))
16
-			expect { API.new 'imirhil.fr' } .to raise_error WaitingError
17
-		end
18
-
19
-		it 'single' do
20
-			stub_request(:get, URL).with(query: { d: 'imirhil.fr'})
21
-			.to_return(status: 200, body: File.read('spec/html/perfect.html'))
22
-			results = API.new 'imirhil.fr'
23
-			expect(results.hostname).to eq 'imirhil.fr'
24
-			expect(results.ip).to eq '5.135.187.37'
25
-			expect(results.rank).to eq 'A+'
26
-			expect(results.ssl).to be false
27
-			expect(results.tls).to be true
28
-			expect(results.rc4).to be false
29
-			expect(results.pfs).to be true
30
-			expect(results.hsts).to be true
31
-			expect(results.bits).to be 128
32
-		end
33
-
34
-		it 'multiple' do
35
-			stub_request(:get, URL).with(query: { d: 'fortuneo.fr'})
36
-				.to_return(status: 200, body: File.read('spec/html/multiple.html'))
37
-			stub_request(:get, URL).with(query: { d: 'fortuneo.fr', s: '93.20.46.72'})
38
-				.to_return(status: 200, body: File.read('spec/html/results.html'))
39
-			results = API.new 'fortuneo.fr'
40
-			expect(results.hostname).to eq 'fortuneo.fr'
41
-			expect(results.ip).to eq '194.51.217.72'
42
-			expect(results.rank).to eq 'B'
43
-			expect(results.ssl).to be true
44
-			expect(results.tls).to be false
45
-			expect(results.rc4).to be true
46
-			expect(results.pfs).to be false
47
-			expect(results.hsts).to be false
48
-			expect(results.bits).to be 128
49
-		end
50
-	end
51
-end

+ 0
- 10
sslcheck Dosyayı Görüntüle

@@ -1,10 +0,0 @@
1
-#!/usr/bin/env ruby
2
-$:.unshift 'lib'
3
-require 'logging'
4
-require 'sslcheck'
5
-
6
-Logging.logger.root.appenders = Logging.appenders.stdout
7
-Logging.logger.root.level = :warn
8
-
9
-server = SSLCheck::Server.new(ARGV[0], ARGV[1] || 443)
10
-p grade = SSLCheck::Grade.new(server)

+ 0
- 19
sslcheck-alexa Dosyayı Görüntüle

@@ -1,19 +0,0 @@
1
-#!/usr/bin/env ruby
2
-require 'yaml'
3
-$:.unshift 'lib'
4
-require 'sslcheck'
5
-
6
-Logging.logger.root.appenders = Logging.appenders.stdout
7
-Logging.logger.root.level = :error
8
-
9
-hosts = []
10
-File.open('top-1m.csv', 'r') do |file|
11
-	i = 0
12
-	while line = file.gets
13
-		hosts << ['Top 100 Alexa', line.strip.split(',')[1]]
14
-		i += 1
15
-		break if i == 100
16
-	end
17
-end
18
-
19
-SSLCheck.analyze hosts, 'output/alexa.html'

+ 0
- 5
sslcheck-all Dosyayı Görüntüle

@@ -1,5 +0,0 @@
1
-#!/usr/bin/env ruby
2
-$:.unshift 'lib'
3
-require 'sslcheck'
4
-name = ARGV[0] || 'index'
5
-SSLCheck.analyze_from_file "output/#{name}.yml", "output/#{name}.html"

+ 0
- 60
sslcheck-ssllabs Dosyayı Görüntüle

@@ -1,60 +0,0 @@
1
-#!/usr/bin/env ruby
2
-require 'erb'
3
-require 'yaml'
4
-$:.unshift 'lib'
5
-require 'sslcheck'
6
-
7
-SCORES = %w(A+ A A- B C D E F T M)
8
-def score(a); SCORES.index a.rank; end
9
-
10
-def check(hostname)
11
-	hostname.strip!
12
-	print '  ', hostname, ' : '
13
-	begin
14
-		result = SSLCheck::SSLLabs::API.new hostname
15
-		puts result.rank
16
-		result
17
-	rescue SSLCheck::SSLLabs::NoEncryptionError
18
-		puts 'no encryption'
19
-		raise
20
-	rescue => e
21
-		puts e
22
-		raise
23
-	end
24
-end
25
-
26
-config = YAML.load_file 'hosts.yml'
27
-results = Hash[config.collect { |c| [c['description'], []] }]
28
-
29
-loop do
30
-	waiting = false
31
-	config.each do |c|
32
-		description, hosts = c['description'], c['hostnames']
33
-		puts description
34
-		hosts.clone.each do |host|
35
-			begin
36
-				results[description] << check(host)
37
-				hosts.delete host
38
-			rescue SSLCheck::SSLLabs::WaitingError
39
-				waiting = true
40
-			rescue SSLCheck::SSLLabs::Error
41
-			rescue => e
42
-				p e.backtrace
43
-			end
44
-		end
45
-	end
46
-
47
-	break if not waiting
48
-	puts 'Waiting end of analyze'
49
-	sleep 1*60
50
-end
51
-
52
-results.each do |d, _|
53
-	results[d].sort! do |a, b|
54
-		cmp = score(a) <=> score(b)
55
-		cmp != 0 ? cmp : a.hostname <=> b.hostname
56
-	end
57
-end
58
-
59
-puts 'Generate results'
60
-File.write 'output/index.html', ERB.new(File.read('index.erb')).result(binding)

Loading…
İptal
Kaydet