CFNetwork SSLHandshake ล้มเหลวใน iOS 9


207

มีใครกับ iOS 9 เบต้า 1 มีปัญหานี้หรือไม่?

ฉันใช้ NSURLConnection มาตรฐานเพื่อเชื่อมต่อกับ webservice และทันทีที่มีการโทรไปยัง webservice ฉันพบข้อผิดพลาดด้านล่าง ปัจจุบันนี้ใช้งานได้กับ iOS 8.3

ข้อผิดพลาดเบต้าที่เป็นไปได้หรือไม่ ความคิดหรือความคิดใด ๆ จะดี! ฉันรู้ว่ามันเร็วมากในการพัฒนา iOS 9

นี่คือข้อผิดพลาดแบบเต็ม:

CFNetwork SSLHandshake ล้มเหลว (-9824) NSURLSession / NSURLConnection HTTP การโหลดล้มเหลว (kCFStreamErrorDomainSSL, -9824)

 NSURLRequest * urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:@"https://mywebserviceurl"]];
        NSURLResponse * response = nil;
        NSError * error = nil;
        NSData * data = [NSURLConnection sendSynchronousRequest:urlRequest
                                                  returningResponse:&response
                                                              error:&error];

คำตอบ:


310

iOS 9 และ OSX 10.11 ต้องใช้ TLSv1.2 SSL สำหรับโฮสต์ทั้งหมดที่คุณวางแผนที่จะขอข้อมูลเว้นแต่คุณจะระบุโดเมนข้อยกเว้นในไฟล์ Info.plist ของแอป

ไวยากรณ์สำหรับการกำหนดค่า Info.plist มีลักษณะดังนี้:

<key>NSAppTransportSecurity</key>
<dict>
  <key>NSExceptionDomains</key>
  <dict>
    <key>yourserver.com</key>
    <dict>
      <!--Include to allow subdomains-->
      <key>NSIncludesSubdomains</key>
      <true/>
      <!--Include to allow insecure HTTP requests-->
      <key>NSExceptionAllowsInsecureHTTPLoads</key>
      <true/>
      <!--Include to specify minimum TLS version-->
      <key>NSExceptionMinimumTLSVersion</key>
      <string>TLSv1.1</string>
    </dict>
  </dict>
</dict>

หากแอปพลิเคชันของคุณ (เช่นเว็บเบราว์เซอร์ของบุคคลที่สาม) จำเป็นต้องเชื่อมต่อกับโฮสต์โดยพลการคุณสามารถกำหนดค่าดังนี้:

<key>NSAppTransportSecurity</key>
<dict>
    <!--Connect to anything (this is probably BAD)-->
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>

หากคุณต้องทำสิ่งนี้อาจเป็นการดีที่สุดที่จะอัปเดตเซิร์ฟเวอร์ของคุณเพื่อใช้ TLSv1.2 และ SSL หากยังไม่ได้ดำเนินการ นี่ควรเป็นวิธีแก้ปัญหาชั่วคราว

ณ วันนี้เอกสารก่อนวางจำหน่ายไม่ได้กล่าวถึงตัวเลือกการกำหนดค่าเหล่านี้ในลักษณะเฉพาะใด ๆ ฉันจะอัปเดตคำตอบเพื่อเชื่อมโยงไปยังเอกสารที่เกี่ยวข้อง


1
App Transport Security (ATS) อนุญาตให้แอปเพิ่มการประกาศลงในไฟล์ Info.plist ที่ระบุโดเมนที่ต้องการการสื่อสารที่ปลอดภัย ATS ป้องกันการเปิดเผยโดยไม่ตั้งใจให้พฤติกรรมเริ่มต้นที่ปลอดภัยและง่ายต่อการปรับใช้ คุณควรปรับใช้ ATS โดยเร็วที่สุดโดยไม่คำนึงว่าคุณกำลังสร้างแอพใหม่หรืออัปเดตแอปที่มีอยู่ หากคุณกำลังพัฒนาแอพใหม่คุณควรใช้ HTTPS โดยเฉพาะ หากคุณมีแอพที่มีอยู่คุณควรใช้ HTTPS ให้มากที่สุดในตอนนี้และสร้างแผนสำหรับการโอนย้ายแอปที่เหลือของคุณโดยเร็วที่สุด
user3099837

