잘못된 인증서로 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

이제이 인증서를 직접 신뢰하고 싶습니다 (지문 등을 검증 할 수있는 자체 발급 인증서를 상상해보십시오). 인증서를 요청하고 검증 / 신뢰하려면 어떻게해야합니까?

인증서를 다운로드하고 내 파일에로드하고 tls.Configstruct를 채우려면 openssl을 사용해야 할 것입니다 !?


5
이것은 "잘못된 인증서"가 아니라 다른 CN을 가진 인증서입니다. InsecureSkipVerify는 여기서 합법적 인 사용이 아닙니다. 연결하려는 대상과 일치하도록 tls.Config에서 ServerName을 설정해야합니다. 이 StackOverflow 게시물은 Go 코드의 큰 보안 허점을 모든 곳에 퍼뜨리는 원인이됩니다. InsecureSkipVerify는 인증서를 전혀 확인하지 않습니다. 원하는 것은 CN이 호스트 이름과 일치하지 않더라도 인증서가 신뢰할 수있는 엔터티에 의해 합법적으로 서명되었는지 확인하는 것입니다. 터널과 NATS로 인해 합법적으로이 불일치가 발생할 수 있습니다.
Rob

답변:


283

보안 정보 : 보안 검사를 비활성화하는 것은 위험하므로 피해야합니다.

기본 클라이언트의 모든 요청에 ​​대해 전역 적으로 보안 검사를 비활성화 할 수 있습니다.

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
.NET없이 연결을 사용할 수 있도록 신뢰할 수있는 인증서를 어디에 두어야할지 궁금 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 dec.

7
많은 사람들이 호스트 이름 검사를 비활성화하려고합니다 (인증서 검사를 완전히 비활성화하지 않음). 이것은 잘못된 대답입니다. Go에서는 연결 한 DNS 이름이 아닌 경우 연결하려는 호스트의 CN과 일치하도록 tls 구성의 ServerName을 설정해야합니다. InsecureSkipVerify는 포트에 대한 평범한 텔넷보다 더 안전하지 않습니다. 이 설정에는 인증이 없습니다. 대신 사용자 ServerName!
Rob

8
주의 : 대부분의 필드에 0 값을 사용하여 모든 기본값을 잃어 버리는 것처럼 생성 된 전송 . 으로 대답은 아래의 제안, 당신은 그들을 통해 복사 할 수 있습니다. 우리는 알아내는 재미의 좋은 거래를했다 우리가 파일 설명이 부족하고 왜 우리는을 잃었 때문에 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},
}

최신 정보

짧은 방법 :

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

