Làm thế nào để thực hiện một yêu cầu https với chứng chỉ xấu?


128

Nói rằng tôi muốn có được https://golang.orglập trình. Hiện tại golang.org (ssl) có chứng chỉ xấu được cấp cho*.appspot.com Vì vậy khi tôi chạy này:

package main

import (
    "log"
    "net/http"
)

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

Tôi nhận được (như tôi mong đợi)

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

Bây giờ, tôi muốn tự tin vào chứng chỉ này (hãy tưởng tượng chứng chỉ tự cấp nơi tôi có thể xác thực dấu vân tay, v.v.): làm thế nào tôi có thể đưa ra yêu cầu và xác thực / tin cậy chứng chỉ?

Tôi có lẽ cần phải sử dụng openssl để tải chứng chỉ, tải nó vào tập tin của mình và điền vào tls.Configstruct!?


5
đây không phải là "chứng chỉ xấu" mà là chứng chỉ có CN khác. InsecureSkipVerify không phải là một sử dụng hợp pháp ở đây. Bạn phải đặt ServerName trong tls.Config để khớp với những gì bạn đang cố gắng kết nối. Bài đăng StackOverflow này đang khiến lỗ hổng bảo mật lớn trong mã Go này lan rộng khắp nơi. InsecureSkipVerify không kiểm tra chứng chỉ TẠI TẤT CẢ. Những gì bạn muốn là xác minh rằng chứng chỉ đã được ký hợp pháp bởi một thực thể đáng tin cậy, ngay cả khi CN không khớp với tên máy chủ. Các đường hầm và NATS hợp pháp có thể khiến điều này không khớp.
Cướp

Câu trả lời:


283

Lưu ý bảo mật: Vô hiệu hóa kiểm tra bảo mật là nguy hiểm và nên tránh

Bạn có thể tắt kiểm tra bảo mật trên toàn cầu cho tất cả các yêu cầu của máy khách mặc định:

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)
    }
}

Bạn có thể vô hiệu hóa kiểm tra bảo mật cho một khách hàng:

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
Tôi tự hỏi nơi để đặt một chứng chỉ tin cậy để kết nối có thể được sử dụng mà không cần InsecureSkipVerify: true. Điều đó có thể không?
topskip

7
NameToCertificatecó thể giúp đỡ, xem tls.Configtài liệu: golang.org/pkg/crypto/tls/#Config
cyberdelia

1
Dưới đây là một ví dụ để thêm nhóm chứng chỉ CA tùy chỉnh: golang.org/pkg/crypto/tls/#example_Dial Bạn cũng có thể sử dụng điều này trong ứng dụng khách HTTP.
Bitbored

7
Rất nhiều người cuối cùng ở đây cố gắng vô hiệu hóa kiểm tra tên máy chủ (không hoàn toàn vô hiệu hóa kiểm tra chứng chỉ). Đây là câu trả lời sai. Go yêu cầu bạn đặt ServerName trong cấu hình tls để khớp với CN của máy chủ bạn đang kết nối, nếu đó không phải là tên dns mà bạn đã kết nối. InsecureSkipVerify không an toàn hơn một telnet cũ đến cổng. KHÔNG có xác thực với cài đặt này. Tên người dùng thay thế!
Cướp

8
Cẩn thận: Một phương tiện giao thông được tạo như thế sử dụng các giá trị 0 cho hầu hết các trường của nó và do đó mất tất cả các giá trị mặc định . Như câu trả lời dưới đây cho thấy, bạn có thể muốn sao chép chúng qua. Chúng tôi đã có rất nhiều niềm vui để tìm ra lý do tại sao chúng tôi hết các mô tả tập tin , bởi vì chúng tôi đã mất Dialer.Timeout.
mknecht

26

Đây là một cách để làm điều đó mà không làm mất các cài đặt mặc định của DefaultTransportvà không cần yêu cầu giả theo nhận xét của người dùng.

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},
}

CẬP NHẬT

Đường ngắn hơn:

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

Cách đúng (kể từ Go 1.13) (được cung cấp bởi câu trả lời bên dưới ):

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

Cảnh báo : Chỉ dành cho mục đích thử nghiệm / phát triển. Bất cứ điều gì khác, tiến hành có nguy cơ của riêng bạn !!!