2
@StevenPeterson เฮ้สตีฟฉันลาดเทดูเหมือนจะได้รับตัวอย่างโดเมนข้อยกเว้นในการทำงานคุณมีความคิดใด ๆ โดยบังเอิญฉันเพิ่งคัดลอกและวางลงใน. plist เปลี่ยน TLSv1.1 เป็น TLSv1.0 และโดเมนไปยังโดเมนของเราโดยไม่ต้อง https: // etc
user3099837

27
ขออภัยสำหรับการแชทที่ยาวนาน แต่ฉันคิดออกแล้วฉันจำเป็นต้องปิดการใช้งาน <key> NSTemporaryExceptionRequiresForwardSecrecy </key> <false />
3099837

2
@RashmiRanjanmallick NSTemporaryExceptionMinimumTLSVersion ใช้เพื่อบอก ATS ว่าคุณกำลังทำงานกับเซิร์ฟเวอร์ที่ไม่ใช่ 1.2 ตัวอย่างเช่นใช้สิ่งนี้หากคุณพยายามเชื่อมต่อกับโฮสต์ที่ใช้ TLS 1.0 คุณต้องใช้ NSTemporaryExceptionRequiresForwardSecrecy ตั้งค่าเป็นเท็จตามที่ผู้ใช้ 3099837 ระบุไว้ข้างต้น
Womble

2
ste.vn/2015/06/10/… - นี่คือบล็อกที่คำตอบนั้นมาจาก
Sean Dev

66

ใน iOS 10+ สตริง TLS ต้องอยู่ในรูปแบบ "TLSv1.0" มันไม่สามารถเป็น "1.0" ได้ (ถอนหายใจ)


การรวมกันของคำตอบอื่น ๆ ต่อไปนี้ทำงานได้

สมมติว่าคุณกำลังพยายามเชื่อมต่อกับโฮสต์ (YOUR_HOST.COM) ที่มีเฉพาะ TLS 1.0

เพิ่มสิ่งเหล่านี้ลงใน Info.plist ของแอปของคุณ

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSExceptionDomains</key>
    <dict>
        <key>YOUR_HOST.COM</key>
        <dict>
            <key>NSIncludesSubdomains</key>
            <true/>
            <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
            <true/>
            <key>NSTemporaryExceptionMinimumTLSVersion</key>
            <string>TLSv1.0</string>
            <key>NSTemporaryExceptionRequiresForwardSecrecy</key>
            <false/>
        </dict>
    </dict>
</dict>

9
ดูเหมือนว่าการเพิ่มNSTemporaryExceptionRequiresForwardSecrecyไม่ได้หลอกลวงให้ฉันขอบคุณ!
Josh Valdivieso

2
เวอร์ชันนี้ใช้งานไม่ได้กับ iOS9.1 - ฉันต้องใช้รูปแบบสตริง TLSVersion ในคำตอบอื่น <key> NSTemporaryExceptionMinimumTLSVersion </key> <string> TLSv1.1 </string>
300baud

ใช้งานได้ แต่คำถามของฉันคือ: หมายความว่าแอปของฉันไม่ได้ใช้ ssl เมื่อพารามิเตอร์เหล่านี้เปิดอยู่และไม่มีการเข้ารหัสข้อมูลหรือไม่
theDC

33

สำหรับข้อมูลเพิ่มเติมการกำหนดค่าข้อยกเว้นด้านความปลอดภัยของแอพใน iOS 9 และ OSX 10.11

อยากรู้อยากเห็นคุณจะสังเกตเห็นว่าการเชื่อมต่อพยายามที่จะเปลี่ยนโปรโตคอล http เป็น https เพื่อป้องกันข้อผิดพลาดในรหัสของคุณที่คุณอาจมีการกำหนดค่า URL ผิดพลาดโดยไม่ตั้งใจ ในบางกรณีอาจใช้งานได้จริง แต่ก็สับสนเช่นกัน

การส่งแอพนี้ด้วยแอพการขนส่งความปลอดภัยครอบคลุมเคล็ดลับการดีบักที่ดี

ความล้มเหลว ATS

ความล้มเหลวของ ATS ส่วนใหญ่จะแสดงเป็น CFErrors ด้วยรหัสในซีรีย์ -9800 เหล่านี้ถูกกำหนดไว้ในส่วนหัว Security / SecureTransport.h

2015-08-23 06:34:42.700 SelfSignedServerATSTest[3792:683731] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9813)

CFNETWORK_DIAGNOSTICS

ตั้งค่าตัวแปรสภาพแวดล้อม CFNETWORK_DIAGNOSTICS เป็น 1 เพื่อรับข้อมูลเพิ่มเติมเกี่ยวกับคอนโซลเกี่ยวกับความล้มเหลว

