Skip to content

Commit bd38bbd

Browse files
[HttpClient] Add option crypto_method to set the minimum TLS version and make it default to TLSv1.2
1 parent 6222f8e commit bd38bbd

File tree

10 files changed

+53
-4
lines changed

10 files changed

+53
-4
lines changed

UPGRADE-6.3.md

+2
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ FrameworkBundle
6868
HttpClient
6969
----------
7070

71+
* The minimum TLS version now defaults to TLSv1.2; use the `crypto_method`
72+
option if you need to connect to servers that don't support it
7173
* The default user agents have been renamed from `Symfony HttpClient/Amp`, `Symfony HttpClient/Curl`
7274
and `Symfony HttpClient/Native` to `Symfony HttpClient (Amp)`, `Symfony HttpClient (Curl)`
7375
and `Symfony HttpClient (Native)` respectively to comply with the RFC 9110 specification

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php

+7-4
Original file line numberDiff line numberDiff line change
@@ -1817,7 +1817,7 @@ private function addHttpClientSection(ArrayNodeDefinition $rootNode, callable $e
18171817
->info('A network interface name, IP address, a host name or a UNIX socket to bind to.')
18181818
->end()
18191819
->booleanNode('verify_peer')
1820-
->info('Indicates if the peer should be verified in an SSL/TLS context.')
1820+
->info('Indicates if the peer should be verified in an TLS context.')
18211821
->end()
18221822
->booleanNode('verify_host')
18231823
->info('Indicates if the host should exist as a certificate common name.')
@@ -1838,7 +1838,7 @@ private function addHttpClientSection(ArrayNodeDefinition $rootNode, callable $e
18381838
->info('The passphrase used to encrypt the "local_pk" file.')
18391839
->end()
18401840
->scalarNode('ciphers')
1841-
->info('A list of SSL/TLS ciphers separated by colons, commas or spaces (e.g. "RC3-SHA:TLS13-AES-128-GCM-SHA256"...)')
1841+
->info('A list of TLS ciphers separated by colons, commas or spaces (e.g. "RC3-SHA:TLS13-AES-128-GCM-SHA256"...)')
18421842
->end()
18431843
->arrayNode('peer_fingerprint')
18441844
->info('Associative array: hashing algorithm => hash(es).')
@@ -1849,6 +1849,9 @@ private function addHttpClientSection(ArrayNodeDefinition $rootNode, callable $e
18491849
->variableNode('md5')->end()
18501850
->end()
18511851
->end()
1852+
->scalarNode('crypto_method')
1853+
->info('The minimum version of TLS to accept; must be one of STREAM_CRYPTO_METHOD_TLSv*_CLIENT constants.')
1854+
->end()
18521855
->arrayNode('extra')
18531856
->info('Extra options for specific HTTP client')
18541857
->normalizeKeys(false)
@@ -1965,7 +1968,7 @@ private function addHttpClientSection(ArrayNodeDefinition $rootNode, callable $e
19651968
->info('A network interface name, IP address, a host name or a UNIX socket to bind to.')
19661969
->end()
19671970
->booleanNode('verify_peer')
1968-
->info('Indicates if the peer should be verified in an SSL/TLS context.')
1971+
->info('Indicates if the peer should be verified in a TLS context.')
19691972
->end()
19701973
->booleanNode('verify_host')
19711974
->info('Indicates if the host should exist as a certificate common name.')
@@ -1986,7 +1989,7 @@ private function addHttpClientSection(ArrayNodeDefinition $rootNode, callable $e
19861989
->info('The passphrase used to encrypt the "local_pk" file.')
19871990
->end()
19881991
->scalarNode('ciphers')
1989-
->info('A list of SSL/TLS ciphers separated by colons, commas or spaces (e.g. "RC3-SHA:TLS13-AES-128-GCM-SHA256"...)')
1992+
->info('A list of TLS ciphers separated by colons, commas or spaces (e.g. "RC3-SHA:TLS13-AES-128-GCM-SHA256"...)')
19901993
->end()
19911994
->arrayNode('peer_fingerprint')
19921995
->info('Associative array: hashing algorithm => hash(es).')

src/Symfony/Component/HttpClient/AmpHttpClient.php

+4
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ final class AmpHttpClient implements HttpClientInterface, LoggerAwareInterface,
4747
use HttpClientTrait;
4848
use LoggerAwareTrait;
4949

50+
public const OPTIONS_DEFAULTS = HttpClientInterface::OPTIONS_DEFAULTS + [
51+
'crypto_method' => \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT,
52+
];
53+
5054
private array $defaultOptions = self::OPTIONS_DEFAULTS;
5155
private static array $emptyDefaults = self::OPTIONS_DEFAULTS;
5256
private AmpClientState $multi;

src/Symfony/Component/HttpClient/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ CHANGELOG
44
6.3
55
---
66

7+
* Add option `crypto_method` to set the minimum TLS version and make it default to TLSv1.2
78
* Add `UriTemplateHttpClient` to use URI templates as specified in the RFC 6570
89
* Add `ServerSentEvent::getArrayData()` to get the Server-Sent Event's data decoded as an array when it's a JSON payload
910
* Allow array of urls as `base_uri` option value in `RetryableHttpClient` to retry on a new url each time

src/Symfony/Component/HttpClient/CurlHttpClient.php

+10
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ final class CurlHttpClient implements HttpClientInterface, LoggerAwareInterface,
3636
{
3737
use HttpClientTrait;
3838

39+
public const OPTIONS_DEFAULTS = HttpClientInterface::OPTIONS_DEFAULTS + [
40+
'crypto_method' => \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT,
41+
];
42+
3943
private array $defaultOptions = self::OPTIONS_DEFAULTS + [
4044
'auth_ntlm' => null, // array|string - an array containing the username as first value, and optionally the
4145
// password as the second one; or string like username:password - enabling NTLM auth
@@ -116,6 +120,12 @@ public function request(string $method, string $url, array $options = []): Respo
116120
\CURLOPT_SSLKEY => $options['local_pk'],
117121
\CURLOPT_KEYPASSWD => $options['passphrase'],
118122
\CURLOPT_CERTINFO => $options['capture_peer_cert_chain'],
123+
\CURLOPT_SSLVERSION => match ($options['crypto_method']) {
124+
\STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT => \CURL_SSLVERSION_TLSv1_3,
125+
\STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT => \CURL_SSLVERSION_TLSv1_2,
126+
\STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT => \CURL_SSLVERSION_TLSv1_1,
127+
\STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT => \CURL_SSLVERSION_TLSv1_0,
128+
},
119129
];
120130

121131
if (1.0 === (float) $options['http_version']) {

src/Symfony/Component/HttpClient/HttpClientTrait.php

+10
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Symfony\Component\HttpClient\Response\StreamableInterface;
1717
use Symfony\Component\HttpClient\Response\StreamWrapper;
1818
use Symfony\Component\Mime\MimeTypes;
19+
use Symfony\Contracts\HttpClient\HttpClientInterface;
1920

2021
/**
2122
* Provides the common logic from writing HttpClientInterface implementations.
@@ -116,6 +117,15 @@ private static function prepareRequest(?string $method, ?string $url, array $opt
116117
$options['peer_fingerprint'] = self::normalizePeerFingerprint($options['peer_fingerprint']);
117118
}
118119

120+
if (isset($options['crypto_method']) && !\in_array($options['crypto_method'], [
121+
\STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT,
122+
\STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT,
123+
\STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT,
124+
\STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT,
125+
], true)) {
126+
throw new InvalidArgumentException('Option "crypto_method" must be one of "STREAM_CRYPTO_METHOD_TLSv1_*_CLIENT".');
127+
}
128+
119129
// Validate on_progress
120130
if (isset($options['on_progress']) && !\is_callable($onProgress = $options['on_progress'])) {
121131
throw new InvalidArgumentException(sprintf('Option "on_progress" must be callable, "%s" given.', get_debug_type($onProgress)));

src/Symfony/Component/HttpClient/Internal/AmpClientState.php

+2
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ private function getClient(array $options): array
126126
'ciphers' => $options['ciphers'],
127127
'capture_peer_cert_chain' => $options['capture_peer_cert_chain'] || $options['peer_fingerprint'],
128128
'proxy' => $options['proxy'],
129+
'crypto_method' => $options['crypto_method'],
129130
];
130131

131132
$key = hash('xxh128', serialize($options));
@@ -141,6 +142,7 @@ private function getClient(array $options): array
141142
$options['local_cert'] && $context = $context->withCertificate(new Certificate($options['local_cert'], $options['local_pk']));
142143
$options['ciphers'] && $context = $context->withCiphers($options['ciphers']);
143144
$options['capture_peer_cert_chain'] && $context = $context->withPeerCapturing();
145+
$options['crypto_method'] && $context = $context->withMinimumVersion($options['crypto_method']);
144146

145147
$connector = $handleConnector = new class() implements Connector {
146148
public $connector;

src/Symfony/Component/HttpClient/NativeHttpClient.php

+11
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ final class NativeHttpClient implements HttpClientInterface, LoggerAwareInterfac
3636
use HttpClientTrait;
3737
use LoggerAwareTrait;
3838

39+
public const OPTIONS_DEFAULTS = HttpClientInterface::OPTIONS_DEFAULTS + [
40+
'crypto_method' => \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT,
41+
];
42+
3943
private array $defaultOptions = self::OPTIONS_DEFAULTS;
4044
private static array $emptyDefaults = self::OPTIONS_DEFAULTS;
4145

@@ -198,6 +202,12 @@ public function request(string $method, string $url, array $options = []): Respo
198202
$options['timeout'] = min($options['max_duration'], $options['timeout']);
199203
}
200204

205+
switch ($cryptoMethod = $options['crypto_method']) {
206+
case \STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT: $cryptoMethod |= \STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
207+
case \STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT: $cryptoMethod |= \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT;
208+
case \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT: $cryptoMethod |= \STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT;
209+
}
210+
201211
$context = [
202212
'http' => [
203213
'protocol_version' => min($options['http_version'] ?: '1.1', '1.1'),
@@ -224,6 +234,7 @@ public function request(string $method, string $url, array $options = []): Respo
224234
'allow_self_signed' => (bool) $options['peer_fingerprint'],
225235
'SNI_enabled' => true,
226236
'disable_compression' => true,
237+
'crypto_method' => $cryptoMethod,
227238
], static fn ($v) => null !== $v),
228239
'socket' => [
229240
'bindto' => $options['bindto'],

src/Symfony/Contracts/CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
3.3
5+
---
6+
7+
* Add option `crypto_method` to `HttpClientInterface` to define the minimum TLS version to accept
8+
49
3.2
510
---
611

src/Symfony/Contracts/HttpClient/HttpClientInterface.php

+1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ interface HttpClientInterface
6666
'ciphers' => null,
6767
'peer_fingerprint' => null,
6868
'capture_peer_cert_chain' => false,
69+
'crypto_method' => \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT, // STREAM_CRYPTO_METHOD_TLSv*_CLIENT - minimum TLS version
6970
'extra' => [], // array - additional options that can be ignored if unsupported, unlike regular options
7071
];
7172

0 commit comments

Comments
 (0)