mirror of
https://github.com/drwetter/testssl.sh.git
synced 2025-01-01 06:19:44 +01:00
Merge branch 'magnuslarsen-grading_dev' into 3.1dev
This commit is contained in:
commit
f5a1884439
@ -13,7 +13,7 @@
|
||||
* Several display/output fixes
|
||||
* Security fix: DNS input
|
||||
* Don't use external pwd anymore
|
||||
* Rating (pending)
|
||||
* Rating (SSL Labs, not complete)
|
||||
|
||||
### Features implemented / improvements in 3.0
|
||||
|
||||
|
@ -46,9 +46,9 @@ Full contribution, see git log.
|
||||
- Client simulations
|
||||
- CI integration, some test cases for it
|
||||
|
||||
* Steven Danneman
|
||||
- Postgres and MySQL STARTTLS support
|
||||
- MongoDB support
|
||||
* Steven Danneman
|
||||
- Postgres and MySQL STARTTLS support
|
||||
- MongoDB support
|
||||
|
||||
* Christian Dresen
|
||||
- Dockerfile
|
||||
@ -78,6 +78,9 @@ Full contribution, see git log.
|
||||
* Hubert Kario
|
||||
- helped with avoiding accidental TCP fragmentation
|
||||
|
||||
* Magnus Larsen
|
||||
- SSL Labs Rating
|
||||
|
||||
* Jacco de Leeuw
|
||||
- skip checks which might trigger an IDS ($OFFENSIVE / --ids-friendly)
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
.\" generated with Ronn/v0.7.3
|
||||
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
||||
.
|
||||
.TH "TESTSSL" "1" "January 2020" "" ""
|
||||
.TH "TESTSSL" "1" "May 2020" "" ""
|
||||
.
|
||||
.SH "NAME"
|
||||
\fBtestssl\fR
|
||||
@ -43,10 +43,10 @@ Any OpenSSL or LibreSSL version is needed as a helper\. Unlike previous versions
|
||||
1) SSL/TLS protocol check
|
||||
.
|
||||
.P
|
||||
2) standard cipher categories to give you upfront an idea for the ciphers supported
|
||||
2) standard cipher categories
|
||||
.
|
||||
.P
|
||||
3) server's cipher preferences (server order)
|
||||
3) server\'s cipher preferences (server order?)
|
||||
.
|
||||
.P
|
||||
4) forward secrecy: ciphers and elliptical curves
|
||||
@ -61,8 +61,14 @@ Any OpenSSL or LibreSSL version is needed as a helper\. Unlike previous versions
|
||||
7) vulnerabilities
|
||||
.
|
||||
.P
|
||||
8) testing each of 370 preconfigured ciphers
|
||||
.
|
||||
.P
|
||||
8) client simulation
|
||||
.
|
||||
.P
|
||||
9) rating
|
||||
.
|
||||
.SH "OPTIONS AND PARAMETERS"
|
||||
Options are either short or long options\. Any long or short option requiring a value can be called with or without an equal sign\. E\.g\. \fBtestssl\.sh \-t=smtp \-\-wide \-\-openssl=/usr/bin/openssl <URI>\fR (short options with equal sign) is equivalent to \fBtestssl\.sh \-\-starttls smtp \-\-wide \-\-openssl /usr/bin/openssl <URI>\fR (long option without equal sign)\. Some command line options can also be preset via ENV variables\. \fBWIDE=true OPENSSL=/usr/bin/openssl testssl\.sh \-\-starttls=smtp <URI>\fR would be the equivalent to the aforementioned examples\. Preference has the command line over any environment variables\.
|
||||
.
|
||||
@ -235,8 +241,7 @@ session resumption capabilities,
|
||||
Time skew relative to localhost (most server implementations return random values)\.
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Several certificate information
|
||||
.RS
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
signature algorithm,
|
||||
.
|
||||
@ -272,7 +277,7 @@ displaying DNS Certification Authority Authorization resource record
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Certificate Transparency info (if provided by server)\.
|
||||
.RE
|
||||
.
|
||||
.IP "" 0
|
||||
|
||||
.
|
||||
@ -406,7 +411,7 @@ Security headers (X\-Frame\-Options, X\-XSS\-Protection, Expect\-CT,\.\.\. , CSP
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Please note that in testssl\.sh 3,0 you can still use \fBrfc\fR instead of \fBiana\fR and \fBno\-rfc\fR instead of \fBno\-iana\fR but it\'ll disappear after 3\.0\.
|
||||
Please note that in testssl\.sh 3\.0 you can still use \fBrfc\fR instead of \fBiana\fR and \fBno\-rfc\fR instead of \fBno\-iana\fR but it\'ll disappear after 3\.0\.
|
||||
.
|
||||
.P
|
||||
\fB\-\-show\-each\fR This is an option for all wide modes only: it displays all ciphers tested \-\- not only succeeded ones\. \fBSHOW_EACH_C\fR is your friend if you prefer to set this via the shell environment\.
|
||||
@ -440,6 +445,9 @@ whole 9 yards
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
\fB\-\-disable\-rating\fR disables rating\. Rating automatically gets disabled, to not give a wrong or misleading grade, when not all required functions are executed (e\.g when checking for a single vulnerabilities)\.
|
||||
.
|
||||
.SS "FILE OUTPUT OPTIONS"
|
||||
\fB\-\-log, \-\-logging\fR Logs stdout also to \fB${NODE}\-p${port}${YYYYMMDD\-HHMM}\.log\fR in current working directory of the shell\. Depending on the color output option (see above) the output file will contain color and other markup escape codes, unless you specify \fB\-\-color 0\fR too\. \fBcat\fR and \-\- if properly configured \fBless\fR \-\- will show the output properly formatted on your terminal\. The output shows a banner with the almost the same information as on the screen\. In addition it shows the command line of the testssl\.sh instance\. Please note that the resulting log file is formatted according to the width of your screen while running testssl\.sh\. You can override the width with the environment variable TERM_WIDTH\.
|
||||
.
|
||||
@ -623,6 +631,30 @@ MAX_HEADER_FAIL: A number which tells testssl\.sh how often a HTTP GET request o
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "RATING"
|
||||
This program has a near\-complete implementation of SSL Labs\'s \'SSL Server Rating Guide \fIhttps://github\.com/ssllabs/research/wiki/SSL\-Server\-Rating\-Guide\fR\'\.
|
||||
.
|
||||
.P
|
||||
This is \fInot\fR a 100% reimplementation of the SSL Lab\'s SSL Server Test \fIhttps://www\.ssllabs\.com/ssltest/analyze\.html\fR, but an implementation of the above rating specification, slight discrepancies may occur\. Please note that for now we stick to the SSL Labs rating as good as possible\. We are not responsible for their rating\. Before filing issues please inspect their Rating Guide\.
|
||||
.
|
||||
.P
|
||||
Disclaimer: Having a good grade is \fBNOT\fR necessarily equal to having good security! Don\'t start a competition for the best grade, at least not without monitoring the client handshakes and not without adding a portion of good sense to it\.
|
||||
.
|
||||
.P
|
||||
As of writing, these checks are missing: * GOLDENDOODLE \- should be graded \fBF\fR if vulnerable * Insecure renegotiation \- should be graded \fBF\fR if vulnerable * Padding oracle in AES\-NI CBC MAC check (CVE\-2016\-2107) \- should be graded \fBF\fR if vulnerable * Sleeping POODLE \- should be graded \fBF\fR if vulnerable * Zero Length Padding Oracle (CVE\-2019\-1559) \- should be graded \fBF\fR if vulnerable * Zombie POODLE \- should be graded \fBF\fR if vulnerable * All remaining old Symantec PKI certificates are distrusted \- should be graded \fBT\fR * Symantec certificates issued before June 2016 are distrusted \- should be graded \fBT\fR * ! A reading of DH params \- should give correct points in \fBset_key_str_score()\fR * Anonymous key exchange \- should give \fB0\fR points in \fBset_key_str_score()\fR * Exportable key exchange \- should give \fB40\fR points in \fBset_key_str_score()\fR * Weak key (Debian OpenSSL Flaw) \- should give \fB0\fR points in \fBset_key_str_score()\fR
|
||||
.
|
||||
.P
|
||||
To implement a new grading cap, simply call the \fBset_grade_cap()\fR function, with the grade and a reason: \fBbash set_grade_cap "D" "Vulnerable to documentation"\fR To implement a new grade warning, simply call the \fBset_grade_warning()\fR function, with a message: \fBbash set_grade_warning "Documentation is always right"\fR
|
||||
.
|
||||
.P
|
||||
When implementing a new check (be it vulnerability or not) that sets grade caps, the \fBset_rating_state()\fR has to be updated (i\.e\. the \fB$do_mycheck\fR variable\-name has to be added to the loop, and \fB$nr_enabled\fR if\-statement has to be incremented)
|
||||
.
|
||||
.P
|
||||
The \fBset_rating_state()\fR automatically disables ratinng, if all the required checks are \fInot\fR enabled\. This is to prevent giving out a misleading or wrong grade\.
|
||||
.
|
||||
.P
|
||||
When a new revision of the rating specification comes around, the following has to be done: * New grade caps has to be either: 1\. Added to the script wherever relevant, or 2\. Added to the above list of missing checks (if \fIi\.\fR is not possible) * New grade warnings has to be added wherever relevant * The revision output in \fBrun_rating()\fR function has to updated
|
||||
.
|
||||
.SH "EXAMPLES"
|
||||
.
|
||||
.nf
|
||||
@ -632,7 +664,7 @@ MAX_HEADER_FAIL: A number which tells testssl\.sh how often a HTTP GET request o
|
||||
.fi
|
||||
.
|
||||
.P
|
||||
does a default run on https://testssl\.sh (protocols, standard cipher lists, FS, server preferences, server defaults, vulnerabilities, testing all known 370 ciphers, client simulation\.
|
||||
does a default run on https://testssl\.sh (protocols, standard cipher lists, server\'s cipher preferences, forward secrecy, server defaults, vulnerabilities, client simulation, and rating\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
|
@ -121,9 +121,9 @@ linked OpenSSL binaries for major operating systems are supplied in <code>./bin/
|
||||
|
||||
<p>1) SSL/TLS protocol check</p>
|
||||
|
||||
<p>2) standard cipher categories to give you upfront an idea for the ciphers supported</p>
|
||||
<p>2) standard cipher categories</p>
|
||||
|
||||
<p>3) server's cipher preferences (server order)</p>
|
||||
<p>3) server's cipher preferences (server order?)</p>
|
||||
|
||||
<p>4) forward secrecy: ciphers and elliptical curves</p>
|
||||
|
||||
@ -135,6 +135,8 @@ linked OpenSSL binaries for major operating systems are supplied in <code>./bin/
|
||||
|
||||
<p>8) client simulation</p>
|
||||
|
||||
<p>9) rating</p>
|
||||
|
||||
<h2 id="OPTIONS-AND-PARAMETERS">OPTIONS AND PARAMETERS</h2>
|
||||
|
||||
<p>Options are either short or long options. Any long or short option requiring a value can be called with or without an equal sign. E.g. <code>testssl.sh -t=smtp --wide --openssl=/usr/bin/openssl <URI></code> (short options with equal sign) is equivalent to <code>testssl.sh --starttls smtp --wide --openssl /usr/bin/openssl <URI></code> (long option without equal sign). Some command line options can also be preset via ENV variables. <code>WIDE=true OPENSSL=/usr/bin/openssl testssl.sh --starttls=smtp <URI></code> would be the equivalent to the aforementioned examples. Preference has the command line over any environment variables.</p>
|
||||
@ -183,7 +185,7 @@ The same can be achieved by setting the environment variable <code>WARNINGS</cod
|
||||
|
||||
<p><code>--openssl-timeout <seconds></code> This is especially useful for all connects using openssl and practically useful for mass testing. It avoids the openssl connect to hang for ~2 minutes. The expected parameter <code>seconds</code> instructs testssl.sh to wait before the openssl connect will be terminated. The option is only available if your OS has a timeout binary installed. As there are different implementations of <code>timeout</code>: It automatically calls the binary with the right parameters. OPENSSL_TIMEOUT is the equivalent environment variable.</p>
|
||||
|
||||
<p><code>--basicauth <user:pass></code> This can be set to provide HTTP basic auth credentials which are used during checks for security headers. BASICAUTH is the ENV variable you can use instead.</p>
|
||||
<p><code>--basicauth <user:pass></code> This can be set to provide HTTP basic auth credentials which are used during checks for security headers. BASICAUTH is the ENV variable you can use instead.</p>
|
||||
|
||||
<h3 id="SPECIAL-INVOCATIONS">SPECIAL INVOCATIONS</h3>
|
||||
|
||||
@ -355,7 +357,7 @@ Also for multiple server certificates are being checked for as well as for the c
|
||||
</ul>
|
||||
|
||||
|
||||
<p>Please note that in testssl.sh 3,0 you can still use <code>rfc</code> instead of <code>iana</code> and <code>no-rfc</code> instead of <code>no-iana</code> but it'll disappear after 3.0.</p>
|
||||
<p>Please note that in testssl.sh 3.0 you can still use <code>rfc</code> instead of <code>iana</code> and <code>no-rfc</code> instead of <code>no-iana</code> but it'll disappear after 3.0.</p>
|
||||
|
||||
<p><code>--show-each</code> This is an option for all wide modes only: it displays all ciphers tested -- not only succeeded ones. <code>SHOW_EACH_C</code> is your friend if you prefer to set this via the shell environment.</p>
|
||||
|
||||
@ -375,6 +377,9 @@ Also for multiple server certificates are being checked for as well as for the c
|
||||
</ol>
|
||||
|
||||
|
||||
<p><code>--disable-rating</code> disables rating.
|
||||
Rating automatically gets disabled, to not give a wrong or misleading grade, when not all required functions are executed (e.g when checking for a single vulnerabilities).</p>
|
||||
|
||||
<h3 id="FILE-OUTPUT-OPTIONS">FILE OUTPUT OPTIONS</h3>
|
||||
|
||||
<p><code>--log, --logging</code> Logs stdout also to <code>${NODE}-p${port}${YYYYMMDD-HHMM}.log</code> in current working directory of the shell. Depending on the color output option (see above) the output file will contain color and other markup escape codes, unless you specify <code>--color 0</code> too. <code>cat</code> and -- if properly configured <code>less</code> -- will show the output properly formatted on your terminal. The output shows a banner with the almost the same information as on the screen. In addition it shows the command line of the testssl.sh instance. Please note that the resulting log file is formatted according to the width of your screen while running testssl.sh. You can override the width with the environment variable TERM_WIDTH.</p>
|
||||
@ -473,12 +478,61 @@ Also for multiple server certificates are being checked for as well as for the c
|
||||
</ul>
|
||||
|
||||
|
||||
<h3 id="RATING">RATING</h3>
|
||||
|
||||
<p>This program has a near-complete implementation of SSL Labs's '<a href="https://github.com/ssllabs/research/wiki/SSL-Server-Rating-Guide">SSL Server Rating Guide</a>'.</p>
|
||||
|
||||
<p>This is <em>not</em> a 100% reimplementation of the <a href="https://www.ssllabs.com/ssltest/analyze.html">SSL Lab's SSL Server Test</a>, but an implementation of the above rating specification, slight discrepancies may occur. Please note that for now we stick to the SSL Labs rating as good as possible. We are not responsible for their rating. Before filing issues please inspect their Rating Guide.</p>
|
||||
|
||||
<p>Disclaimer: Having a good grade is <strong>NOT</strong> necessarily equal to having good security! Don't start a competition for the best grade, at least not without monitoring the client handshakes and not without adding a portion of good sense to it.</p>
|
||||
|
||||
<p>As of writing, these checks are missing:
|
||||
* GOLDENDOODLE - should be graded <strong>F</strong> if vulnerable
|
||||
* Insecure renegotiation - should be graded <strong>F</strong> if vulnerable
|
||||
* Padding oracle in AES-NI CBC MAC check (CVE-2016-2107) - should be graded <strong>F</strong> if vulnerable
|
||||
* Sleeping POODLE - should be graded <strong>F</strong> if vulnerable
|
||||
* Zero Length Padding Oracle (CVE-2019-1559) - should be graded <strong>F</strong> if vulnerable
|
||||
* Zombie POODLE - should be graded <strong>F</strong> if vulnerable
|
||||
* All remaining old Symantec PKI certificates are distrusted - should be graded <strong>T</strong>
|
||||
* Symantec certificates issued before June 2016 are distrusted - should be graded <strong>T</strong>
|
||||
* ! A reading of DH params - should give correct points in <code>set_key_str_score()</code>
|
||||
* Anonymous key exchange - should give <strong>0</strong> points in <code>set_key_str_score()</code>
|
||||
* Exportable key exchange - should give <strong>40</strong> points in <code>set_key_str_score()</code>
|
||||
* Weak key (Debian OpenSSL Flaw) - should give <strong>0</strong> points in <code>set_key_str_score()</code></p>
|
||||
|
||||
<h4 id="Implementing-new-grades-caps-or-warnings">Implementing new grades caps or -warnings</h4>
|
||||
|
||||
<p>To implement a new grading cap, simply call the <code>set_grade_cap()</code> function, with the grade and a reason:
|
||||
<code>bash
|
||||
set_grade_cap "D" "Vulnerable to documentation"
|
||||
</code>
|
||||
To implement a new grade warning, simply call the <code>set_grade_warning()</code> function, with a message:
|
||||
<code>bash
|
||||
set_grade_warning "Documentation is always right"
|
||||
</code></p>
|
||||
|
||||
<h4 id="Implementing-a-new-check-which-contains-grade-caps">Implementing a new check which contains grade caps</h4>
|
||||
|
||||
<p>When implementing a new check (be it vulnerability or not) that sets grade caps, the <code>set_rating_state()</code> has to be updated (i.e. the <code>$do_mycheck</code> variable-name has to be added to the loop, and <code>$nr_enabled</code> if-statement has to be incremented)</p>
|
||||
|
||||
<p>The <code>set_rating_state()</code> automatically disables ratinng, if all the required checks are <em>not</em> enabled.
|
||||
This is to prevent giving out a misleading or wrong grade.</p>
|
||||
|
||||
<h4 id="Implementing-a-new-revision">Implementing a new revision</h4>
|
||||
|
||||
<p>When a new revision of the rating specification comes around, the following has to be done:
|
||||
* New grade caps has to be either:
|
||||
1. Added to the script wherever relevant, or
|
||||
2. Added to the above list of missing checks (if <em>i.</em> is not possible)
|
||||
* New grade warnings has to be added wherever relevant
|
||||
* The revision output in <code>run_rating()</code> function has to updated</p>
|
||||
|
||||
<h2 id="EXAMPLES">EXAMPLES</h2>
|
||||
|
||||
<pre><code> testssl.sh testssl.sh
|
||||
</code></pre>
|
||||
|
||||
<p>does a default run on https://testssl.sh (protocols, standard cipher lists, FS, server preferences, server defaults, vulnerabilities, testing all known 370 ciphers, client simulation.</p>
|
||||
<p>does a default run on https://testssl.sh (protocols, standard cipher lists, server's cipher preferences, forward secrecy, server defaults, vulnerabilities, client simulation, and rating.</p>
|
||||
|
||||
<pre><code> testssl.sh testssl.net:443
|
||||
</code></pre>
|
||||
@ -604,7 +658,7 @@ where to get this program from.</p>
|
||||
|
||||
<ol class='man-decor man-foot man foot'>
|
||||
<li class='tl'></li>
|
||||
<li class='tc'>January 2020</li>
|
||||
<li class='tc'>May 2020</li>
|
||||
<li class='tr'>testssl(1)</li>
|
||||
</ol>
|
||||
|
||||
|
@ -40,7 +40,7 @@ linked OpenSSL binaries for major operating systems are supplied in `./bin/`.
|
||||
|
||||
1) SSL/TLS protocol check
|
||||
|
||||
2) standard cipher categories to give you upfront an idea for the ciphers supported
|
||||
2) standard cipher categories
|
||||
|
||||
3) server's cipher preferences (server order?)
|
||||
|
||||
@ -52,8 +52,13 @@ linked OpenSSL binaries for major operating systems are supplied in `./bin/`.
|
||||
|
||||
7) vulnerabilities
|
||||
|
||||
8) testing each of 370 preconfigured ciphers
|
||||
|
||||
8) client simulation
|
||||
|
||||
9) rating
|
||||
|
||||
|
||||
|
||||
## OPTIONS AND PARAMETERS
|
||||
|
||||
@ -265,7 +270,7 @@ Also for multiple server certificates are being checked for as well as for the c
|
||||
* `no-openssl`: don't display the OpenSSL cipher suite name, display IANA names only.
|
||||
* `no-iana`: don't display the IANA cipher suite name, display OpenSSL names only.
|
||||
|
||||
Please note that in testssl.sh 3,0 you can still use `rfc` instead of `iana` and `no-rfc` instead of `no-iana` but it'll disappear after 3.0.
|
||||
Please note that in testssl.sh 3.0 you can still use `rfc` instead of `iana` and `no-rfc` instead of `no-iana` but it'll disappear after 3.0.
|
||||
|
||||
`--show-each` This is an option for all wide modes only: it displays all ciphers tested -- not only succeeded ones. `SHOW_EACH_C` is your friend if you prefer to set this via the shell environment.
|
||||
|
||||
@ -284,6 +289,8 @@ Please note that in testssl.sh 3,0 you can still use `rfc` instead of `iana` and
|
||||
5. display bytes received via sockets
|
||||
6. whole 9 yards
|
||||
|
||||
`--disable-rating` disables rating.
|
||||
Rating automatically gets disabled, to not give a wrong or misleading grade, when not all required functions are executed (e.g when checking for a single vulnerabilities).
|
||||
|
||||
|
||||
### FILE OUTPUT OPTIONS
|
||||
@ -381,13 +388,55 @@ Except the environment variables mentioned above which can replace command line
|
||||
* MAX_OSSL_FAIL: A number which tells testssl.sh how often an OpenSSL s_client connect may fail before the program gives up and terminates. The default is 2. You can increase it to a higher value if you frequently see a message like *Fatal error: repeated TCP connect problems, giving up*.
|
||||
* MAX_HEADER_FAIL: A number which tells testssl.sh how often a HTTP GET request over OpenSSL may return an empty file before the program gives up and terminates. The default is 3. Also here you can incerase the threshold when you spot messages like *Fatal error: repeated HTTP header connect problems, doesn't make sense to continue*.
|
||||
|
||||
### RATING
|
||||
This program has a near-complete implementation of SSL Labs's '[SSL Server Rating Guide](https://github.com/ssllabs/research/wiki/SSL-Server-Rating-Guide)'.
|
||||
|
||||
This is *not* a 100% reimplementation of the [SSL Lab's SSL Server Test](https://www.ssllabs.com/ssltest/analyze.html), but an implementation of the above rating specification, slight discrepancies may occur. Please note that for now we stick to the SSL Labs rating as good as possible. We are not responsible for their rating. Before filing issues please inspect their Rating Guide.
|
||||
|
||||
Disclaimer: Having a good grade is **NOT** necessarily equal to having good security! Don't start a competition for the best grade, at least not without monitoring the client handshakes and not without adding a portion of good sense to it.
|
||||
|
||||
As of writing, these checks are missing:
|
||||
* GOLDENDOODLE - should be graded **F** if vulnerable
|
||||
* Insecure renegotiation - should be graded **F** if vulnerable
|
||||
* Padding oracle in AES-NI CBC MAC check (CVE-2016-2107) - should be graded **F** if vulnerable
|
||||
* Sleeping POODLE - should be graded **F** if vulnerable
|
||||
* Zero Length Padding Oracle (CVE-2019-1559) - should be graded **F** if vulnerable
|
||||
* Zombie POODLE - should be graded **F** if vulnerable
|
||||
* All remaining old Symantec PKI certificates are distrusted - should be graded **T**
|
||||
* Symantec certificates issued before June 2016 are distrusted - should be graded **T**
|
||||
* ! A reading of DH params - should give correct points in `set_key_str_score()`
|
||||
* Anonymous key exchange - should give **0** points in `set_key_str_score()`
|
||||
* Exportable key exchange - should give **40** points in `set_key_str_score()`
|
||||
* Weak key (Debian OpenSSL Flaw) - should give **0** points in `set_key_str_score()`
|
||||
|
||||
#### Implementing new grades caps or -warnings
|
||||
To implement a new grading cap, simply call the `set_grade_cap()` function, with the grade and a reason:
|
||||
```bash
|
||||
set_grade_cap "D" "Vulnerable to documentation"
|
||||
```
|
||||
To implement a new grade warning, simply call the `set_grade_warning()` function, with a message:
|
||||
```bash
|
||||
set_grade_warning "Documentation is always right"
|
||||
```
|
||||
#### Implementing a new check which contains grade caps
|
||||
When implementing a new check (be it vulnerability or not) that sets grade caps, the `set_rating_state()` has to be updated (i.e. the `$do_mycheck` variable-name has to be added to the loop, and `$nr_enabled` if-statement has to be incremented)
|
||||
|
||||
The `set_rating_state()` automatically disables ratinng, if all the required checks are *not* enabled.
|
||||
This is to prevent giving out a misleading or wrong grade.
|
||||
|
||||
#### Implementing a new revision
|
||||
When a new revision of the rating specification comes around, the following has to be done:
|
||||
* New grade caps has to be either:
|
||||
1. Added to the script wherever relevant, or
|
||||
2. Added to the above list of missing checks (if *i.* is not possible)
|
||||
* New grade warnings has to be added wherever relevant
|
||||
* The revision output in `run_rating()` function has to updated
|
||||
|
||||
## EXAMPLES
|
||||
|
||||
testssl.sh testssl.sh
|
||||
|
||||
does a default run on https://testssl.sh (protocols, standard cipher lists, FS, server preferences, server defaults, vulnerabilities, testing all known 370 ciphers, client simulation.
|
||||
does a default run on https://testssl.sh (protocols, standard cipher lists, server's cipher preferences, forward secrecy, server defaults, vulnerabilities, client simulation, and rating.
|
||||
|
||||
testssl.sh testssl.net:443
|
||||
|
||||
@ -506,4 +555,3 @@ Probably. Current known ones and interface for filing new ones: https://testssl.
|
||||
## SEE ALSO
|
||||
|
||||
`ciphers`(1), `openssl`(1), `s_client`(1), `x509`(1), `verify`(1), `ocsp`(1), `crl`(1), `bash`(1) and the websites https://testssl.sh/ and https://github.com/drwetter/testssl.sh/ .
|
||||
|
||||
|
498
testssl.sh
498
testssl.sh
@ -140,6 +140,7 @@ declare CMDLINE
|
||||
CMDLINE_PARSED="" # This makes sure we don't let early fatal() write into files when files aren't created yet
|
||||
declare -r -a CMDLINE_ARRAY=("$@") # When performing mass testing, the child processes need to be sent the
|
||||
declare -a MASS_TESTING_CMDLINE # command line in the form of an array (see #702 and https://mywiki.wooledge.org/BashFAQ/050).
|
||||
declare -a SKIP_TESTS=() # This array hold the checks to be skipped
|
||||
|
||||
|
||||
########### Defining (and presetting) variables which can be changed
|
||||
@ -374,6 +375,13 @@ SERVER_COUNTER=0 # Counter for multiple servers
|
||||
TLS_LOW_BYTE="" # For "secret" development stuff, see -q below
|
||||
HEX_CIPHER="" # "
|
||||
|
||||
GRADE_CAP="" # Keeps track of the current grading cap
|
||||
GRADE_CAP_REASONS=() # Keeps track of all the reasons why grades are capped
|
||||
GRADE_WARNINGS=() # Keeps track of all the grade warnings
|
||||
KEY_EXCH_SCORE=0 # Keeps track of the score for category 2 "Key Exchange Strength"
|
||||
CIPH_STR_BEST=0 # Keeps track of the best bit size for category 3 "Cipher Strength"
|
||||
CIPH_STR_WORST=100000 # Keeps track of the worst bit size for category 3 "Cipher Strength"
|
||||
# Intentionally set very high, so it can be set to 0, if necessary
|
||||
|
||||
########### Global variables for parallel mass testing
|
||||
#
|
||||
@ -984,10 +992,102 @@ f5_port_decode() {
|
||||
echo $((16#${tmp:2:2}${tmp:0:2})) # reverse order and convert it from hex to dec
|
||||
}
|
||||
|
||||
###### START ServerHello/OpenSSL/F5 function definitions ######
|
||||
###### END helper function definitions ######
|
||||
###### END universal helper function definitions ######
|
||||
|
||||
|
||||
###### START scoring function definitions ######
|
||||
|
||||
# Sets the grade cap to ARG1
|
||||
# arg1: A grade to set ("A", "B", "C", "D", "E", "F", "M", or "T")
|
||||
# arg2: A reason why (e.g. "Vulnerable to CRIME")
|
||||
set_grade_cap() {
|
||||
"$do_rating" || return 0
|
||||
GRADE_CAP_REASONS+=("Grade capped to $1. $2")
|
||||
|
||||
# Always set special attributes. These are hard caps, due to name mismatch or cert being invalid
|
||||
if [[ "$1" == T || "$1" == M ]]; then
|
||||
GRADE_CAP="$1"
|
||||
# Only keep track of the lowest grade cap, since a higher grade cap wont do anything (F = lowest, A = highest)
|
||||
elif [[ ! "$GRADE_CAP" > "$1" ]]; then
|
||||
GRADE_CAP="$1"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# Sets a grade warning, as specified by the grade specification
|
||||
# arg1: A warning message
|
||||
set_grade_warning() {
|
||||
"$do_rating" || return 0
|
||||
GRADE_WARNINGS+=("$1")
|
||||
return 0
|
||||
}
|
||||
|
||||
# Sets the score for Category 2 (Key Exchange Strength)
|
||||
# arg1: Short key algorithm ("EC", "DH", "RSA", ...) # Can die, when we get DH_PARAMs
|
||||
# arg2: key size (number of bits)
|
||||
set_key_str_score() {
|
||||
local type=$1
|
||||
local size=$2
|
||||
|
||||
"$do_rating" || return 0
|
||||
|
||||
# TODO: We need to get the size of DH params (follows the same table as the "else" clause)
|
||||
# For now, verifying the key size will do...
|
||||
if [[ $type == EC || $type == DH ]]; then
|
||||
if [[ $size -lt 110 ]]; then
|
||||
let KEY_EXCH_SCORE=20
|
||||
set_grade_cap "F" "Using an insecure key"
|
||||
elif [[ $size -lt 123 ]]; then
|
||||
let KEY_EXCH_SCORE=40
|
||||
set_grade_cap "F" "Using an insecure key"
|
||||
elif [[ $size -lt 163 ]]; then
|
||||
let KEY_EXCH_SCORE=80
|
||||
set_grade_cap "B" "Using a weak key"
|
||||
elif [[ $size -lt 225 ]]; then
|
||||
let KEY_EXCH_SCORE=90
|
||||
elif [[ $size -ge 225 ]]; then
|
||||
let KEY_EXCH_SCORE=100
|
||||
else
|
||||
let KEY_EXCH_SCORE=0
|
||||
set_grade_cap "F" "Using an insecure key"
|
||||
fi
|
||||
else
|
||||
if [[ $size -lt 512 ]]; then
|
||||
let KEY_EXCH_SCORE=20
|
||||
set_grade_cap "F" "Using an insecure key"
|
||||
elif [[ $size -lt 1024 ]]; then
|
||||
let KEY_EXCH_SCORE=40
|
||||
set_grade_cap "F" "Using an insecure key"
|
||||
elif [[ $size -lt 2048 ]]; then
|
||||
let KEY_EXCH_SCORE=80
|
||||
set_grade_cap "B" "Using a weak key"
|
||||
elif [[ $size -lt 4096 ]]; then
|
||||
let KEY_EXCH_SCORE=90
|
||||
elif [[ $size -ge 4096 ]]; then
|
||||
let KEY_EXCH_SCORE=100
|
||||
else
|
||||
let KEY_EXCH_SCORE=0
|
||||
set_grade_cap "F" "Using an insecure key"
|
||||
fi
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# Sets the best and worst bit size key, used to grade Category 3 (Cipher Strength)
|
||||
# This function itself doesn't actually set a score; its just in the name to keep it logical (score == grading function)
|
||||
# arg1: a bit size
|
||||
set_ciph_str_score() {
|
||||
local size=$1
|
||||
|
||||
"$do_rating" || return 0
|
||||
|
||||
[[ $size -gt $CIPH_STR_BEST ]] && let CIPH_STR_BEST=$size
|
||||
[[ $size -lt $CIPH_STR_WORST ]] && let CIPH_STR_WORST=$size
|
||||
return 0
|
||||
}
|
||||
|
||||
###### END scoring function definitions ######
|
||||
|
||||
##################### START output file formatting functions #########################
|
||||
#################### START JSON file functions ####################
|
||||
|
||||
@ -1024,6 +1124,7 @@ fileout_json_section() {
|
||||
9) echo -e ",\n \"vulnerabilities\" : [" ;;
|
||||
10) echo -e ",\n \"cipherTests\" : [" ;;
|
||||
11) echo -e ",\n \"browserSimulations\": [" ;;
|
||||
12) echo -e ",\n \"rating\" : [" ;;
|
||||
*) echo "invalid section" ;;
|
||||
esac
|
||||
}
|
||||
@ -1758,12 +1859,14 @@ check_revocation_crl() {
|
||||
out ", "
|
||||
pr_svrty_critical "revoked"
|
||||
fileout "$jsonID" "CRITICAL" "revoked"
|
||||
set_grade_cap "T" "Certificate revoked"
|
||||
else
|
||||
retcode="$(verify_retcode_helper "$retcode")"
|
||||
out " $retcode"
|
||||
retcode="${retcode#(}"
|
||||
retcode="${retcode%)}"
|
||||
fileout "$jsonID" "WARN" "$retcode"
|
||||
set_grade_cap "T" "Issues with certificate $retcode"
|
||||
if [[ $DEBUG -ge 2 ]]; then
|
||||
outln
|
||||
cat "${tmpfile%%.crl}.err"
|
||||
@ -1825,6 +1928,7 @@ check_revocation_ocsp() {
|
||||
out ", "
|
||||
pr_svrty_critical "revoked"
|
||||
fileout "$jsonID" "CRITICAL" "revoked"
|
||||
set_grade_cap "T" "Certificate revoked"
|
||||
else
|
||||
out ", "
|
||||
pr_warning "error querying OCSP responder"
|
||||
@ -2444,15 +2548,18 @@ run_hsts() {
|
||||
if [[ $hsts_age_days -eq -1 ]]; then
|
||||
pr_svrty_medium "misconfiguration: HSTS max-age (recommended > $HSTS_MIN seconds = $((HSTS_MIN/86400)) days ) is required but missing"
|
||||
fileout "${jsonID}_time" "MEDIUM" "misconfiguration, parameter max-age (recommended > $HSTS_MIN seconds = $((HSTS_MIN/86400)) days) missing"
|
||||
set_grade_cap "A" "HSTS max-age is misconfigured"
|
||||
elif [[ $hsts_age_sec -eq 0 ]]; then
|
||||
pr_svrty_low "HSTS max-age is set to 0. HSTS is disabled"
|
||||
fileout "${jsonID}_time" "LOW" "0. HSTS is disabled"
|
||||
set_grade_cap "A" "HSTS is disabled"
|
||||
elif [[ $hsts_age_sec -gt $HSTS_MIN ]]; then
|
||||
pr_svrty_good "$hsts_age_days days" ; out "=$hsts_age_sec s"
|
||||
fileout "${jsonID}_time" "OK" "$hsts_age_days days (=$hsts_age_sec seconds) > $HSTS_MIN seconds"
|
||||
else
|
||||
pr_svrty_medium "$hsts_age_sec s = $hsts_age_days days is too short ( > $HSTS_MIN seconds recommended)"
|
||||
fileout "${jsonID}_time" "MEDIUM" "max-age too short. $hsts_age_days days (=$hsts_age_sec seconds) <= $HSTS_MIN seconds"
|
||||
set_grade_cap "A" "HSTS max-age is too short"
|
||||
fi
|
||||
if includeSubDomains "$TMPFILE"; then
|
||||
fileout "${jsonID}_subdomains" "OK" "includes subdomains"
|
||||
@ -2470,6 +2577,7 @@ run_hsts() {
|
||||
else
|
||||
pr_svrty_low "not offered"
|
||||
fileout "$jsonID" "LOW" "not offered"
|
||||
set_grade_cap "A" "HSTS is not offered"
|
||||
fi
|
||||
outln
|
||||
|
||||
@ -2505,6 +2613,7 @@ run_hpkp() {
|
||||
first_hpkp_header="$(grep -ai '^Public-Key-Pins:' $TMPFILE | head -1)"
|
||||
# we only evaluate the keys here, unless they a not present
|
||||
out "$spaces "
|
||||
set_grade_cap "A" "Problems with HTTP Public Key Pinning (HPKP)"
|
||||
elif [[ $(grep -aci '^Public-Key-Pins-Report-Only:' $TMPFILE) -gt 1 ]]; then
|
||||
outln "Multiple HPKP headers (Report-Only), taking first line"
|
||||
fileout "HPKP_notice" "INFO" "multiple Public-Key-Pins-Report-Only in header"
|
||||
@ -2531,6 +2640,7 @@ run_hpkp() {
|
||||
if [[ $hpkp_nr_keys -eq 1 ]]; then
|
||||
pr_svrty_high "Only one key pinned (NOT ok), means the site may become unavailable in the future, "
|
||||
fileout "HPKP_SPKIs" "HIGH" "Only one key pinned"
|
||||
set_grade_cap "A" "Problems with HTTP Public Key Pinning (HPKP)"
|
||||
else
|
||||
pr_svrty_good "$hpkp_nr_keys"
|
||||
out " keys, "
|
||||
@ -2551,6 +2661,7 @@ run_hpkp() {
|
||||
out "$hpkp_age_sec s = "
|
||||
pr_svrty_medium "$hpkp_age_days days (< $HPKP_MIN s = $((HPKP_MIN / 86400)) days is not good enough)"
|
||||
fileout "HPKP_age" "MEDIUM" "age is set to $hpkp_age_days days ($hpkp_age_sec sec) < $HPKP_MIN s = $((HPKP_MIN / 86400)) days is not good enough."
|
||||
set_grade_cap "A" "Problems with HTTP Public Key Pinning (HPKP)"
|
||||
fi
|
||||
|
||||
if includeSubDomains "$TMPFILE"; then
|
||||
@ -2708,11 +2819,13 @@ run_hpkp() {
|
||||
"$has_backup_spki" && out "$spaces" # we had a few lines with backup SPKIs already
|
||||
prln_svrty_high " No matching key for SPKI found "
|
||||
fileout "HPKP_SPKImatch" "HIGH" "None of the SPKI match your host certificate, intermediate CA or known root CAs. Bricked site?"
|
||||
set_grade_cap "A" "Problems with HTTP Public Key Pinning (HPKP)"
|
||||
fi
|
||||
|
||||
if ! "$has_backup_spki"; then
|
||||
prln_svrty_high " No backup keys found. Loss/compromise of the currently pinned key(s) will lead to bricked site. "
|
||||
fileout "HPKP_backup" "HIGH" "No backup keys found. Loss/compromise of the currently pinned key(s) will lead to bricked site."
|
||||
set_grade_cap "A" "Problems with HTTP Public Key Pinning (HPKP)"
|
||||
fi
|
||||
else
|
||||
outln "--"
|
||||
@ -3329,6 +3442,9 @@ neat_list(){
|
||||
enc="${enc//POLY1305/}" # remove POLY1305
|
||||
enc="${enc//\//}" # remove "/"
|
||||
|
||||
# For rating set bit size
|
||||
set_ciph_str_score $strength
|
||||
|
||||
[[ "$export" =~ export ]] && strength="$strength,exp"
|
||||
|
||||
[[ "$DISPLAY_CIPHERNAMES" != openssl-only ]] && tls_cipher="$(show_rfc_style "$hexcode")"
|
||||
@ -5034,6 +5150,7 @@ run_protocols() {
|
||||
if [[ "$lines" -gt 1 ]]; then
|
||||
nr_ciphers_detected=$((V2_HELLO_CIPHERSPEC_LENGTH / 3))
|
||||
add_proto_offered ssl2 yes
|
||||
set_grade_cap "F" "SSLv2 is offered"
|
||||
if [[ 0 -eq "$nr_ciphers_detected" ]]; then
|
||||
prln_svrty_high "supported but couldn't detect a cipher and vulnerable to CVE-2015-3197 ";
|
||||
fileout "$jsonID" "HIGH" "offered, no cipher" "CVE-2015-3197" "CWE-310"
|
||||
@ -5055,6 +5172,7 @@ run_protocols() {
|
||||
0) prln_svrty_critical "offered (NOT ok)"
|
||||
fileout "$jsonID" "CRITICAL" "offered"
|
||||
add_proto_offered ssl2 yes
|
||||
set_grade_cap "F" "SSLv2 is offered"
|
||||
;;
|
||||
1) prln_svrty_best "not offered (OK)"
|
||||
fileout "$jsonID" "OK" "not offered"
|
||||
@ -5063,6 +5181,8 @@ run_protocols() {
|
||||
5) prln_svrty_high "CVE-2015-3197: $supported_no_ciph2";
|
||||
fileout "$jsonID" "HIGH" "offered, no cipher" "CVE-2015-3197" "CWE-310"
|
||||
add_proto_offered ssl2 yes
|
||||
add_tls_offered ssl2 yes
|
||||
set_grade_cap "F" "SSLv2 is offered"
|
||||
;;
|
||||
7) prln_local_problem "$OPENSSL doesn't support \"s_client -ssl2\""
|
||||
fileout "$jsonID" "INFO" "not tested due to lack of local support"
|
||||
@ -5090,6 +5210,8 @@ run_protocols() {
|
||||
latest_supported_string="SSLv3"
|
||||
fi
|
||||
add_proto_offered ssl3 yes
|
||||
add_tls_offered ssl3 yes
|
||||
set_grade_cap "B" "SSLv3 is offered"
|
||||
;;
|
||||
1) prln_svrty_best "not offered (OK)"
|
||||
fileout "$jsonID" "OK" "not offered"
|
||||
@ -5125,6 +5247,7 @@ run_protocols() {
|
||||
5) pr_svrty_high "$supported_no_ciph1" # protocol detected but no cipher --> comes from run_prototest_openssl
|
||||
fileout "$jsonID" "HIGH" "$supported_no_ciph1"
|
||||
add_proto_offered ssl3 yes
|
||||
set_grade_cap "B" "SSLv3 is offered"
|
||||
;;
|
||||
7) if "$using_sockets" ; then
|
||||
# can only happen in debug mode
|
||||
@ -5156,6 +5279,7 @@ run_protocols() {
|
||||
latest_supported="0301"
|
||||
latest_supported_string="TLSv1.0"
|
||||
add_proto_offered tls1 yes
|
||||
set_grade_cap "B" "TLS 1.0 offered"
|
||||
;; # nothing wrong with it -- per se
|
||||
1) out "not offered"
|
||||
add_proto_offered tls1 no
|
||||
@ -5202,6 +5326,7 @@ run_protocols() {
|
||||
5) outln "$supported_no_ciph1" # protocol detected but no cipher --> comes from run_prototest_openssl
|
||||
fileout "$jsonID" "INFO" "$supported_no_ciph1"
|
||||
add_proto_offered tls1 yes
|
||||
set_grade_cap "B" "TLS 1.0 offered"
|
||||
;;
|
||||
7) if "$using_sockets" ; then
|
||||
# can only happen in debug mode
|
||||
@ -5234,6 +5359,7 @@ run_protocols() {
|
||||
latest_supported="0302"
|
||||
latest_supported_string="TLSv1.1"
|
||||
add_proto_offered tls1_1 yes
|
||||
set_grade_cap "B" "TLS 1.1 offered"
|
||||
;; # nothing wrong with it
|
||||
1) out "not offered"
|
||||
add_proto_offered tls1_1 no
|
||||
@ -5283,6 +5409,7 @@ run_protocols() {
|
||||
5) outln "$supported_no_ciph1" # protocol detected but no cipher --> comes from run_prototest_openssl
|
||||
fileout "$jsonID" "INFO" "$supported_no_ciph1"
|
||||
add_proto_offered tls1_1 yes
|
||||
set_grade_cap "B" "TLS 1.1 offered"
|
||||
;;
|
||||
7) if "$using_sockets" ; then
|
||||
# can only happen in debug mode
|
||||
@ -5358,6 +5485,7 @@ run_protocols() {
|
||||
fileout "$jsonID" "INFO" "not offered"
|
||||
else
|
||||
fileout "$jsonID" "MEDIUM" "not offered" # TLS 1.3, no TLS 1.2 --> no GCM, penalty
|
||||
set_grade_cap "C" "TLS 1.2 or TLS 1.3 are not offered"
|
||||
fi
|
||||
else
|
||||
prln_svrty_critical " -- connection failed rather than downgrading to $latest_supported_string"
|
||||
@ -5365,6 +5493,7 @@ run_protocols() {
|
||||
fi
|
||||
;;
|
||||
2) add_proto_offered tls1_2 no
|
||||
set_grade_cap "C" "TLS 1.2 is not offered"
|
||||
pr_svrty_medium "not offered and downgraded to a weaker protocol"
|
||||
if [[ "$tls12_detected_version" == 0300 ]]; then
|
||||
detected_version_string="SSLv3"
|
||||
@ -5393,12 +5522,14 @@ run_protocols() {
|
||||
3) out "not offered, "
|
||||
fileout "$jsonID" "INFO" "not offered"
|
||||
add_proto_offered tls1_2 no
|
||||
set_grade_cap "C" "TLS 1.2 is not offered"
|
||||
pr_warning "TLS downgraded to STARTTLS plaintext"; outln
|
||||
fileout "$jsonID" "WARN" "TLS downgraded to STARTTLS plaintext"
|
||||
;;
|
||||
4) out "likely "; pr_svrty_medium "not offered, "
|
||||
fileout "$jsonID" "MEDIUM" "not offered"
|
||||
add_proto_offered tls1_2 no
|
||||
set_grade_cap "C" "TLS 1.2 is not offered"
|
||||
pr_warning "received 4xx/5xx after STARTTLS handshake"; outln "$debug_recomm"
|
||||
fileout "$jsonID" "WARN" "received 4xx/5xx after STARTTLS handshake${debug_recomm}"
|
||||
;;
|
||||
@ -5765,6 +5896,10 @@ sub_cipherlists() {
|
||||
((ret++))
|
||||
;;
|
||||
esac
|
||||
|
||||
# Not a perfect place here. A new one should be picked in the future
|
||||
[[ $sclient_success -eq 0 && "$1" =~ (^|:)EXPORT(:|$) ]] && set_grade_cap "F" "Export suite offered"
|
||||
[[ $sclient_success -eq 0 && "$1" =~ AEAD ]] && set_grade_cap "B" "No AEAD ciphers offered"
|
||||
fi
|
||||
tmpfile_handle ${FUNCNAME[0]}.${5}.txt
|
||||
[[ $DEBUG -ge 1 ]] && tm_out " -- $1"
|
||||
@ -6952,17 +7087,17 @@ verify_retcode_helper() {
|
||||
|
||||
case $retcode in
|
||||
# codes from ./doc/apps/verify.pod | verify(1ssl)
|
||||
44) tm_out "(different CRL scope)" ;; # X509_V_ERR_DIFFERENT_CRL_SCOPE
|
||||
26) tm_out "(unsupported certificate purpose)" ;; # X509_V_ERR_INVALID_PURPOSE
|
||||
24) tm_out "(certificate unreadable)" ;; # X509_V_ERR_INVALID_CA
|
||||
23) tm_out "(certificate revoked)" ;; # X509_V_ERR_CERT_REVOKED
|
||||
21) tm_out "(chain incomplete, only 1 cert provided)" ;; # X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE
|
||||
20) tm_out "(chain incomplete)" ;; # X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY
|
||||
19) tm_out "(self signed CA in chain)" ;; # X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN
|
||||
18) tm_out "(self signed)" ;; # X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT
|
||||
10) tm_out "(expired)" ;; # X509_V_ERR_CERT_HAS_EXPIRED
|
||||
9) tm_out "(not yet valid)" ;; # X509_V_ERR_CERT_NOT_YET_VALID
|
||||
2) tm_out "(issuer cert missing)" ;; # X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT
|
||||
44) tm_out "(different CRL scope)" ;; # X509_V_ERR_DIFFERENT_CRL_SCOPE
|
||||
26) tm_out "(unsupported certificate purpose)" ;; # X509_V_ERR_INVALID_PURPOSE
|
||||
24) tm_out "(certificate unreadable)" ;; # X509_V_ERR_INVALID_CA
|
||||
23) tm_out "(certificate revoked)" ;; # X509_V_ERR_CERT_REVOKED
|
||||
21) tm_out "(chain incomplete, only 1 cert provided)" ;; # X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE
|
||||
20) tm_out "(chain incomplete)" ;; # X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY
|
||||
19) tm_out "(self signed CA in chain)" ;; # X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN
|
||||
18) tm_out "(self signed)" ;; # X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT
|
||||
10) tm_out "(expired)" ;; # X509_V_ERR_CERT_HAS_EXPIRED
|
||||
9) tm_out "(not yet valid)" ;; # X509_V_ERR_CERT_NOT_YET_VALID
|
||||
2) tm_out "(issuer cert missing)" ;; # X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT
|
||||
*) ret=1 ; tm_out " (unknown, pls report) $1" ;;
|
||||
esac
|
||||
return $ret
|
||||
@ -7058,6 +7193,7 @@ determine_trust() {
|
||||
out "$code"
|
||||
fi
|
||||
fileout "${jsonID}${json_postfix}" "CRITICAL" "failed $code. $addtl_warning"
|
||||
set_grade_cap "T" "Issues with certificate $code"
|
||||
else
|
||||
# is one ok and the others not ==> display the culprit store
|
||||
if "$some_ok"; then
|
||||
@ -7076,6 +7212,7 @@ determine_trust() {
|
||||
out "$code"
|
||||
fi
|
||||
notok_was="${certificate_file[i]} $code $notok_was"
|
||||
set_grade_cap "T" "Issues with certificate $code"
|
||||
fi
|
||||
done
|
||||
#pr_svrty_high "$notok_was "
|
||||
@ -8193,6 +8330,7 @@ certificate_info() {
|
||||
fi
|
||||
outln
|
||||
fileout "${jsonID}${json_postfix}" "MEDIUM" "SHA1 with RSA"
|
||||
set_grade_cap "T" "Uses SHA1 algorithm"
|
||||
;;
|
||||
sha224WithRSAEncryption)
|
||||
outln "SHA224 with RSA"
|
||||
@ -8213,6 +8351,7 @@ certificate_info() {
|
||||
ecdsa-with-SHA1)
|
||||
prln_svrty_medium "ECDSA with SHA1"
|
||||
fileout "${jsonID}${json_postfix}" "MEDIUM" "ECDSA with SHA1"
|
||||
set_grade_cap "T" "Uses SHA1 algorithm"
|
||||
;;
|
||||
ecdsa-with-SHA224)
|
||||
outln "ECDSA with SHA224"
|
||||
@ -8233,6 +8372,7 @@ certificate_info() {
|
||||
dsaWithSHA1)
|
||||
prln_svrty_medium "DSA with SHA1"
|
||||
fileout "${jsonID}${json_postfix}" "MEDIUM" "DSA with SHA1"
|
||||
set_grade_cap "T" "Uses SHA1 algorithm"
|
||||
;;
|
||||
dsa_with_SHA224)
|
||||
outln "DSA with SHA224"
|
||||
@ -8248,6 +8388,7 @@ certificate_info() {
|
||||
sha1)
|
||||
prln_svrty_medium "RSASSA-PSS with SHA1"
|
||||
fileout "${jsonID}${json_postfix}" "MEDIUM" "RSASSA-PSS with SHA1"
|
||||
set_grade_cap "T" "Uses SHA1 algorithm"
|
||||
;;
|
||||
sha224)
|
||||
outln "RSASSA-PSS with SHA224"
|
||||
@ -8274,6 +8415,7 @@ certificate_info() {
|
||||
md2*)
|
||||
prln_svrty_critical "MD2"
|
||||
fileout "${jsonID}${json_postfix}" "CRITICAL" "MD2"
|
||||
set_grade_cap "F" "Supports a insecure signature (MD2)"
|
||||
;;
|
||||
md4*)
|
||||
prln_svrty_critical "MD4"
|
||||
@ -8282,6 +8424,7 @@ certificate_info() {
|
||||
md5*)
|
||||
prln_svrty_critical "MD5"
|
||||
fileout "${jsonID}${json_postfix}" "CRITICAL" "MD5"
|
||||
set_grade_cap "F" "Supports a insecure signature (MD5)"
|
||||
;;
|
||||
*)
|
||||
out "$cert_sig_algo ("
|
||||
@ -8336,6 +8479,8 @@ certificate_info() {
|
||||
((ret++))
|
||||
fi
|
||||
outln " bits"
|
||||
|
||||
set_key_str_score "$short_keyAlgo" "$cert_keysize" # TODO: should be $dh_param_size
|
||||
elif [[ $cert_key_algo =~ RSA ]] || [[ $cert_key_algo =~ rsa ]] || [[ $cert_key_algo =~ dsa ]] || \
|
||||
[[ $cert_key_algo =~ dhKeyAgreement ]] || [[ $cert_key_algo == X9.42\ DH ]]; then
|
||||
if [[ "$cert_keysize" -le 512 ]]; then
|
||||
@ -8362,6 +8507,8 @@ certificate_info() {
|
||||
fileout "${jsonID}${json_postfix}" "WARN" "$cert_keysize bits (Odd)"
|
||||
((ret++))
|
||||
fi
|
||||
|
||||
set_key_str_score "$short_keyAlgo" "$cert_keysize"
|
||||
else
|
||||
out "$cert_key_algo + $cert_keysize bits ("
|
||||
pr_warning "FIXME: can't tell whether this is good or not"
|
||||
@ -8528,6 +8675,7 @@ certificate_info() {
|
||||
if [[ "$issuer_O" == "issuer=" ]] || [[ "$issuer_O" == "issuer= " ]] || [[ "$issuer_CN" == "$cn" ]]; then
|
||||
prln_svrty_critical "self-signed (NOT ok)"
|
||||
fileout "${jsonID}${json_postfix}" "CRITICAL" "selfsigned"
|
||||
set_grade_cap "T" "Self-signed certificate"
|
||||
else
|
||||
issuerfinding="$issuer_CN"
|
||||
pr_italic "$issuer_CN"
|
||||
@ -8571,7 +8719,9 @@ certificate_info() {
|
||||
has_dns_sans=$HAS_DNS_SANS
|
||||
|
||||
case $trust_sni in
|
||||
0) trustfinding="certificate does not match supplied URI" ;;
|
||||
0) trustfinding="certificate does not match supplied URI"
|
||||
set_grade_cap "M" "Domain name mismatch"
|
||||
;;
|
||||
1) trustfinding="Ok via SAN" ;;
|
||||
2) trustfinding="Ok via SAN wildcard" ;;
|
||||
4) if "$has_dns_sans"; then
|
||||
@ -8676,6 +8826,7 @@ certificate_info() {
|
||||
# Shortcut for this special case here.
|
||||
pr_italic "WoSign/StartCom"; out " are " ; prln_svrty_critical "not trusted anymore (NOT ok)"
|
||||
fileout "${jsonID}${json_postfix}" "CRITICAL" "Issuer not trusted anymore (WoSign/StartCom)"
|
||||
set_grade_cap "T" "Untrusted certificate chain"
|
||||
else
|
||||
# Also handles fileout, keep error if happened
|
||||
determine_trust "$jsonID" "$json_postfix" || ((ret++))
|
||||
@ -8768,6 +8919,7 @@ certificate_info() {
|
||||
pr_svrty_critical "expired"
|
||||
expfinding="expired"
|
||||
expok="CRITICAL"
|
||||
set_grade_cap "T" "Certificate expired"
|
||||
else
|
||||
secs2warn=$((24 * 60 * 60 * days2warn2)) # low threshold first
|
||||
expire=$($OPENSSL x509 -in $HOSTCERT -checkend $secs2warn 2>>$ERRFILE)
|
||||
@ -9562,8 +9714,9 @@ run_fs() {
|
||||
|
||||
if [[ $sclient_success -ne 0 ]]; then
|
||||
outln
|
||||
prln_svrty_medium " No ciphers supporting Forward Secrecy offered"
|
||||
fileout "$jsonID" "MEDIUM" "No ciphers supporting (P)FS offered"
|
||||
prln_svrty_medium " No ciphers supporting Forward Secrecy (FS) offered"
|
||||
fileout "$jsonID" "MEDIUM" "No ciphers supporting Forward Secrecy offered"
|
||||
set_grade_cap "B" "Forward Secrecy (FS) is not supported"
|
||||
else
|
||||
outln
|
||||
fs_offered=true
|
||||
@ -14920,6 +15073,7 @@ run_heartbleed(){
|
||||
else
|
||||
pr_svrty_critical "VULNERABLE (NOT ok)"
|
||||
fileout "$jsonID" "CRITICAL" "VULNERABLE $cve" "$cwe" "$hint"
|
||||
set_grade_cap "F" "Vulnerable to Heartbleed"
|
||||
fi
|
||||
else
|
||||
pr_svrty_best "not vulnerable (OK)"
|
||||
@ -15078,6 +15232,7 @@ run_ccs_injection(){
|
||||
# decryption failed received
|
||||
pr_svrty_critical "VULNERABLE (NOT ok)"
|
||||
fileout "$jsonID" "CRITICAL" "VULNERABLE" "$cve" "$cwe" "$hint"
|
||||
set_grade_cap "F" "Vulnerable to CCS injection"
|
||||
elif [[ "$byte6" == "0A" ]] || [[ "$byte6" == "28" ]]; then
|
||||
# Unexpected message / Handshake failure received
|
||||
pr_warning "likely "
|
||||
@ -15378,6 +15533,7 @@ run_ticketbleed() {
|
||||
if [[ ${memory[1]} != ${memory[2]} ]] && [[ ${memory[2]} != ${memory[3]} ]]; then
|
||||
pr_svrty_critical "VULNERABLE (NOT ok)"
|
||||
fileout "$jsonID" "CRITICAL" "VULNERABLE" "$cve" "$cwe" "$hint"
|
||||
set_grade_cap "F" "Vulnerable to Ticketbleed"
|
||||
else
|
||||
pr_svrty_best "not vulnerable (OK)"
|
||||
out ", session IDs were returned but potential memory fragments do not differ"
|
||||
@ -15435,6 +15591,7 @@ run_renego() {
|
||||
case $sec_renego in
|
||||
0) prln_svrty_critical "Not supported / VULNERABLE (NOT ok)"
|
||||
fileout "$jsonID" "CRITICAL" "VULNERABLE" "$cve" "$cwe" "$hint"
|
||||
set_grade_warning "Secure renegotiation is not supported"
|
||||
;;
|
||||
1) prln_svrty_best "supported (OK)"
|
||||
fileout "$jsonID" "OK" "supported" "$cve" "$cwe"
|
||||
@ -15618,6 +15775,7 @@ run_crime() {
|
||||
# not clear whether a protocol != HTTP offers the ability to repeatedly modify the input
|
||||
# which is done e.g. via javascript in the context of HTTP
|
||||
fi
|
||||
set_grade_cap "C" "Vulnerable to CRIME"
|
||||
fi
|
||||
outln
|
||||
|
||||
@ -15750,6 +15908,7 @@ run_sweet32() {
|
||||
local -i nr_sweet32_ciphers=0 nr_supported_ciphers=0 nr_ssl2_sweet32_ciphers=0 nr_ssl2_supported_ciphers=0
|
||||
local ssl2_sweet=false
|
||||
local using_sockets=true
|
||||
local tls1_1_vulnerable=false
|
||||
|
||||
[[ $VULN_COUNT -le $VULN_THRESHLD ]] && outln && pr_headlineln " Testing for SWEET32 (Birthday Attacks on 64-bit Block Ciphers) " && outln
|
||||
pr_bold " SWEET32"; out " (${cve// /, }) "
|
||||
@ -15810,6 +15969,7 @@ run_sweet32() {
|
||||
sclient_connect_successful $? $TMPFILE
|
||||
sclient_success=$?
|
||||
[[ $DEBUG -ge 2 ]] && grep -Eq "error|failure" $ERRFILE | grep -Eav "unable to get local|verify error"
|
||||
[[ $proto == -tls1_1 && $sclient_success -eq 0 ]] && tls1_1_vulnerable=true
|
||||
[[ $sclient_success -eq 0 ]] && break
|
||||
done
|
||||
if "$HAS_SSL2"; then
|
||||
@ -15829,9 +15989,11 @@ run_sweet32() {
|
||||
if [[ $sclient_success -eq 0 ]] && "$ssl2_sweet" ; then
|
||||
pr_svrty_low "VULNERABLE"; out ", uses 64 bit block ciphers for SSLv2 and above"
|
||||
fileout "SWEET32" "LOW" "uses 64 bit block ciphers for SSLv2 and above" "$cve" "$cwe" "$hint"
|
||||
"$tls1_1_vulnerable" && set_grade_cap "C" "Uses 64 bit block ciphers with TLS 1.1 (vulnerable to SWEET32)"
|
||||
elif [[ $sclient_success -eq 0 ]]; then
|
||||
pr_svrty_low "VULNERABLE"; out ", uses 64 bit block ciphers"
|
||||
fileout "SWEET32" "LOW" "uses 64 bit block ciphers" "$cve" "$cwe" "$hint"
|
||||
"$tls1_1_vulnerable" && set_grade_cap "C" "Uses 64 bit block ciphers with TLS 1.1 (vulnerable to SWEET32)"
|
||||
elif "$ssl2_sweet"; then
|
||||
pr_svrty_low "VULNERABLE"; out ", uses 64 bit block ciphers wth SSLv2 only"
|
||||
fileout "SWEET32" "LOW" "uses 64 bit block ciphers with SSLv2 only" "$cve" "$cwe" "$hint"
|
||||
@ -15913,6 +16075,7 @@ run_ssl_poodle() {
|
||||
POODLE=0
|
||||
pr_svrty_high "VULNERABLE (NOT ok)"; out ", uses SSLv3+CBC (check TLS_FALLBACK_SCSV mitigation below)"
|
||||
fileout "$jsonID" "HIGH" "VULNERABLE, uses SSLv3+CBC" "$cve" "$cwe" "$hint"
|
||||
set_grade_cap "C" "Vulnerable to POODLE"
|
||||
else
|
||||
POODLE=1
|
||||
pr_svrty_best "not vulnerable (OK)";
|
||||
@ -15943,6 +16106,8 @@ run_tls_poodle() {
|
||||
#FIXME
|
||||
prln_warning "#FIXME"
|
||||
fileout "$jsonID" "WARN" "Not yet implemented #FIXME" "$cve" "$cwe"
|
||||
# set_grade_cap "F" "Vulnerable to POODLE TLS"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
@ -16090,15 +16255,18 @@ run_tls_fallback_scsv() {
|
||||
if [[ -z "$POODLE" ]]; then
|
||||
pr_warning "Rerun including POODLE SSL check. "
|
||||
pr_svrty_medium "Downgrade attack prevention NOT supported"
|
||||
fileout "$jsonID" "WARN" "NOT supported. Pls rerun wity POODLE SSL check"
|
||||
fileout "$jsonID" "WARN" "NOT supported. Pls rerun with POODLE SSL check"
|
||||
ret=1
|
||||
elif [[ "$POODLE" -eq 0 ]]; then
|
||||
pr_svrty_high "Downgrade attack prevention NOT supported and vulnerable to POODLE SSL"
|
||||
fileout "$jsonID" "HIGH" "NOT supported and vulnerable to POODLE SSL"
|
||||
set_grade_cap "C" "Vulnerable to POODLE"
|
||||
else
|
||||
pr_svrty_medium "Downgrade attack prevention NOT supported"
|
||||
fileout "$jsonID" "MEDIUM" "NOT supported"
|
||||
fi
|
||||
set_grade_cap "A" "Does not support TLS_FALLBACK_SCSV"
|
||||
|
||||
elif grep -qa "alert inappropriate fallback" "$TMPFILE"; then
|
||||
pr_svrty_good "Downgrade attack prevention supported (OK)"
|
||||
fileout "$jsonID" "OK" "supported"
|
||||
@ -16441,6 +16609,7 @@ run_logjam() {
|
||||
if "$vuln_exportdh_ciphers"; then
|
||||
pr_svrty_high "VULNERABLE (NOT ok):"; out " uses DH EXPORT ciphers"
|
||||
fileout "$jsonID" "HIGH" "VULNERABLE, uses DH EXPORT ciphers" "$cve" "$cwe" "$hint"
|
||||
set_grade_cap "B" "Uses weak DH key exchange parameters (vulnerable to LOGJAM)"
|
||||
if [[ $subret -eq 3 ]]; then
|
||||
out ", no DH key detected with <= TLS 1.2"
|
||||
fileout "$jsonID2" "OK" "no DH key detected with <= TLS 1.2"
|
||||
@ -16456,6 +16625,7 @@ run_logjam() {
|
||||
else
|
||||
if [[ $subret -eq 1 ]]; then
|
||||
out_common_prime "$jsonID2" "$cve" "$cwe"
|
||||
set_grade_cap "A" "Uses known DH key exchange parameters"
|
||||
if ! "$openssl_no_expdhciphers"; then
|
||||
outln ","
|
||||
out "${spaces}but no DH EXPORT ciphers${addtl_warning}"
|
||||
@ -16553,6 +16723,7 @@ run_drown() {
|
||||
else
|
||||
prln_svrty_critical "VULNERABLE (NOT ok), SSLv2 offered with $nr_ciphers_detected ciphers";
|
||||
fileout "$jsonID" "CRITICAL" "VULNERABLE, SSLv2 offered with $nr_ciphers_detected ciphers. Make sure you don't use this certificate elsewhere, see https://censys.io/ipv4?q=$cert_fingerprint_sha2" "$cve" "$cwe" "$hint"
|
||||
set_grade_cap "F" "Vulnerable to DROWN"
|
||||
fi
|
||||
outln "$spaces Make sure you don't use this certificate elsewhere, see:"
|
||||
out "$spaces "
|
||||
@ -16866,6 +17037,7 @@ run_beast(){
|
||||
pr_svrty_medium "VULNERABLE"
|
||||
outln " -- and no higher protocols as mitigation supported"
|
||||
fileout "$jsonID" "MEDIUM" "VULNERABLE -- and no higher protocols as mitigation supported" "$cve" "$cwe" "$hint"
|
||||
set_grade_cap "B" "Vulnerable to BEAST"
|
||||
fi
|
||||
fi
|
||||
"$first" && ! "$vuln_beast" && prln_svrty_good "no CBC ciphers found for any protocol (OK)"
|
||||
@ -17113,6 +17285,11 @@ run_rc4() {
|
||||
fi
|
||||
"$WIDE" && "$SHOW_SIGALGO" && grep -q "\-\-\-\-\-BEGIN CERTIFICATE\-\-\-\-\-" $TMPFILE && \
|
||||
sigalg[i]="$(read_sigalg_from_file "$TMPFILE")"
|
||||
|
||||
# If you use RC4 with newer protocols, you are punished harder
|
||||
if [[ "$proto" == -tls1_1 ]]; then
|
||||
set_grade_cap "C" "RC4 ciphers offered on TLS 1.1"
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
@ -17193,6 +17370,7 @@ run_rc4() {
|
||||
outln
|
||||
"$WIDE" && out " " && prln_svrty_high "VULNERABLE (NOT ok)"
|
||||
fileout "$jsonID" "HIGH" "VULNERABLE, Detected ciphers: $rc4_detected" "$cve" "$cwe" "$hint"
|
||||
set_grade_cap "B" "RC4 ciphers offered"
|
||||
elif [[ $nr_ciphers -eq 0 ]]; then
|
||||
prln_local_problem "No RC4 Ciphers configured in $OPENSSL"
|
||||
fileout "$jsonID" "WARN" "RC4 ciphers not supported by local OpenSSL ($OPENSSL)"
|
||||
@ -17837,6 +18015,7 @@ run_robot() {
|
||||
prln_svrty_critical "VULNERABLE (NOT ok)"
|
||||
fileout "$jsonID" "CRITICAL" "VULNERABLE" "$cve" "$cwe"
|
||||
fi
|
||||
set_grade_cap "F" "Vulnerable to ROBOT"
|
||||
else
|
||||
prln_svrty_best "not vulnerable (OK)"
|
||||
fileout "$jsonID" "OK" "not vulnerable" "$cve" "$cwe"
|
||||
@ -18340,6 +18519,7 @@ output options (can also be preset via environment variables):
|
||||
--color <0|1|2|3> 0: no escape or other codes, 1: b/w escape codes, 2: color (default), 3: extra color (color all ciphers)
|
||||
--colorblind swap green and blue in the output
|
||||
--debug <0-6> 1: screen output normal but keeps debug output in /tmp/. 2-6: see "grep -A 5 '^DEBUG=' testssl.sh"
|
||||
--disable-rating Explicitly disables the rating output
|
||||
|
||||
file output options (can also be preset via environment variables)
|
||||
--log, --logging logs stdout to '\${NODE}-p\${port}\${YYYYMMDD-HHMM}.log' in current working directory (cwd)
|
||||
@ -20315,6 +20495,224 @@ run_mass_testing_parallel() {
|
||||
return $?
|
||||
}
|
||||
|
||||
run_rating() {
|
||||
local final_score pre_cap_grade final_grade
|
||||
local c1_score c2_score c3_score c1_wscore c2_wscore c3_wscore
|
||||
local c1_worst c1_best
|
||||
local c3_worst c3_best c3_worst_cb c3_best_cb
|
||||
local old_ifs=$IFS sorted_reasons sorted_warnings reason_nr=0 warning_nr=0
|
||||
|
||||
outln "\n";
|
||||
pr_headlineln " Rating (experimental) "
|
||||
outln
|
||||
|
||||
if [[ -n "$STARTTLS_PROTOCOL" ]]; then
|
||||
pr_bold " Grade "; pr_svrty_critical "T"
|
||||
outln " - STARTTLS encryption is opportunistic"
|
||||
outln " (Further details would lead to a false sense of security)"
|
||||
fileout "grade" "CRITICAL" "T"
|
||||
fileout "grade_cap_reasons" "INFO" "No more details shown as it would lead to a false sense of security"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Sort the reasons. This is just nicer to read in genereal
|
||||
IFS=$'\n' sorted_reasons=($(sort -ru <<<"${GRADE_CAP_REASONS[*]}"))
|
||||
IFS=$'\n' sorted_warnings=($(sort -u <<<"${GRADE_WARNINGS[*]}"))
|
||||
IFS=$old_ifs
|
||||
pr_bold " Rating specs"; out " (not complete) "; outln "SSL Labs's 'SSL Server Rating Guide' (version 2009q from 2020-01-30)"
|
||||
pr_bold " Specification documentation "; pr_url "https://github.com/ssllabs/research/wiki/SSL-Server-Rating-Guide"
|
||||
outln
|
||||
fileout "rating_spec" "INFO" "SSL Labs's 'SSL Server Rating Guide' (version 2009q from 2020-01-30)"
|
||||
|
||||
# No point in calculating a score, if a cap of "F", "T", or "M" has been set
|
||||
if [[ $GRADE_CAP == F || $GRADE_CAP == T || $GRADE_CAP == M ]]; then
|
||||
pr_bold " Protocol Support"; out " (weighted) "; outln "0 (0)"
|
||||
pr_bold " Key Exchange"; out " (weighted) "; outln "0 (0)"
|
||||
pr_bold " Cipher Strength"; out " (weighted) "; outln "0 (0)"
|
||||
pr_bold " Final Score "; outln "0"
|
||||
pr_bold " Overall Grade "; prln_svrty_critical "$GRADE_CAP"
|
||||
fileout "grade" "CRITICAL" "$GRADE_CAP"
|
||||
else
|
||||
## Category 1
|
||||
# get best score, by searching for the best protocol, until a hit occurs
|
||||
if [[ $(has_server_protocol "tls1_3") -eq 0 || $(has_server_protocol "tls1_2") -eq 0 ]]; then
|
||||
c1_best=100
|
||||
elif [[ $(has_server_protocol "tls1_1") -eq 0 ]]; then
|
||||
c1_best=95
|
||||
elif [[ $(has_server_protocol "tls1") -eq 0 ]]; then
|
||||
c1_best=90
|
||||
elif [[ $(has_server_protocol "ssl3") -eq 0 ]]; then
|
||||
c1_best=80
|
||||
# If the best protocol offered is SSLv3, cap to F. It is easier done here
|
||||
set_grade_cap "F" "SSLv3 is the best protocol offered"
|
||||
else # SSLv2 gives 0 points
|
||||
c1_best=0
|
||||
fi
|
||||
|
||||
# get worst score, by searching for the worst protcol, until a hit occurs
|
||||
if [[ $(has_server_protocol "ssl2") -eq 0 ]]; then
|
||||
c1_worst=0
|
||||
elif [[ $(has_server_protocol "ssl3") -eq 0 ]]; then
|
||||
c1_worst=80
|
||||
elif [[ $(has_server_protocol "tls1") -eq 0 ]]; then
|
||||
c1_worst=90
|
||||
elif [[ $(has_server_protocol "tls1_1") -eq 0 ]]; then
|
||||
c1_worst=95
|
||||
else # TLS1.2 and TLS1.3 both give 100 points
|
||||
c1_worst=100
|
||||
fi
|
||||
|
||||
let c1_score="($c1_best+$c1_worst)/2" # Gets the category score
|
||||
let c1_wscore=$c1_score*30/100 # Gets the weighted score for category (30%)
|
||||
|
||||
pr_bold " Protocol Support "; out "(weighted) "; outln "$c1_score ($c1_wscore)"
|
||||
|
||||
## Category 2
|
||||
let c2_score=$KEY_EXCH_SCORE
|
||||
let c2_wscore=$c2_score*30/100
|
||||
|
||||
pr_bold " Key Exchange "; out " (weighted) "; outln "$c2_score ($c2_wscore)"
|
||||
|
||||
|
||||
## Category 3
|
||||
# Get the cipher bits sizes for the best cipher, and the worst cipher
|
||||
c3_best_cb=$CIPH_STR_BEST
|
||||
c3_worst_cb=$CIPH_STR_WORST
|
||||
|
||||
# Determine score for the best key
|
||||
if [[ $c3_best_cb -ge 256 ]]; then
|
||||
c3_best=100
|
||||
elif [[ $c3_best_cb -ge 128 ]]; then
|
||||
c3_best=80
|
||||
elif [[ $c3_best_cb -ge 0 ]]; then
|
||||
c3_best=20
|
||||
else
|
||||
c3_best=0
|
||||
fi
|
||||
|
||||
# Determine the score for the worst key
|
||||
if [[ $c3_worst_cb -gt 0 && $c3_worst_cb -lt 128 ]]; then
|
||||
c3_worst=20
|
||||
elif [[ $c3_worst_cb -lt 256 ]]; then
|
||||
c3_worst=80
|
||||
elif [[ $c3_worst_cb -ge 256 ]]; then
|
||||
c3_worst=100
|
||||
else
|
||||
c3_worst=0
|
||||
fi
|
||||
let c3_score="($c3_best+$c3_worst)/2" # Gets the category score
|
||||
let c3_wscore=$c3_score*40/100 # Gets the weighted score for category (40%)
|
||||
|
||||
pr_bold " Cipher Strength "; out " (weighted) "; outln "$c3_score ($c3_wscore)"
|
||||
|
||||
## Calculate final score and grade
|
||||
let final_score=$c1_wscore+$c2_wscore+$c3_wscore
|
||||
|
||||
pr_bold " Final Score "; outln $final_score
|
||||
|
||||
# get score, and somehow do something about the GRADE_CAP
|
||||
if [[ $final_score -ge 80 ]]; then
|
||||
pre_cap_grade="A"
|
||||
elif [[ $final_score -ge 65 ]]; then
|
||||
pre_cap_grade="B"
|
||||
elif [[ $final_score -ge 50 ]]; then
|
||||
pre_cap_grade="C"
|
||||
elif [[ $final_score -ge 35 ]]; then
|
||||
pre_cap_grade="D"
|
||||
elif [[ $final_score -ge 20 ]]; then
|
||||
pre_cap_grade="E"
|
||||
elif [[ $final_score -lt 20 ]]; then
|
||||
pre_cap_grade="F"
|
||||
fi
|
||||
|
||||
# If the calculated grade is bigger than the grade cap, then set grade as the cap
|
||||
if [[ -n "$GRADE_CAP" && ! $pre_cap_grade > $GRADE_CAP ]]; then
|
||||
final_grade=$GRADE_CAP
|
||||
# For "exceptional" config, an "A+" is awarded, or "A-" for slightly less "exceptional"
|
||||
elif [[ -z "$GRADE_CAP" && $pre_cap_grade == A ]]; then
|
||||
if [[ ${#sorted_warnings[@]} -eq 0 ]]; then
|
||||
final_grade="A+"
|
||||
else
|
||||
final_grade="A-"
|
||||
fi
|
||||
else
|
||||
final_grade=$pre_cap_grade
|
||||
fi
|
||||
|
||||
pr_bold " Overall Grade "
|
||||
case "$final_grade" in
|
||||
A*) prln_svrty_best $final_grade
|
||||
fileout "grade" "OK" "$final_grade"
|
||||
;;
|
||||
B) prln_svrty_medium $final_grade
|
||||
fileout "grade" "MEDIUM" "$final_grade"
|
||||
;;
|
||||
C) prln_svrty_medium $final_grade
|
||||
fileout "grade" "MEDIUM" "$final_grade"
|
||||
;;
|
||||
D) prln_svrty_high $final_grade
|
||||
fileout "grade" "HIGH" "$final_grade"
|
||||
;;
|
||||
E) prln_svrty_high $final_grade
|
||||
fileout "grade" "HIGH" "$final_grade"
|
||||
;;
|
||||
F) prln_svrty_critical $final_grade
|
||||
fileout "grade" "CRITICAL" "$final_grade"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Pretty print - again, it's just nicer to read
|
||||
for reason in "${sorted_reasons[@]}"; do
|
||||
if [[ $reason_nr -eq 0 ]]; then
|
||||
pr_bold " Grade cap reasons "; outln "$reason"
|
||||
else
|
||||
outln " $reason"
|
||||
fi
|
||||
let reason_nr++
|
||||
fileout "grade_cap_reason_${reason_nr}" "INFO" "$reason"
|
||||
done
|
||||
|
||||
for warning in "${sorted_warnings[@]}"; do
|
||||
if [[ $warning_nr -eq 0 ]]; then
|
||||
pr_bold " Grade warning "; prln_svrty_medium "$warning"
|
||||
else
|
||||
prln_svrty_medium " $warning"
|
||||
fi
|
||||
let warning_nr++
|
||||
fileout "grade_cap_warning_${warning_nr}" "INFO" "$warning"
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Checks whether rating can be done or not.
|
||||
# Rating needs a mix of certificate and vulnerabilities checks, in order to give out proper grades.
|
||||
# This function disables rating, if not all required checks are enabled
|
||||
# Returns "0" if rating is enabled, and "1" if rating is disabled
|
||||
set_rating_state() {
|
||||
local gbl
|
||||
local nr_enabled=0
|
||||
|
||||
# All of these should be enabled
|
||||
for gbl in do_protocols do_cipherlists do_fs do_server_defaults do_header \
|
||||
do_heartbleed do_ccs_injection do_ticketbleed do_robot do_renego \
|
||||
do_crime do_ssl_poodle do_tls_fallback_scsv do_drown do_beast \
|
||||
do_rc4 do_logjam; do
|
||||
"${!gbl}" && let nr_enabled++
|
||||
done
|
||||
|
||||
# ... atleast one of these has to be set
|
||||
[[ "$do_allciphers" || "$do_cipher_per_proto" ]] && let nr_enabled++
|
||||
|
||||
# ... else we can't do rating
|
||||
if [[ $nr_enabled -lt 18 ]]; then
|
||||
do_rating=false
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
# This initializes boolean global do_* variables. They keep track of what to do
|
||||
@ -20358,6 +20756,7 @@ initialize_globals() {
|
||||
do_client_simulation=false
|
||||
do_display_only=false
|
||||
do_starttls=false
|
||||
do_rating=false
|
||||
}
|
||||
|
||||
|
||||
@ -20393,6 +20792,7 @@ set_scanning_defaults() {
|
||||
else
|
||||
VULN_COUNT=12
|
||||
fi
|
||||
do_rating=true
|
||||
}
|
||||
|
||||
# returns number of $do variables set = number of run_funcs() to perform
|
||||
@ -20403,8 +20803,8 @@ count_do_variables() {
|
||||
for gbl in do_allciphers do_vulnerabilities do_beast do_lucky13 do_breach do_ccs_injection do_ticketbleed do_cipher_per_proto do_crime \
|
||||
do_freak do_logjam do_drown do_header do_heartbleed do_mx_all_ips do_fs do_protocols do_rc4 do_grease do_robot do_renego \
|
||||
do_cipherlists do_server_defaults do_server_preference do_ssl_poodle do_tls_fallback_scsv \
|
||||
do_sweet32 do_client_simulation do_cipher_match do_tls_sockets do_mass_testing do_display_only; do
|
||||
[[ "${!gbl}" == true ]] && let true_nr++
|
||||
do_sweet32 do_client_simulation do_cipher_match do_tls_sockets do_mass_testing do_display_only do_rating; do
|
||||
"${!gbl}" && let true_nr++
|
||||
done
|
||||
return $true_nr
|
||||
}
|
||||
@ -20416,13 +20816,31 @@ debug_globals() {
|
||||
for gbl in do_allciphers do_vulnerabilities do_beast do_lucky13 do_breach do_ccs_injection do_ticketbleed do_cipher_per_proto do_crime \
|
||||
do_freak do_logjam do_drown do_header do_heartbleed do_mx_all_ips do_fs do_protocols do_rc4 do_grease do_robot do_renego \
|
||||
do_cipherlists do_server_defaults do_server_preference do_ssl_poodle do_tls_fallback_scsv \
|
||||
do_sweet32 do_client_simulation do_cipher_match do_tls_sockets do_mass_testing do_display_only; do
|
||||
do_sweet32 do_client_simulation do_cipher_match do_tls_sockets do_mass_testing do_display_only do_rating; do
|
||||
printf "%-22s = %s\n" $gbl "${!gbl}"
|
||||
done
|
||||
# ${!var} is an indirect expansion, see https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html
|
||||
# Example: https://stackoverflow.com/questions/8515411/what-is-indirect-expansion-what-does-var-mean#8515492
|
||||
printf "%-22s : %s\n" URI: "$URI"
|
||||
}
|
||||
|
||||
|
||||
# This is determining the tests which should be skipped by --no-* or --disable-* a a cmdline arg.
|
||||
# It achieves that by setting the do_<variables> according to the global array $SKIP_TESTS
|
||||
#
|
||||
set_skip_tests() {
|
||||
local t
|
||||
|
||||
for t in ${SKIP_TESTS[@]} ; do
|
||||
t="do_${t}"
|
||||
# declare won't do it here --> local scope
|
||||
eval "$t"=false
|
||||
debugme printf '%s\n' "set $t: ${!t}"
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
|
||||
# arg1: either switch+value (=) or switch
|
||||
# arg2: value (if no = provided)
|
||||
parse_opt_equal_sign() {
|
||||
@ -20494,7 +20912,7 @@ parse_cmd_line() {
|
||||
;;
|
||||
esac
|
||||
|
||||
# initializing
|
||||
# set all do_* globals to false
|
||||
initialize_globals
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
@ -20680,6 +21098,11 @@ parse_cmd_line() {
|
||||
-g|--grease)
|
||||
do_grease=true
|
||||
;;
|
||||
--disable-rating|--no-rating)
|
||||
SKIP_TESTS+=("rating")
|
||||
# TODO: a generic thing would be --disable-* / --no-* ,
|
||||
# catch $1 and add it to the array ( #1502 )
|
||||
;;
|
||||
-9|--full)
|
||||
set_scanning_defaults
|
||||
do_allciphers=false
|
||||
@ -20690,18 +21113,18 @@ parse_cmd_line() {
|
||||
ADDTL_CA_FILES="$(parse_opt_equal_sign "$1" "$2")"
|
||||
[[ $? -eq 0 ]] && shift
|
||||
;;
|
||||
--devel) ### this development feature will soon disappear
|
||||
--devel) echo -e "\nthis is a development feature and may disappear at any time"
|
||||
# arg1: SSL/TLS protocol (SSLv2=22)
|
||||
# arg2: list of cipher suites / hostname/ip
|
||||
# arg3: hostname/ip
|
||||
HEX_CIPHER="$TLS12_CIPHER"
|
||||
# DEBUG=3 ./testssl.sh --devel 04 "13,02, 13,01" google.com --> TLS 1.3
|
||||
# DEBUG=3 ./testssl.sh --devel 03 "cc, 13, c0, 13" google.de --> TLS 1.2, old CHACHA/POLY
|
||||
# DEBUG=3 ./testssl.sh --devel 03 "cc,a8, cc,a9, cc,aa, cc,ab, cc,ac" blog.cloudflare.com --> new CHACHA/POLY
|
||||
# DEBUG=3 ./testssl.sh --devel 01 yandex.ru --> TLS 1.0
|
||||
# DEBUG=3 ./testssl.sh --devel 04 "13,02, 13,01" google.com --> TLS 1.3
|
||||
# DEBUG=3 ./testssl.sh --devel 03 "cc, 13, c0, 13" google.de --> TLS 1.2, old CHACHA/POLY
|
||||
# DEBUG=3 ./testssl.sh --devel 03 "cc,a8, cc,a9, cc,aa, cc,ab, cc,ac" blog.cloudflare.com --> new CHACHA/POLY
|
||||
# DEBUG=3 ./testssl.sh --devel 01 yandex.ru --> TLS 1.0
|
||||
# DEBUG=3 ./testssl.sh --devel 00 <host which supports SSLv3>
|
||||
# DEBUG=3 ./testssl.sh --devel 22 <host which still supports SSLv2>
|
||||
TLS_LOW_BYTE="$2";
|
||||
HEX_CIPHER="$TLS12_CIPHER"
|
||||
TLS_LOW_BYTE="$2"
|
||||
if [[ $# -eq 4 ]]; then # protocol AND ciphers specified
|
||||
HEX_CIPHER="$3"
|
||||
shift
|
||||
@ -20963,9 +21386,9 @@ parse_cmd_line() {
|
||||
SSL_NATIVE=true
|
||||
;;
|
||||
--basicauth|--basicauth=*)
|
||||
BASICAUTH="$(parse_opt_equal_sign "$1" "$2")"
|
||||
[[ $? -eq 0 ]] && shift
|
||||
;;
|
||||
BASICAUTH="$(parse_opt_equal_sign "$1" "$2")"
|
||||
[[ $? -eq 0 ]] && shift
|
||||
;;
|
||||
(--) shift
|
||||
break
|
||||
;;
|
||||
@ -20999,10 +21422,15 @@ parse_cmd_line() {
|
||||
grep -q "BEGIN CERTIFICATE" "$fname" || fatal "\"$fname\" is not CA file in PEM format" $ERR_RESOURCE
|
||||
done
|
||||
|
||||
[[ "$DEBUG" -ge 5 ]] && debug_globals
|
||||
|
||||
count_do_variables
|
||||
[[ $? -eq 0 ]] && set_scanning_defaults
|
||||
set_skip_tests
|
||||
[[ "$DEBUG" -ge 5 ]] && debug_globals
|
||||
|
||||
# Unless explicit disabled, check if rating can be enabled
|
||||
# Should be called after set_scanning_defaults
|
||||
! "$do_rating" && set_rating_state
|
||||
|
||||
CMDLINE_PARSED=true
|
||||
}
|
||||
|
||||
@ -21171,6 +21599,10 @@ lets_roll() {
|
||||
|
||||
fileout_section_header $section_number true && ((section_number++))
|
||||
"$do_client_simulation" && { run_client_simulation; ret=$(($? + ret)); stopwatch run_client_simulation; }
|
||||
|
||||
fileout_section_header $section_number true && ((section_number++))
|
||||
"$do_rating" && { run_rating; ret=$(($? + ret)); stopwatch run_rating; }
|
||||
|
||||
fi
|
||||
fileout_section_footer true
|
||||
fi
|
||||
|
Loading…
Reference in New Issue
Block a user