จะขอ https ด้วยใบรับรองที่ไม่ถูกต้องได้อย่างไร


128

บอกว่าฉันต้องการรับแบบเป็นhttps://golang.orgโปรแกรม ปัจจุบัน golang.org (ssl) มีใบรับรองที่ไม่ถูกต้องซึ่งออกให้*.appspot.comดังนั้นเมื่อฉันเรียกใช้สิ่งนี้:

package main

import (
    "log"
    "net/http"
)

func main() {
    _, err := http.Get("https://golang.org/")
    if err != nil {
        log.Fatal(err)
    }
}

ฉันได้รับ (ตามที่ฉันคาดไว้)

Get https://golang.org/: certificate is valid for *.appspot.com, *.*.appspot.com, appspot.com, not golang.org

ตอนนี้ฉันต้องการเชื่อถือใบรับรองนี้ด้วยตัวเอง (ลองนึกภาพใบรับรองที่ออกเองซึ่งฉันสามารถตรวจสอบลายนิ้วมือ ฯลฯ ): ฉันจะส่งคำขอและตรวจสอบ / เชื่อถือใบรับรองได้อย่างไร

ฉันอาจต้องใช้ openssl เพื่อดาวน์โหลดใบรับรองโหลดลงในไฟล์ของฉันและกรอกโครงสร้างtls.Config!?


5
นี่ไม่ใช่ "ใบรับรองที่ไม่ถูกต้อง" แต่เป็นใบรับรองที่มี CN อื่น InsecureSkipVerify ไม่ใช่การใช้งานที่ถูกต้องตามกฎหมายที่นี่ คุณต้องตั้งค่า ServerName ใน tls.Config ให้ตรงกับสิ่งที่คุณพยายามเชื่อมต่อ โพสต์ StackOverflow นี้ทำให้ช่องโหว่ขนาดใหญ่ในรหัส Go แพร่กระจายไปทุกที่ InsecureSkipVerify ไม่ตรวจสอบใบรับรองเลย สิ่งที่คุณต้องการคือการตรวจสอบว่าใบรับรองได้รับการลงนามอย่างถูกต้องโดยหน่วยงานที่เชื่อถือได้แม้ว่า CN จะไม่ตรงกับชื่อโฮสต์ก็ตาม Tunnels และ NATS อาจทำให้สิ่งนี้ไม่ตรงกัน
Rob

คำตอบ:


284

หมายเหตุด้านความปลอดภัย: การปิดใช้งานการตรวจสอบความปลอดภัยเป็นสิ่งที่อันตรายและควรหลีกเลี่ยง

คุณสามารถปิดใช้งานการตรวจสอบความปลอดภัยได้ทั่วโลกสำหรับคำขอทั้งหมดของไคลเอ็นต์เริ่มต้น:

package main

import (
    "fmt"
    "net/http"
    "crypto/tls"
)

func main() {
    http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
    _, err := http.Get("https://golang.org/")
    if err != nil {
        fmt.Println(err)
    }
}

คุณสามารถปิดใช้งานการตรวจสอบความปลอดภัยสำหรับไคลเอ็นต์:

package main

import (
    "fmt"
    "net/http"
    "crypto/tls"
)

func main() {
    tr := &http.Transport{
        TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
    }
    client := &http.Client{Transport: tr}
    _, err := client.Get("https://golang.org/")
    if err != nil {
        fmt.Println(err)
    }
}

17
InsecureSkipVerify: trueฉันสงสัยว่าจะนำใบรับรองที่เชื่อถือได้เพื่อให้การเชื่อมต่อสามารถนำมาใช้โดยไม่ต้อง เป็นไปได้หรือไม่
topskip

7
NameToCertificateอาจช่วยได้โปรดดูtls.Configเอกสาร: golang.org/pkg/crypto/tls/#Config
cyberdelia

1
นี่คือตัวอย่างสำหรับการเพิ่มพูลใบรับรอง CA ที่กำหนดเอง: golang.org/pkg/crypto/tls/#example_Dial คุณสามารถใช้สิ่งนี้ในไคลเอนต์ HTTP ได้เช่นกัน
Bitbored