nscurl

เครื่องมือจะทำงานผ่านข้อยกเว้น ATS หลายแบบรวมกันพยายามเชื่อมต่ออย่างปลอดภัยกับโฮสต์ที่กำหนดภายใต้การกำหนดค่า ATS แต่ละรายการและรายงานผลลัพธ์

nscurl --ats-diagnostics https://example.com

1
เฉพาะจุดที่ nscurl สามารถใช้ได้ใน Mac OS X "El Capitan" เท่านั้น
webo80

1
อีกหนึ่งเคล็ดลับในการแก้ไขข้อบกพร่องเพื่อตรวจสอบว่ารหัสการเชื่อมต่อ TLS ใดที่เซิร์ฟเวอร์ของคุณกำลังใช้งาน: curl -v https: // <hostname>
AndréMorujão

1
ความคิดเห็นใด ๆ เกี่ยวกับสิ่งที่อาจทำให้เกิดปัญหาหากขด PASS ทุกขั้นตอนเป็นปกติ
Aston

@ onmyway133 คุณสามารถเพิ่มคำอธิบายเกี่ยวกับวิธีการ "ตั้งค่าตัวแปรสภาพแวดล้อม CFNETWORK_DIAGNOSTICS เป็น 1" ได้อย่างไร
YakirNa

1
@YakirNa คุณสามารถอ่านเกี่ยวกับวิธีการทำที่นี่nshipster.com/launch-arguments-and-environment-variablesมันค่อนข้างตรงไปตรง :) :)
onmyway133

2

หากแบ็กเอนด์ของคุณใช้การเชื่อมต่อที่ปลอดภัยคุณจะได้รับการใช้ NSURLS

CFNetwork SSLHandshake failed (-9801)
NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9801)

คุณต้องตรวจสอบการกำหนดค่าเซิร์ฟเวอร์ของคุณโดยเฉพาะเพื่อรับข้อมูลเวอร์ชัน ATS และใบรับรอง SSL:

แทนที่จะตั้งค่าอนุญาตการเชื่อมต่อที่ไม่ปลอดภัยโดยการตั้งค่าNSExceptionAllowsInsecureHTTPLoads = YESแทนคุณต้องอนุญาตการรักษาความปลอดภัยที่ลดลงในกรณีที่เซิร์ฟเวอร์ของคุณไม่ตรงตามข้อกำหนดขั้นต่ำ (v1.2) สำหรับ ATS (หรือดีกว่าเพื่อแก้ไขฝั่งเซิร์ฟเวอร์)

อนุญาตให้ความปลอดภัยที่ลดลงไปยังเซิร์ฟเวอร์เดียว

<key>NSExceptionDomains</key>
<dict>
    <key>api.yourDomaine.com</key>
    <dict>
        <key>NSExceptionMinimumTLSVersion</key>
        <string>TLSv1.0</string>
        <key>NSExceptionRequiresForwardSecrecy</key>
        <false/>
    </dict>
</dict>

ใช้ไคลเอ็นต์ openssl เพื่อตรวจสอบใบรับรองและรับการกำหนดค่าเซิร์ฟเวอร์ของคุณโดยใช้ไคลเอ็นต์ openssl:

openssl s_client  -connect api.yourDomaine.com:port //(you may need to specify port or  to try with https://... or www.)

หาที่สิ้นสุด

SSL-Session:
    Protocol  : TLSv1
    Cipher    : AES256-SHA
    Session-ID: //
    Session-ID-ctx: 
    Master-Key: //
    Key-Arg   : None
    Start Time: 1449693038
    Timeout   : 300 (sec)
    Verify return code: 0 (ok)

App Transport Security (ATS) ต้องการโปรโตคอล Transport Layer Security (TLS) เวอร์ชัน 1.2

ข้อกำหนดสำหรับการเชื่อมต่อโดยใช้ ATS:

ข้อกำหนดสำหรับการเชื่อมต่อบริการเว็บเพื่อใช้ App Transport Security (ATS) เกี่ยวข้องกับเซิร์ฟเวอร์รหัสการเชื่อมต่อและใบรับรองดังต่อไปนี้:

ใบรับรองต้องลงนามด้วยหนึ่งในประเภทของคีย์ต่อไปนี้:

  • Secure Hash Algorithm 2 (SHA-2) คีย์ที่มีความยาวสรุปอย่างน้อย 256 (นั่นคือ SHA-256 หรือสูงกว่า)

  • คีย์รูปไข่เข้ารหัสลับ (ECC) ที่มีขนาดอย่างน้อย 256 บิต

  • คีย์ Rivest-Shamir-Adleman (RSA) ที่มีความยาวอย่างน้อย 2048 บิตใบรับรองที่ไม่ถูกต้องส่งผลให้เกิดความล้มเหลวอย่างหนักและไม่มีการเชื่อมต่อ

ciphers การเชื่อมต่อต่อไปนี้สนับสนุน forward Secrecy (FS) และทำงานกับ ATS:

TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA

ปรับปรุง: ปรากฎว่า OpenSSL เพียง แต่ให้น้อยที่สุดโปรโตคอลรุ่นโปรโตคอล: TLSv1 การเชื่อมโยง


คำถามที่เหลือคือมันเป็นไปได้ที่จะตีความข้อมูล openssl เพื่อดูว่าเซิร์ฟเวอร์ตรงตามข้อกำหนดหรือไม่ โปรโตคอลอื่น ๆ : TLSv1 อาจเป็นเวอร์ชั่น makor แทน 1.x
Idali

ฉันทำให้เป็นเรื่องทั่วไปในกรณีที่ใช้พอร์ต
Idali

คำตอบเปิดคำถามมากกว่าที่ได้รับคำตอบ ดูเหมือนว่าปัญหาอาจเป็นการแมประหว่างรายงาน openssl กับเอกสารของ Apple เป็นไปไม่ได้จากเอาต์พุต openssl เพื่อพิจารณาว่าสนับสนุน TLS 1.2 หรือไม่ คำตอบยังไม่อนุญาตให้มีการตรวจสอบว่าสนับสนุนการส่งต่ออย่างปลอดภัยหรือไม่
zaph

2

หลังจากสองวันแห่งความพยายามและความล้มเหลวสิ่งที่ใช้ได้ผลสำหรับฉันคือรหัสของwomble

กับหนึ่งการเปลี่ยนแปลงตามนี้โพสต์เราควรจะหยุดใช้ปุ่มย่อยที่เกี่ยวข้องกับNSExceptionDomainsพจนานุกรมของชนิดของการประชุมว่า

  NSTemporaryExceptionMinimumTLSVersion

และใช้ที่อนุสัญญาใหม่

  NSExceptionMinimumTLSVersion

แทน.

เอกสารแอปเปิ้ล

รหัสของฉัน

<key>NSAppTransportSecurity</key>
    <dict>
        <key>NSExceptionDomains</key>
        <dict>
            <key>YOUR_HOST.COM</key>
            <dict>
                <key>NSExceptionAllowsInsecureHTTPLoads</key>
                <true/>
                <key>NSExceptionMinimumTLSVersion</key>
                <string>TLSv1.0</string>
                <key>NSExceptionRequiresForwardSecrecy</key>
                <false/>
                <key>NSIncludesSubdomains</key>
                <true/>
            </dict>
        </dict>
    </dict>

1

เครื่องมือที่มีประโยชน์อีกอย่างคือ nmap (ชงติดตั้ง nmap)

nmap --script ssl-enum-ciphers -p 443 google.com

ให้เอาต์พุต