Sẽ dễ dàng hơn nếu chỉ sao chép cài đặt truyền tải mặc định bằng cách sử dụng mytransportsettings := &(*http.DefaultTransport.(*http.Transport))và sau đó chỉ sửa đổi cấu hình máy khách TLS mytransportsettings.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}?
TheDiveO

Đáng để thử, tôi nghĩ rằng tôi đã cố gắng đảm bảo rằng tôi không sửa đổi DefaultTransport thực sự
Jonathan Lin

1
điều này đảm bảo tạo ra một bản sao nông, giống như bạn đã làm, đủ cho trường hợp sử dụng được thảo luận. Tôi thực sự đang triển khai điều này trong mã làm việc.
TheDiveO

19

Tất cả những câu trả lời đều sai! Không sử dụng InsecureSkipVerifyđể đối phó với một CN không khớp với tên máy chủ. Các nhà phát triển Go không chính xác kiên quyết về việc không vô hiệu hóa kiểm tra tên máy chủ (có sử dụng hợp pháp - đường hầm, nats, chia sẻ cụm, v.v.), trong khi cũng có một cái gì đó trông tương tự nhưng thực sự hoàn toàn bỏ qua kiểm tra chứng chỉ. Bạn cần biết rằng chứng chỉ hợp lệ và được ký bởi một chứng chỉ mà bạn tin tưởng. Nhưng trong các tình huống phổ biến, bạn biết rằng CN sẽ không khớp với tên máy chủ mà bạn đã kết nối. Đối với những người, thiết lập ServerNametrên tls.Config. Nếu tls.Config.ServerName== remoteServerCN, thì kiểm tra chứng chỉ sẽ thành công. Đây là cái bạn muốn. InsecureSkipVerifycó nghĩa là KHÔNG có xác thực; và nó đã chín muồi cho một Man-In-The-Middle; đánh bại mục đích sử dụng TLS.

Có một cách sử dụng hợp pháp cho InsecureSkipVerify: sử dụng nó để kết nối với máy chủ và lấy chứng chỉ của nó, sau đó ngắt kết nối ngay lập tức. Nếu bạn thiết lập mã của mình để sử dụng InsecureSkipVerify, thông thường là do bạn đã không đặt ServerNameđúng (nó sẽ cần đến từ một var env hoặc một cái gì đó - đừng đau bụng về yêu cầu này ... hãy làm điều đó một cách chính xác).

Cụ thể, nếu bạn sử dụng certs của khách hàng và dựa vào chúng để xác thực, về cơ bản bạn có một thông tin đăng nhập giả mà không thực sự đăng nhập nữa. Từ chối mã đó InsecureSkipVerify, hoặc bạn sẽ học được những gì sai với nó một cách khó khăn!


1
Bạn có tình cờ biết một trang web nơi tôi có thể kiểm tra điều này không? golang org bây giờ không ném lỗi nữa.
topskip

3
Câu trả lời này là sai lầm khủng khiếp. Nếu bạn chấp nhận bất kỳ chứng chỉ được ký hợp lệ bất kể tên máy chủ, bạn vẫn không nhận được bảo mật thực sự. Tôi có thể dễ dàng có được chứng chỉ hợp lệ cho bất kỳ miền nào tôi kiểm soát; nếu bạn chỉ định chỉ định tên máy chủ của tôi cho kiểm tra TLS, bạn sẽ mất xác thực rằng tôi thực sự là người tôi nói. Tại thời điểm đó, thực tế là chứng chỉ "hợp pháp" không thành vấn đề; nó vẫn sai và bạn vẫn dễ bị tổn thương bởi người đàn ông ở giữa.
Daniel Farrell

5
Trong một Doanh nghiệp nơi bạn thực sự không tin tưởng bất kỳ CA thương mại nào (ví dụ: nếu họ ở ngoài Hoa Kỳ) và thay thế bằng một CA cho Doanh nghiệp của bạn, bạn chỉ cần xác thực rằng đây là một trong những giấy tờ của bạn. Kiểm tra tên máy chủ dành cho các tình huống mà người dùng đang mong đợi tên máy chủ trùng khớp, nhưng DNS không an toàn. Trong doanh nghiệp, bạn thường kết nối với một cụm máy mà tên máy chủ / IP không thể khớp với nhau, bởi vì đó là bản sao chính xác của một máy được sinh ra dưới IP mới. Tên dns tìm thấy cert và cert là id, không phải tên dns.
Rob

