Make ciphersuite selection configurable; match Chromium by default. (#12524)

New option --ssl-ciphers takes a colon-separated list of OpenSSL
cipher names and sets the client cipher list to exactly that list.

The default for this option is arranged to match Chromium 35, which
has had its cipher selection optimized for the modern Web
(see https://briansmith.org/browser-ciphersuites-01.html for
rationales).  (Newer versions are the same except that they also add
ChaCha20-based ciphersuites, which OpenSSL 1.0.1 hasn't yet picked up.)
2.0
Zack Weinberg 2014-08-30 20:32:46 -04:00
parent e8cddbfe7b
commit f4eb3645f2
3 changed files with 55 additions and 0 deletions

View File

@ -68,6 +68,7 @@ static const struct QCommandLineConfigEntry flags[] =
{ QCommandLine::Option, '\0', "script-language", "Sets the script language instead of detecting it: 'javascript'", QCommandLine::Optional },
{ QCommandLine::Option, '\0', "web-security", "Enables web security, 'true' (default) or 'false'", QCommandLine::Optional },
{ QCommandLine::Option, '\0', "ssl-protocol", "Selects a specific SSL protocol version to offer. Values (case insensitive): TLSv1.2, TLSv1.1, TLSv1.0, TLSv1 (same as v1.0), SSLv3, or ANY. Default is to offer all that Qt thinks are secure (SSLv3 and up). Not all values may be supported, depending on the system OpenSSL library.", QCommandLine::Optional },
{ QCommandLine::Option, '\0', "ssl-ciphers", "Sets supported TLS/SSL ciphers. Argument is a colon-separated list of OpenSSL cipher names (macros like ALL, kRSA, etc. may not be used). Default matches modern browsers.", QCommandLine::Optional },
{ QCommandLine::Option, '\0', "ssl-certificates-path", "Sets the location for custom CA certificates (if none set, uses system default)", QCommandLine::Optional },
{ QCommandLine::Option, '\0', "webdriver", "Starts in 'Remote WebDriver mode' (embedded GhostDriver): '[[<IP>:]<PORT>]' (default '127.0.0.1:8910') ", QCommandLine::Optional },
{ QCommandLine::Option, '\0', "webdriver-logfile", "File where to write the WebDriver's Log (default 'none') (NOTE: needs '--webdriver') ", QCommandLine::Optional },
@ -556,6 +557,25 @@ void Config::resetToDefaults()
m_helpFlag = false;
m_printDebugMessages = false;
m_sslProtocol = "default";
// Default taken from Chromium 35.0.1916.153
m_sslCiphers = ("ECDHE-ECDSA-AES128-GCM-SHA256"
":ECDHE-RSA-AES128-GCM-SHA256"
":DHE-RSA-AES128-GCM-SHA256"
":ECDHE-ECDSA-AES256-SHA"
":ECDHE-ECDSA-AES128-SHA"
":ECDHE-RSA-AES128-SHA"
":ECDHE-RSA-AES256-SHA"
":ECDHE-ECDSA-RC4-SHA"
":ECDHE-RSA-RC4-SHA"
":DHE-RSA-AES128-SHA"
":DHE-DSS-AES128-SHA"
":DHE-RSA-AES256-SHA"
":AES128-GCM-SHA256"
":AES128-SHA"
":AES256-SHA"
":DES-CBC3-SHA"
":RC4-SHA"
":RC4-MD5");
m_sslCertificatesPath.clear();
m_webdriverIp = QString();
m_webdriverPort = QString();
@ -713,6 +733,9 @@ void Config::handleOption(const QString &option, const QVariant &value)
if (option == "ssl-protocol") {
setSslProtocol(value.toString());
}
if (option == "ssl-ciphers") {
setSslCiphers(value.toString());
}
if (option == "ssl-certificates-path") {
setSslCertificatesPath(value.toString());
}
@ -755,6 +778,17 @@ void Config::setSslProtocol(const QString& sslProtocolName)
m_sslProtocol = sslProtocolName.toLower();
}
QString Config::sslCiphers() const
{
return m_sslCiphers;
}
void Config::setSslCiphers(const QString& sslCiphersName)
{
// OpenSSL cipher strings are case sensitive.
m_sslCiphers = sslCiphersName;
}
QString Config::sslCertificatesPath() const
{
return m_sslCertificatesPath;

View File

@ -58,6 +58,7 @@ class Config: public QObject
Q_PROPERTY(bool javascriptCanOpenWindows READ javascriptCanOpenWindows WRITE setJavascriptCanOpenWindows)
Q_PROPERTY(bool javascriptCanCloseWindows READ javascriptCanCloseWindows WRITE setJavascriptCanCloseWindows)
Q_PROPERTY(QString sslProtocol READ sslProtocol WRITE setSslProtocol)
Q_PROPERTY(QString sslCiphers READ sslCiphers WRITE setSslCiphers)
Q_PROPERTY(QString sslCertificatesPath READ sslCertificatesPath WRITE setSslCertificatesPath)
Q_PROPERTY(QString webdriver READ webdriver WRITE setWebdriver)
Q_PROPERTY(QString webdriverLogFile READ webdriverLogFile WRITE setWebdriverLogFile)
@ -160,6 +161,9 @@ public:
void setSslProtocol(const QString& sslProtocolName);
QString sslProtocol() const;
void setSslCiphers(const QString& sslCiphersName);
QString sslCiphers() const;
void setSslCertificatesPath(const QString& sslCertificatesPath);
QString sslCertificatesPath() const;
@ -221,6 +225,7 @@ private:
bool m_javascriptCanOpenWindows;
bool m_javascriptCanCloseWindows;
QString m_sslProtocol;
QString m_sslCiphers;
QString m_sslCertificatesPath;
QString m_webdriverIp;
QString m_webdriverPort;

View File

@ -36,6 +36,7 @@
#include <QNetworkRequest>
#include <QSslSocket>
#include <QSslCertificate>
#include <QSslCipher>
#include <QRegExp>
#include "phantom.h"
@ -165,6 +166,21 @@ NetworkAccessManager::NetworkAccessManager(QObject *parent, const Config *config
m_sslConfiguration.setProtocol(QSsl::SecureProtocols);
}
// Essentially the same as what QSslSocket::setCiphers(QString) does.
// That overload isn't available on QSslConfiguration.
if (!config->sslCiphers().isEmpty()) {
QList<QSslCipher> cipherList;
foreach (const QString &cipherName,
config->sslCiphers().split(QLatin1String(":"),
QString::SkipEmptyParts)) {
QSslCipher cipher(cipherName);
if (!cipher.isNull())
cipherList << cipher;
}
if (!cipherList.isEmpty())
m_sslConfiguration.setCiphers(cipherList);
}
if (!config->sslCertificatesPath().isEmpty()) {
QList<QSslCertificate> caCerts = QSslCertificate::fromPath(
config->sslCertificatesPath(), QSsl::Pem, QRegExp::Wildcard);