7
มีผู้คนจำนวนมากพยายามปิดใช้งานการตรวจสอบชื่อโฮสต์ (ไม่ได้ปิดการตรวจสอบใบรับรองทั้งหมด) นี่คือคำตอบที่ผิด Go ต้องการให้คุณตั้งค่า ServerName ใน tls config เพื่อให้ตรงกับ CN ของโฮสต์ที่คุณกำลังเชื่อมต่อหากไม่ใช่ชื่อ DNS ที่คุณเชื่อมต่อ InsecureSkipVerify ไม่มีความปลอดภัยมากไปกว่าเทลเน็ตแบบธรรมดาไปยังพอร์ต ไม่มีการตรวจสอบสิทธิ์กับการตั้งค่านี้ User ServerName แทน!
Rob

8
ระวัง: การขนส่งที่สร้างขึ้นเช่นการใช้งานที่ศูนย์ค่ามากที่สุดของเขตและดังนั้นจึงlooses ค่าเริ่มต้นทั้งหมด ตามคำตอบด้านล่างแนะนำคุณอาจต้องการคัดลอก เรามีความสนุกสนานในการหาสาเหตุที่เราใช้ตัวอธิบายไฟล์Dialer.Timeoutไม่เพียงพอเนื่องจากเราทำไฟล์.
mknecht

26

นี่เป็นวิธีที่ทำได้โดยไม่สูญเสียการตั้งค่าเริ่มต้นของDefaultTransportและโดยไม่ต้องใช้คำขอปลอมตามความคิดเห็นของผู้ใช้

defaultTransport := http.DefaultTransport.(*http.Transport)

// Create new Transport that ignores self-signed SSL
customTransport := &http.Transport{
  Proxy:                 defaultTransport.Proxy,
  DialContext:           defaultTransport.DialContext,
  MaxIdleConns:          defaultTransport.MaxIdleConns,
  IdleConnTimeout:       defaultTransport.IdleConnTimeout,
  ExpectContinueTimeout: defaultTransport.ExpectContinueTimeout,
  TLSHandshakeTimeout:   defaultTransport.TLSHandshakeTimeout,
  TLSClientConfig:       &tls.Config{InsecureSkipVerify: true},
}

UPDATE

วิธีที่สั้นกว่า:

customTransport := &(*http.DefaultTransport.(*http.Transport)) // make shallow copy
customTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}

วิธีที่เหมาะสม (ณ วันที่ 1.13) (ตามคำตอบด้านล่าง ):

customTransport := http.DefaultTransport.(*http.Transport).Clone()
customTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}

คำเตือน : สำหรับการทดสอบ / พัฒนาเท่านั้น อย่างอื่นดำเนินการด้วยความเสี่ยงของคุณเอง !!!


การคัดลอกการตั้งค่าการขนส่งเริ่มต้นโดยใช้mytransportsettings := &(*http.DefaultTransport.(*http.Transport))แล้วปรับเปลี่ยนการกำหนดค่าไคลเอนต์ TLS จะง่ายกว่าmytransportsettings.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}ไหม
TheDiveO

คุ้มค่าที่จะลองฉันคิดว่าฉันพยายามทำให้แน่ใจว่าฉันไม่ได้แก้ไขค่าเริ่มต้นการขนส่งจริง
โจนาธานลิน

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

19

คำตอบทั้งหมดนี้ผิด! อย่าใช้InsecureSkipVerifyเพื่อจัดการกับ CN ที่ไม่ตรงกับชื่อโฮสต์ นักพัฒนาซอฟต์แวร์ Go ยืนกรานอย่างไม่เต็มใจที่จะไม่ปิดใช้งานการตรวจสอบชื่อโฮสต์ (ซึ่งมีการใช้งานที่ถูกต้องเช่นอุโมงค์, nats, ใบรับรองคลัสเตอร์ที่ใช้ร่วมกัน ฯลฯ ) ในขณะเดียวกันก็มีบางอย่างที่ดูคล้ายกัน แต่จริงๆแล้วไม่สนใจการตรวจสอบใบรับรองโดยสิ้นเชิง คุณจำเป็นต้องทราบว่าใบรับรองถูกต้องและลงนามโดยใบรับรองที่คุณเชื่อถือ แต่ในสถานการณ์ทั่วไปคุณจะรู้ว่า CN ไม่ตรงกับชื่อโฮสต์ที่คุณเชื่อมต่อ สำหรับผู้ที่ตั้งอยู่บนServerName tls.Configถ้าtls.Config.ServerName== remoteServerCN การตรวจสอบใบรับรองจะสำเร็จ นี่คือสิ่งที่คุณต้องการ InsecureSkipVerifyหมายความว่าไม่มีการรับรองความถูกต้อง และมันก็สุกงอมสำหรับผู้ชายที่อยู่ตรงกลาง การเอาชนะวัตถุประสงค์ของการใช้ TLS