Starting Nmap 7.12 ( https://nmap.org ) at 2016-08-11 17:25 IDT
Nmap scan report for google.com (172.217.23.46)
Host is up (0.061s latency).
Other addresses for google.com (not scanned): 2a00:1450:4009:80a::200e
PORT    STATE SERVICE
443/tcp open  https
| ssl-enum-ciphers: 
|   TLSv1.0: 
|     ciphers: 
|       TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA (secp256r1) - A
|       TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA (secp256r1) - A
|       TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (secp256r1) - A
|       TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A
|       TLS_RSA_WITH_3DES_EDE_CBC_SHA (rsa 2048) - C
|       TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (secp256r1) - A
|       TLS_RSA_WITH_AES_256_CBC_SHA (rsa 2048) - A
|     compressors: 
|       NULL
|     cipher preference: server
|   TLSv1.1: 
|     ciphers: 
|       TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA (secp256r1) - A
|       TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA (secp256r1) - A
|       TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (secp256r1) - A
|       TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A
|       TLS_RSA_WITH_3DES_EDE_CBC_SHA (rsa 2048) - C
|       TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (secp256r1) - A
|       TLS_RSA_WITH_AES_256_CBC_SHA (rsa 2048) - A
|     compressors: 
|       NULL
|     cipher preference: server
|   TLSv1.2: 
|     ciphers: 
|       TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA (secp256r1) - A
|       TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 (secp256r1) - A
|       TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (secp256r1) - A
|       TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA (secp256r1) - A
|       TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 (secp256r1) - A
|       TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (secp256r1) - A
|       TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 (secp256r1) - A
|       TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (secp256r1) - A
|       TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 (secp256r1) - A
|       TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (secp256r1) - A
|       TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (secp256r1) - A
|       TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (secp256r1) - A
|       TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (secp256r1) - A
|       TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (secp256r1) - A
|       TLS_RSA_WITH_3DES_EDE_CBC_SHA (rsa 2048) - C
|       TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A
|       TLS_RSA_WITH_AES_128_CBC_SHA256 (rsa 2048) - A
|       TLS_RSA_WITH_AES_128_GCM_SHA256 (rsa 2048) - A
|       TLS_RSA_WITH_AES_256_CBC_SHA (rsa 2048) - A
|       TLS_RSA_WITH_AES_256_CBC_SHA256 (rsa 2048) - A
|       TLS_RSA_WITH_AES_256_GCM_SHA384 (rsa 2048) - A
|     compressors: 
|       NULL
|     cipher preference: client
|_  least strength: C

Nmap done: 1 IP address (1 host up) scanned in 5.48 seconds

มีประโยชน์มากในการแก้ไขปัญหาใบรับรอง
Lopakhin

0

ข้อผิดพลาดนี้ปรากฏขึ้นในบันทึกบางครั้งเมื่อฉันใช้รุ่น Cordova iOS ของ buggy / crashy มันหายไปเมื่อฉันอัพเกรดหรือลดระดับคอร์โดวา iOS

เซิร์ฟเวอร์ที่ฉันกำลังเชื่อมต่อด้วยกำลังใช้ TLSv1.2 SSL ดังนั้นฉันจึงรู้ว่านั่นไม่ใช่ปัญหา


0

ใน.plistไฟล์โครงการของคุณ ในการเพิ่มสิทธิ์นี้:

<key>NSAppTransportSecurity</key>
<dict>
    <!--Connect to anything (this is probably BAD)-->
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>

0

ไวยากรณ์สำหรับการกำหนดค่า Info.plist

   <key>NSAppTransportSecurity</key>
   <dict>
   <key>NSExceptionDomains</key>
    <dict>
    <key>yourserver.com</key>
   <dict>
  <!--Include to allow subdomains-->
  <key>NSIncludesSubdomains</key>
  <true/>
  <!--Include to allow insecure HTTP requests-->
  <key>NSExceptionAllowsInsecureHTTPLoads</key>
  <true/>
  <!--Include to specify minimum TLS version-->
  <key>NSExceptionMinimumTLSVersion</key>
  <string>TLSv1.1</string>
   </dict>
 </dict>


0

อัปเดตคำตอบ (โพสต์ WWDC 2016):

แอพ iOS จะต้องใช้การเชื่อมต่อ HTTPS ที่ปลอดภัยภายในสิ้นปี 2016 การลองปิด ATS อาจทำให้แอปของคุณถูกปฏิเสธในอนาคต

App Transport Security หรือ ATS เป็นคุณสมบัติที่ Apple เปิดตัวใน iOS 9 เมื่อเปิดใช้งาน ATS แอปจะบังคับให้แอปเชื่อมต่อกับบริการเว็บผ่านการเชื่อมต่อ HTTPS แทนที่จะใช้ HTTP ที่ไม่ปลอดภัย

อย่างไรก็ตามนักพัฒนายังคงสามารถปิด ATS และอนุญาตให้แอปส่งข้อมูลผ่านการเชื่อมต่อ HTTP ดังที่กล่าวไว้ในคำตอบข้างต้น ณ สิ้นปี 2559 Apple จะกำหนดให้ATS บังคับสำหรับนักพัฒนาทั้งหมดที่หวังว่าจะส่งแอพของตนไปยัง App Store ลิงค์


0

อุปกรณ์ที่ฉันทดสอบมีการตั้งเวลาผิด ดังนั้นเมื่อฉันพยายามเข้าถึงหน้าเว็บที่มีใบรับรองที่จะหมดเร็ว ๆ นี้ก็จะปฏิเสธการเข้าถึงเพราะอุปกรณ์แม้ว่าใบรับรองหมดอายุแล้ว หากต้องการแก้ไขให้ตั้งเวลาที่เหมาะสมบนอุปกรณ์!

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.