2020
2121final class Safari
2222{
23- // wreq cipher list (23 ciphers — with CBC SHA384/SHA256 but NO 3DES)
24- private const string CIPHER_LIST = 'TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA ' ;
25-
2623 // curl_impersonate Safari 15.3 cipher list (26 ciphers — with SHA384/SHA256 AND 3DES)
2724 private const string CI_CIPHER_LIST_15_3 = 'TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-ECDSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:DES-CBC3-SHA ' ;
2825
@@ -34,34 +31,49 @@ final class Safari
3431
3532 private const string CURVES_LIST = 'X25519:P-256:P-384:P-521 ' ;
3633
37- private const string SIGALGS_LIST = 'ecdsa_secp256r1_sha256:rsa_pss_rsae_sha256:rsa_pkcs1_sha256:ecdsa_secp384r1_sha384:rsa_pss_rsae_sha384:rsa_pkcs1_sha384:rsa_pss_rsae_sha512:rsa_pkcs1_sha512:rsa_pkcs1_sha1 ' ;
38-
3934 // curl_impersonate sigalgs for Safari 15.x-17.x (includes ecdsa_sha1 + duplicate rsa_pss_rsae_sha384)
4035 private const string CI_SIGALGS_LEGACY = 'ecdsa_secp256r1_sha256:rsa_pss_rsae_sha256:rsa_pkcs1_sha256:ecdsa_secp384r1_sha384:ecdsa_sha1:rsa_pss_rsae_sha384:rsa_pss_rsae_sha384:rsa_pkcs1_sha384:rsa_pss_rsae_sha512:rsa_pkcs1_sha512:rsa_pkcs1_sha1 ' ;
4136
4237 // curl_impersonate sigalgs for Safari 18.x+ (no ecdsa_sha1, has duplicate rsa_pss_rsae_sha384)
4338 private const string CI_SIGALGS_MODERN = 'ecdsa_secp256r1_sha256:rsa_pss_rsae_sha256:rsa_pkcs1_sha256:ecdsa_secp384r1_sha384:rsa_pss_rsae_sha384:rsa_pss_rsae_sha384:rsa_pkcs1_sha384:rsa_pss_rsae_sha512:rsa_pkcs1_sha512:rsa_pkcs1_sha1 ' ;
4439
45- // ─── wreq profiles (v18, iPad18, iOS18) — unchanged ───
46-
40+ // Safari 18.0 — uses CI profile to match curl_safari180 binary
4741 public static function v18 (): Profile
4842 {
49- return self ::buildProfile (
50- userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Safari/605.1.15 ' ,
43+ return new Profile (
44+ tlsOptions: self ::ciTls18x (),
45+ http2Options: self ::ciHttp2_180 (),
46+ defaultHeaders: self ::ciHeaders18x (
47+ userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Safari/605.1.15 ' ,
48+ encoding: 'gzip, deflate, br ' ,
49+ ),
50+ originalHeaderMap: self ::ciHeaderOrder18x (),
5151 );
5252 }
5353
5454 public static function iPad18 (): Profile
5555 {
56- return self ::buildProfile (
57- userAgent: 'Mozilla/5.0 (iPad; CPU OS 18_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1 ' ,
56+ return new Profile (
57+ tlsOptions: self ::ciTls18x (),
58+ http2Options: self ::ciHttp2_180 (),
59+ defaultHeaders: self ::ciHeaders18x (
60+ userAgent: 'Mozilla/5.0 (iPad; CPU OS 18_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1 ' ,
61+ encoding: 'gzip, deflate, br ' ,
62+ ),
63+ originalHeaderMap: self ::ciHeaderOrder18x (),
5864 );
5965 }
6066
6167 public static function iOS18 (): Profile
6268 {
63- return self ::buildProfile (
64- userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1 ' ,
69+ return new Profile (
70+ tlsOptions: self ::ciTls18x (),
71+ http2Options: self ::ciHttp2_180 (),
72+ defaultHeaders: self ::ciHeaders18x (
73+ userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1 ' ,
74+ encoding: 'gzip, deflate, br ' ,
75+ ),
76+ originalHeaderMap: self ::ciHeaderOrder18x (),
6577 );
6678 }
6779
@@ -173,88 +185,6 @@ public static function v260iOS(): Profile
173185 );
174186 }
175187
176- // ─── wreq private helpers (unchanged) ───
177-
178- private static function baseTlsOptions (): TlsOptions
179- {
180- return TlsOptions::builder ()
181- ->minTlsVersion (TlsVersion::TLS_1_2 )
182- ->maxTlsVersion (TlsVersion::TLS_1_3 )
183- ->cipherList (self ::CIPHER_LIST )
184- ->sigalgsList (self ::SIGALGS_LIST )
185- ->curvesList (self ::CURVES_LIST )
186- ->alpnProtocols ([AlpnProtocol::Http2, AlpnProtocol::Http1])
187- ->keyShares ([KeyShare::X25519 ])
188- ->enableOcspStapling (true )
189- ->enableSignedCertTimestamps (false )
190- ->greaseEnabled (false )
191- ->permuteExtensions (false )
192- ->enableEchGrease (false )
193- ->sessionTicket (true )
194- ->build ();
195- }
196-
197- private static function baseHttp2Options (): Http2Options
198- {
199- return Http2Options::builder ()
200- ->headerTableSize (4096 )
201- ->enablePush (false )
202- ->maxConcurrentStreams (100 )
203- ->initialWindowSize (2097152 )
204- ->maxFrameSize (16384 )
205- ->initialConnWindowSize (10485760 )
206- ->headersPseudoOrder (new PseudoHeaderOrder ([
207- PseudoHeader::Method,
208- PseudoHeader::Scheme,
209- PseudoHeader::Path,
210- PseudoHeader::Authority,
211- ]))
212- ->settingsOrder (new SettingsOrder ([
213- SettingId::HeaderTableSize,
214- SettingId::EnablePush,
215- SettingId::MaxConcurrentStreams,
216- SettingId::InitialWindowSize,
217- SettingId::MaxFrameSize,
218- ]))
219- ->build ();
220- }
221-
222- /**
223- * @return array<string, string>
224- */
225- private static function baseHeaders (string $ userAgent ): array
226- {
227- return [
228- 'User-Agent ' => $ userAgent ,
229- 'Accept ' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 ' ,
230- 'Accept-Language ' => 'en-US,en;q=0.9 ' ,
231- 'Accept-Encoding ' => 'gzip, deflate, br ' ,
232- ];
233- }
234-
235- private static function baseHeaderOrder (): OriginalHeaderMap
236- {
237- return new OriginalHeaderMap ([
238- 'Host ' ,
239- 'Accept ' ,
240- 'User-Agent ' ,
241- 'Accept-Language ' ,
242- 'Accept-Encoding ' ,
243- 'Connection ' ,
244- 'Cookie ' ,
245- ]);
246- }
247-
248- private static function buildProfile (string $ userAgent ): Profile
249- {
250- return new Profile (
251- tlsOptions: self ::baseTlsOptions (),
252- http2Options: self ::baseHttp2Options (),
253- defaultHeaders: self ::baseHeaders ($ userAgent ),
254- originalHeaderMap: self ::baseHeaderOrder (),
255- );
256- }
257-
258188 // ─── curl_impersonate private helpers ───
259189
260190 // TLS: Safari 15.x-17.x (TLS 1.0, no session ticket, GREASE, signedCertTS)
@@ -355,6 +285,33 @@ private static function ciHttp2Legacy(bool $enablePush, int $windowSize): Http2O
355285 return $ builder ->build ();
356286 }
357287
288+ // HTTP/2: Safari 18.0 (2:0;3:100;4:2097152;8:1;9:1, pseudo msap, weight 256)
289+ private static function ciHttp2_180 (): Http2Options
290+ {
291+ return Http2Options::builder ()
292+ ->enablePush (false )
293+ ->maxConcurrentStreams (100 )
294+ ->initialWindowSize (2097152 )
295+ ->enableConnectProtocol (true )
296+ ->noRfc7540Priorities (true )
297+ ->initialConnWindowSize (10420225 )
298+ ->headersStreamDependency (new StreamDependency (0 , 256 , false ))
299+ ->headersPseudoOrder (new PseudoHeaderOrder ([
300+ PseudoHeader::Method,
301+ PseudoHeader::Scheme,
302+ PseudoHeader::Authority,
303+ PseudoHeader::Path,
304+ ]))
305+ ->settingsOrder (new SettingsOrder ([
306+ SettingId::EnablePush,
307+ SettingId::MaxConcurrentStreams,
308+ SettingId::InitialWindowSize,
309+ SettingId::EnableConnectProtocol,
310+ SettingId::NoRfc7540Priorities,
311+ ]))
312+ ->build ();
313+ }
314+
358315 // HTTP/2: Safari 18.4 (2:0;3:100;4:2097152;9:1, pseudo msap, weight 256)
359316 private static function ciHttp2_184 (): Http2Options
360317 {
0 commit comments