มีการใช้งานที่ถูกต้องอย่างหนึ่งสำหรับInsecureSkipVerify: ใช้เพื่อเชื่อมต่อกับโฮสต์และรับใบรับรองจากนั้นยกเลิกการเชื่อมต่อทันที หากคุณตั้งค่ารหัสเพื่อใช้InsecureSkipVerifyโดยทั่วไปเป็นเพราะคุณตั้งค่าไม่ServerNameถูกต้อง (จะต้องมาจาก env var หรืออะไรบางอย่าง - อย่าปวดท้องเกี่ยวกับข้อกำหนดนี้ ... ทำอย่างถูกต้อง)

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


1
คุณบังเอิญรู้จักไซต์ที่ฉันสามารถทดสอบได้หรือไม่? golang org ตอนนี้ไม่เกิดข้อผิดพลาดอีกต่อไป
topskip

3
คำตอบนี้ทำให้เข้าใจผิดอย่างมาก หากคุณยอมรับใบรับรองที่ลงนามอย่างถูกต้องโดยไม่คำนึงถึงชื่อโฮสต์คุณจะยังไม่ได้รับความปลอดภัยที่แท้จริง ฉันสามารถขอรับใบรับรองที่ถูกต้องสำหรับโดเมนที่ฉันควบคุมได้อย่างง่ายดาย หากคุณกำลังจะระบุชื่อโฮสต์ของฉันสำหรับการตรวจสอบ TLS คุณจะสูญเสียการตรวจสอบความถูกต้องว่าฉันเป็นคนที่ฉันพูดจริงๆ เมื่อถึงจุดนั้นความจริงที่ว่าใบรับรองนั้น "ถูกต้อง" ก็ไม่สำคัญ มันยังผิดและคุณยังเสี่ยงที่จะมีคนอยู่ตรงกลาง
Daniel Farrell

5
ในองค์กรที่คุณไม่ไว้วางใจCA เชิงพาณิชย์ใด ๆ (เช่น: หากอยู่นอกสหรัฐอเมริกาเป็นต้น) และแทนที่ด้วย CA หนึ่งรายการสำหรับองค์กรของคุณคุณเพียงแค่ต้องตรวจสอบว่านี่เป็นหนึ่งในใบรับรองของคุณ การตรวจสอบชื่อโฮสต์มีไว้สำหรับสถานการณ์ที่ผู้ใช้คาดหวังว่าชื่อโฮสต์จะตรงกัน แต่ DNS ไม่ปลอดภัย ในองค์กรโดยทั่วไปคุณจะเชื่อมต่อกับคลัสเตอร์ของเครื่องที่ไม่สามารถจับคู่ชื่อโฮสต์ / IP ได้เนื่องจากเป็นโคลนที่แน่นอนของเครื่องที่สร้างภายใต้ IP ใหม่ ชื่อ DNS ค้นหาใบรับรองและ cert คือ id ไม่ใช่ชื่อ DNS
Rob

3
แนวคิดก็คือรหัสไคลเอนต์เชื่อถือ CA ขององค์กรเท่านั้น จริงๆแล้วปลอดภัยกว่าระบบ CA ที่ใช้อยู่ในปัจจุบัน ในกรณีนั้นไม่มีอะไร (Thawte, Versign ฯลฯ ) ... ที่เชื่อถือได้ แค่ CA ที่เราเรียกใช้ เว็บเบราว์เซอร์มีรายการความน่าเชื่อถือมากมาย บริการที่คุยกันมีเพียง CA เดียวในไฟล์ความน่าเชื่อถือ
Rob

1
ขอบคุณ @Rob ฉันมีการตั้งค่าที่เหมือนกันซึ่งบริการของเราไว้วางใจ CA เดียวและไม่มีอะไรอื่น นี่เป็นข้อมูลสำคัญและช่วยได้มาก
user99999991