3
ý tưởng là mã khách hàng chỉ tin tưởng CA doanh nghiệp. Nó thực sự an toàn hơn nhiều so với hệ thống CA hiện đang sử dụng. Trong trường hợp đó, không có gì (Thawte, Versign, v.v.) ... được tin cậy. Chỉ cần CA mà chúng tôi chạy. Trình duyệt web có danh sách tin cậy rất lớn. Các dịch vụ nói chuyện với nhau chỉ có một CA trong tệp tin cậy của nó.
Rob

1
cảm ơn bạn @Rob Tôi có một thiết lập giống hệt nhau trong đó các dịch vụ của chúng tôi chỉ tin tưởng giống nhau, CA đơn lẻ và không có gì khác. Đây là thông tin quan trọng và nhiều sự giúp đỡ.
dùng99999991

8

Cách chính xác để thực hiện việc này nếu bạn muốn duy trì cài đặt vận chuyển mặc định là ngay bây giờ (kể từ Go 1.13):

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

Transport.Clone tạo một bản sao sâu sắc của giao thông vận tải. Bằng cách này, bạn không phải lo lắng về việc bỏ lỡ bất kỳ trường mới nào được thêm vào Transportcấu trúc theo thời gian.


7

Nếu bạn muốn sử dụng các cài đặt mặc định từ gói http, vì vậy bạn không cần tạo đối tượng Giao thông và Máy khách mới, bạn có thể thay đổi để bỏ qua xác minh chứng chỉ như sau:

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

7
Làm điều này sẽ dẫn đếnpanic: runtime error: invalid memory address or nil pointer dereference
OscarRyz

6
Nếu bạn không sử dụng người yêu cầu http mặc định trước đó, bạn phải buộc một yêu cầu giả mạo để vận chuyển mặc định được khởi tạo
Cornel Damian

1
điều này không vô hiệu hóa kiểm tra tên máy chủ. nó vô hiệu hóa tất cả các kiểm tra chứng chỉ. Bắt buộc bạn phải đặt Tên máy chủ bằng với CN trong chứng nhận về những gì bạn kết nối. InsecureSkipVerify sẽ kết nối với một máy chủ MITM giả mạo giả vờ chuyển tiếp đến các dịch vụ thực. Việc sử dụng hợp pháp CHỈ cho một InsecureSkipVerify là lấy chứng chỉ của đầu từ xa và ngay lập tức ngắt kết nối.
Cướp

Điều này chỉ ổn nếu bạn CSONG chỉ định một ConfirmPeerCertert. Nếu bạn chỉ đặt InsecureSkipVerify thì nó hoàn toàn không kiểm tra. Nhưng một ConfirmPeerCertert đã được thêm vào để bạn có thể viết lại séc để làm những gì bạn cần. Bạn có thể muốn bỏ qua tên máy chủ, hoặc thậm chí ngày hết hạn. Google cho các triển khai khác nhau của ConfirmPeerCertert thực hiện điều này.
Cướp

0

Nói chung, Tên miền DNS của URL PHẢI khớp với Chủ đề chứng chỉ của chứng chỉ.

Trước đây, điều này có thể bằng cách đặt tên miền là cn của chứng chỉ hoặc bằng cách đặt tên miền làm Tên thay thế chủ đề.

Hỗ trợ cho cn không được chấp nhận trong một thời gian dài (kể từ năm 2000 trong RFC 2818 ) và trình duyệt Chrome thậm chí sẽ không xem cn nữa, vì vậy hôm nay bạn cần phải có Tên miền DNS của URL làm Tên thay thế chủ đề.

RFC 6125 cấm kiểm tra cn nếu có SAN cho Miền DNS, nhưng không có nếu SAN cho Địa chỉ IP có mặt. RFC 6125 cũng nhắc lại rằng cn không được dùng trong RFC 2818. Và Diễn đàn Trình duyệt Chứng nhận sẽ có mặt kết hợp với RFC 6125 về cơ bản có nghĩa là cn sẽ không bao giờ được kiểm tra tên miền DNS.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.