올바른 방법 (Go 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

시도할만한 가치가 있습니다. 실제 DefaultTransport를 수정하지 않으려 고 노력한 것 같습니다
Jonathan Lin

1
이것은 당신이했던 것처럼 얕은 사본을 만들도록 보장 합니다. 이것은 논의 된 사용 사례에 충분합니다. 나는 실제로 이것을 작업 코드에 배포하고 있습니다.
TheDiveO

19

이 모든 답변이 잘못되었습니다! InsecureSkipVerify호스트 이름과 일치하지 않는 CN을 처리하는 데 사용하지 마십시오 . Go 개발자는 호스트 이름 검사 (터널, nats, 공유 클러스터 인증서 등의 합법적 인 용도)를 비활성화하지 않는 것에 대해 단호한 태도를 보였으며 유사 해 보이지만 실제로 는 인증서 검사를 완전히 무시했습니다. 인증서가 유효하며 신뢰할 수있는 인증서에 의해 서명되었음을 알아야합니다. 그러나 일반적인 시나리오에서는 CN이 연결 한 호스트 이름과 일치하지 않는다는 것을 알고 있습니다. 사람들을 위해, 설정 ServerNametls.Config. 경우 tls.Config.ServerName== remoteServerCN 후 인증서 검사가 성공합니다. 이것이 당신이 원하는 것입니다. InsecureSkipVerify인증이 없음을 의미합니다. Man-In-The-Middle에게는 무르 익었습니다. TLS 사용 목적을 무너 뜨리는 것.

한 가지 합법적 인 용도가 있습니다 InsecureSkipVerify.이를 사용하여 호스트에 연결하고 인증서를 가져온 다음 즉시 연결을 끊습니다. 를 사용하도록 코드를 설정 한 경우 InsecureSkipVerify일반적으로 ServerName올바르게 설정하지 않았기 때문입니다 (env var 또는 다른 것에서 가져와야합니다.

특히 클라이언트 인증서를 사용하고 인증에 의존하는 경우 기본적으로 실제로 더 이상 로그인하지 않는 가짜 로그인을 갖게됩니다. 하는 코드를 거부하십시오. 그렇지 InsecureSkipVerify않으면 어려운 방법으로 무엇이 잘못되었는지 알게 될 것입니다!


1
이것을 테스트 할 수있는 사이트를 알고 있습니까? golang org는 이제 더 이상 오류를 발생시키지 않습니다.
topskip

3
이 대답은 매우 오해의 소지가 있습니다. 호스트 이름에 관계없이 유효하게 서명 된 인증서를 수락하는 경우 여전히 실제 보안이 제공되지 않습니다. 내가 제어하는 ​​모든 도메인에 대해 유효한 인증서를 쉽게 얻을 수 있습니다. TLS 검사를 위해 내 호스트 이름을 지정하려는 경우 내가 실제로 내가 말하는 사람이라는 유효성 검사를 잃게됩니다. 이 시점에서 인증서가 "legit"이라는 사실은 중요하지 않습니다. 그것은 여전히 틀렸고 당신은 여전히 중간에있는 사람에게 취약합니다.
Daniel Farrell

5
실제로 신뢰하지 않는 엔터프라이즈에서 어떤 상업있는 CA를 (예 : 그들은 밖에 예를 들어 미국의 경우) 및 엔터프라이즈를위한 하나의 CA로 교체, 당신은 이것이 당신의 인증서 표시 중 하나입니다 유효성을 검사 할 필요가있다. 호스트 이름 확인은 사용자가 호스트 이름이 일치 할 것으로 예상하지만 DNS가 안전하지 않은 상황을위한 것입니다. 기업에서는 일반적으로 호스트 이름 / IP가 일치 할 수없는 머신 클러스터 에 연결합니다. 이는 새 IP로 생성 된 머신의 정확한 복제본이기 때문입니다. DNS 이름은 인증서를 찾고 cert는 DNS 이름이 아니라 ID입니다.
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 패키지의 기본 설정을 사용하여 새 전송 및 클라이언트 개체를 만들 필요가없는 경우 다음과 같이 인증서 확인을 무시하도록 변경할 수 있습니다.

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

7
이 초래 일panic: runtime error: invalid memory address or nil pointer dereference
OscarRyz

6
이 전에 기본 HTTP 요청을 사용하지 않는 경우, 당신은 초기화있어 기본 전송 있도록 가짜 요청을 강제해야한다
코넬 다 미안

1
이것은 호스트 이름 검사를 비활성화하지 않습니다. 모든 인증서 검사를 비활성화합니다. Go는 연결하는 인증서의 CN과 동일한 ServerName을 설정하도록합니다. InsecureSkipVerify는 실제 서비스로 전달하는 척하는 악성 MITM 서버에 연결합니다. InsecureSkipVerify에 대한 유일한 합법적 인 사용은 원격 끝의 인증서를 가져와 즉시 연결을 끊는 것입니다.

VerifyPeerCertificate도 지정하는 경우에만 괜찮습니다. InsecureSkipVerify 만 설정하면 전혀 확인하지 않습니다. 그러나 VerifyPeerCertificate가 추가되어 필요한 작업을 수행하기 위해 검사를 다시 작성할 수 있습니다. 호스트 이름 또는 만료 날짜를 무시할 수 있습니다. 이를 수행하는 VerifyPeerCertificate의 다양한 구현을위한 Google.
Rob

0

일반적으로 URL의 DNS 도메인은 인증서의 인증서 주체와 일치해야합니다.

이전에는 도메인을 인증서의 cn으로 설정하거나 도메인을 주체 대체 이름으로 설정하는 것이 가능했습니다.

cn에 대한 지원은 오랫동안 ( RFC 2818의 2000 이후 ) 더 이상 사용되지 않았으며 Chrome 브라우저는 더 이상 cn을 보지 않을 것이므로 오늘날 URLDNS 도메인을 주체 대체 이름으로 사용해야합니다 .

RFC 6125DNS 도메인 용 SAN이있는 경우 cn 검사를 금지하지만 IP 주소 용 SAN이있는 경우는 금지하는 . RFC 6125는 또한 RFC 2818에서 이미 언급 된 cn이 더 이상 사용되지 않는다는 사실을 반복합니다. 그리고 RFC 6125와 함께 기본적으로 cn이 DNS 도메인 이름을 확인하지 않음을 의미하는 인증 기관 브라우저 포럼이 표시됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.