8

วิธีที่ถูกต้องในการดำเนินการนี้หากคุณต้องการรักษาการตั้งค่าการขนส่งเริ่มต้นคือตอนนี้ (ณ Go 1.13):

customTransport := http.DefaultTransport.(*http.Transport).Clone()
customTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
client = &http.Client{Transport: customTransport}

Transport.Cloneทำสำเนาการขนส่งอย่างละเอียด วิธีนี้ทำให้คุณไม่ต้องกังวลว่าจะพลาดฟิลด์ใหม่ ๆ ที่เพิ่มเข้าไปในโครงสร้างTransportเมื่อเวลาผ่านไป


7

หากคุณต้องการใช้การตั้งค่าเริ่มต้นจากแพ็กเกจ http ดังนั้นคุณไม่จำเป็นต้องสร้างอ็อบเจ็กต์ Transport และ Client ใหม่คุณสามารถเปลี่ยนเพื่อละเว้นการตรวจสอบใบรับรองได้ดังนี้:

tr := http.DefaultTransport.(*http.Transport)
tr.TLSClientConfig.InsecureSkipVerify = true

7
การทำเช่นนี้จะส่งผลให้panic: runtime error: invalid memory address or nil pointer dereference
OscarRyz

6
หากคุณไม่ได้ใช้ผู้ขอ http เริ่มต้นก่อนหน้านี้คุณต้องบังคับคำขอปลอมเพื่อให้การขนส่งเริ่มต้นเริ่มต้น
Cornel Damian

1
นี่ไม่ได้ปิดใช้งานการตรวจสอบชื่อโฮสต์ จะปิดใช้งานการตรวจสอบใบรับรองทั้งหมด บังคับให้คุณตั้งชื่อเซิร์ฟเวอร์ให้เท่ากับ CN ในใบรับรองของสิ่งที่คุณเชื่อมต่อ InsecureSkipVerify จะเชื่อมต่อกับเซิร์ฟเวอร์ MITM โกงที่แสร้งทำเป็นส่งต่อไปยังบริการจริง การใช้ InsecureSkipVerify ที่ถูกต้องตามกฎหมายเท่านั้นคือการคว้าใบรับรองของรีโมตเอนด์และตัดการเชื่อมต่อทันที
Rob

สิ่งนี้ใช้ได้ก็ต่อเมื่อคุณระบุ VerifyPeerCertificate หากคุณเพิ่งตั้งค่า InsecureSkipVerify จะไม่ตรวจสอบเลย แต่มีการเพิ่ม VerifyPeerCertificate เพื่อให้คุณสามารถเขียนเช็คใหม่เพื่อทำสิ่งที่คุณต้องการได้ คุณอาจต้องการละเว้นชื่อโฮสต์หรืออาจถึงวันหมดอายุ Google สำหรับการใช้งาน VerifyPeerCertificate ต่างๆที่ทำเช่นนี้
Rob

0

โดยทั่วไปโดเมน DNS ของ URL ต้องตรงกับหัวข้อใบรับรองของใบรับรอง

ในอดีตอาจเป็นได้โดยการตั้งค่าโดเมนเป็น cn ของใบรับรองหรือโดยกำหนดให้โดเมนเป็น Subject Alternative Name

การสนับสนุน cn เลิกใช้งานไปนานแล้ว (ตั้งแต่ปี 2000 ในRFC 2818 ) และเบราว์เซอร์ Chrome จะไม่ดู cn อีกต่อไปดังนั้นวันนี้คุณต้องมีโดเมน DNS ของ URL เป็นชื่อทางเลือกของหัวเรื่อง

RFC 6125ซึ่งห้ามไม่ให้ตรวจสอบ cn หากมี SAN สำหรับโดเมน DNS แต่จะไม่แสดงหากมี SAN สำหรับที่อยู่ IP RFC 6125 ซ้ำว่า cn เลิกใช้แล้วซึ่งได้กล่าวไปแล้วใน RFC 2818 และฟอรัมเบราว์เซอร์ของผู้ออกใบรับรองจะนำเสนอซึ่งเมื่อรวมกับ RFC 6125 โดยพื้นฐานแล้วหมายความว่า cn จะไม่ถูกตรวจสอบสำหรับชื่อโดเมน DNS

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