GoLang_net package, OpenSSL Certificate, Certificate Chain

wldbs._.·2024년 11월 29일

WEB-STUDY

목록 보기
3/9
post-thumbnail

GoDoc: net/http package
How to set up HTTPS on golang web server?
Making HTTP requests in Go
웹 개발에서의 Redirect: 페이지 이동과 사용 방법
자체 서명 사설 SSL 인증서 만들기
https 제공을 위한 인증된 SSL과 서버 개인 키 생성하기
[Server] Go Web Server HTTPS 적용하기
openssl 없이 Go HTTPS 서버 실행해보기
인증서 체인
Certificate Chain
리다이렉트(Redirect) | 토스페이먼츠
CHATGPT

11/29~12/3 스터디

  • Go 기본 패키지의 HTTP와 HTTPS 제공 패키지 알아보기

http 통신은 net 패키지로 했던 기억이 나는데.. https 통신도 가능할까?
이전 시간에 알아본 http/https 프로토콜에서 https 통신을 이루기 위해서는 CA의 인증서가 팔요할텐데,,


1. GO - HTTP

Go 언어에서 HTTP 통신을 처리하기 위해 net/http 패키지를 사용한다.
이 패키지는 HTTP 클라이언트와 서버를 구현하기 위한 다양한 기능을 제공한다.


1-1. 예제

  1. 아래 코드는 간단한 HTTP 서버를 구현한다.

아래 코드를 실행하면,
터미널에 Server is running on :8080 가 출력되고,
브라우저에서 http://localhost:8080으로 접속하면 "Hello, World!" 메시지를 확인할 수 있다.

package main

import (
	"fmt"
	"net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "Hello, World!") // 응답 메시지 작성
}

func main() {
	http.HandleFunc("/", handler)             // 요청 경로와 핸들러 연결
	fmt.Println("Server is running on :8080") // 서버 시작 로그 출력
	http.ListenAndServe(":8080", nil)         // HTTP 서버 실행
}

  • 경로 패턴을 매칭해 그에 대응하는 핸들러를 호출한다.
  • /로 시작하는 모든 경로와 매칭

  1. 아래 코드는 HTTP 클라이언트를 구현한다.

HTTP 요청을 보내고 응답을 받는 클라이언트의 예제이다.
GET 요청을 보내고, 상태 코드와 응답 본문을 출력한다.

package main

import (
	"fmt"
	"io"
	"net/http"
)

func main() {
	resp, err := http.Get("https://velog.io/@jiyunwoo/Go-vs.-Rust-1") // GET 요청
	if err != nil {
		fmt.Println("Error:", err)
		return
	}
	defer resp.Body.Close() // 응답 본문 닫기

	body, err := io.ReadAll(resp.Body) // 응답 본문 읽기
	if err != nil {
		fmt.Println("Error reading response:", err)
		return
	}

	fmt.Println("Response Status:", resp.Status) // 응답 상태 출력
	fmt.Println("Response Body:", string(body))  // 응답 본문 출력

	/* 출력 예시
	Response Status: 200 OK
	Response Body: <!doctype html> ~~
	*/
}
  • It is important to note that the response Body should be closed after we are done reading from it to prevent memory leaks.

1-2. 주요 메서드

위 예제에서 사용된 주요 메서드들에 대해 알아보자.
내용은 패키지 문서에서 가져왔다.

func HandleFunc(pattern string, handler func(ResponseWriter, *Request))
  • HandleFunc registers the handler function for the given pattern.

  • A ResponseWriter interface is used by an HTTP handler to construct an HTTP response.

  • A Request represents an HTTP request received by a server or to be sent by a client.

  • The documentation for ServeMux explains how patterns are matched.

    • ServeMux is an HTTP request multiplexer. (요청 URL과 등록된 핸들러를 매칭)
    • It matches the URL of each incoming request against a list of registered patterns,
    • and calls the handler for the pattern that most closely matches the URL.
    • 예: GET /static/ matches a GET request whose path begins with /static/. → 어떤 요청 URL이 어떤 핸들러와 연결될지 관리

작동 방식

  • URL 패턴과 그에 대한 핸들러 미리 등록 → 요청 들어오면, 요청 URL을 등록된 패턴과 비교하여 핸들러 찾아 실행 → 핸들러 호출해 요청 처리
    • 패턴이 여러 개 등록된 경우, 요청 경로와 가장 구체적으로 일치하는 패턴의 핸들러가 실행됨!
    • /static//static/로 시작하는 모든 경로와 매칭 → 슬래시(/)로 끝나는 패턴은 경로의 하위 URL도 처리
    • /static처럼 슬래시가 없는 경우, 경로가 정확히 /static인 경우에만 매칭
func ListenAndServe(addr string, handler Handler) error
  • HTTP 서버 시작 함수, 인자로 서버 주소와 요청을 처리하는 핸들러(보통 nil로 기본값) 설정

  • ListenAndServe starts an HTTP server with a given address and handler.

func Get(url string) (resp *Response, err error)
  • Get issues a GET to the specified URL.

  • If the response is one of the following redirect codes, Get follows the redirect, up to a maximum of 10 redirects:

    • An error is returned if there were too many redirects or if there was an HTTP protocol error.
    • A non-2xx response doesn't cause an error.
    • 예: 웹사이트 A의 주소로 접속한 사용자를 웹사이트 B로 이동시키는 것

+) HTTP status codes as registered with IANA.


2. GO - HTTPS

Go 언어에서 HTTPS 통신을 구현하려면 TLS(SSL) 인증서를 사용하여
net/http 패키지의 http.ListenAndServeTLS 함수로 HTTPS 서버를 실행할 수 있다.

HTTPS는 TLS/SSL을 통해 데이터를 암호화하여 보안 통신을 제공한다.


2-1. TLS 인증서

HTTPS를 구현하려면 다음 두 가지 파일이 필요하다:

  • 인증서 파일 (cert.pem) : 서버의 공개 키, 서버의 도메인 이름, CA 정보, 만료 날짜 등 정보 포함!
  • 키 파일 (key.pem) : 서버의 개인 키 ← 이 파일은 서버에서만 사용되며 외부로 유출되서는 안 된다!
  1. 테스트 목적으로, OpenSSL을 사용해 자체 서명 인증서를 생성할 수 있다.
openssl req -x509 -newkey rsa:2048 -nodes -keyout key.pem -out cert.pem -days 365

  1. 실서비스에서는 Let's Encrypt와 같은 인증 기관(CA)을 통해 무료 인증서를 발급받는 사례가 있다고 한다.

테스트 환경에서는 자체 서명 인증서를 사용하거나, HTTPS 클라이언트에서 InsecureSkipVerify: true 옵션을 사용해 인증서 검증을 비활성화할 수 있다.

  • 서버의 인증서가 자체 서명된 인증서인 경우
  • 테스트 환경에서 인증서 검증을 생략하고 HTTPS 통신을 테스트하려는 경우

→ 하지만 실제 서비스에서는 절대 사용하지 말아야 한다!


2-2. 자체 서명 SSL 인증서 만들어보기

openssl req -x509 -newkey rsa:2048 -nodes -keyout key.pem -out cert.pem -days 365
  • req : OpenSSL의 인증서 서명 요청(Certificate Signing Request, CSR)을 생성하거나 인증서를 발급할 때 사용
  • -x509 : X.509 형식의 인증서를 생성
  • -newkey rsa:2048 : 새로운 RSA 키 쌍(공개 키와 개인 키)을 생성, 키 길이를 2048비트로 설정
  • -nodes : 개인 키(Private Key)를 암호화하지 않고 편리하게 저장 → 기본적으로는 개인 키 암호화해 파일로 저장
  • -keyout key.pem : 생성된 개인 키를 key.pem 파일로 저장
  • -out cert.pem : 생성된 인증서를 cert.pem 파일로 저장
  • -days 365 : 인증서의 유효 기간을 365일로 설정

→ 리눅스 환경에서 위 명령어를 실행해본 결과, cert.pemkey.pem이 만들어졌다! 신기하다,,

+) Snippet 참고


2-3. 예제

아래는 HTTPS 서버 구현 예제이다.

package main

import (
	"fmt"
	"net/http"
)

func handler2(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "Hello, Secure World!")
}

func main() {
	http.HandleFunc("/", handler2)                // 요청 처리 핸들러 등록
	fmt.Println("Starting HTTPS server on :8443") // HTTPS 서버 실행 로그
	err := http.ListenAndServeTLS(":8443", "cert.pem", "key.pem", nil)
	if err != nil {
		fmt.Println("Error starting server:", err)
	}
}
  1. 위 코드를 실행한다.

  2. 브라우저에서 https://localhost:8443로 접속한다.

  3. 인증서가 자체 서명된 것이므로 브라우저에서 경고 메시지가 표시될 수 있다.


아래는 직접 실행해본 결과이다.

→ 고급 → 안전하지 않음 으로 이동

→ Hello, Secure World! 가 출력되는 것을 볼 수 있다.

→ 인증서 정보도 확인할 수 있다. 유효 기간이 365일로 설정되었고, 인증서와 서버의 공개키 등등을 직접 확인해볼 수 있었다.


2-4. 주요 메서드

HTTPS는 ListenAndServeTLS로 TLS를 활성화하는 것을 제외하면 HTTP와 거의 동일하게 구현할 수 있다.

핵심 차이점은 통신이 암호화된다는 점이다.
나머지 라우팅, 핸들러 설정, 요청/응답 처리 방식은 모두 동일하다.

func ListenAndServeTLS(addr, certFile, keyFile string, handler Handler) error

  • HTTPS 서버를 시작하는 함수 ▷ 인자: 서버 주소, 인증서 파일 경로, 키 파일 경로, 요청을 처리할 핸들러.

  • ListenAndServeTLS acts identically to ListenAndServe, except that it expects HTTPS connections.

  • Additionally, files containing a certificate and matching private key for the server must be provided.

    • If the certificate is signed by a certificate authority,
      the certFile should be the concatenation of the server's certificate, any intermediates, and the CA's certificate.

번외. 인증서 체인

  • 클라이언트(예: 브라우저)는 서버 인증서를 신뢰하려면,
    그 인증서가 신뢰할 수 있는 루트 인증서로부터 서명되었음을 확인해야 한다. 이때 중간 인증서들이 필요!
    • 서버 인증서 → 중간 인증서 → 루트 인증서 순서로 인증서 체인이 완성
    • 중간 인증서가 누락되면, 클라이언트는 서버 인증서만 가지고는 신뢰할 수 없어서 "인증서 체인이 불완전하다"는 오류가 발생할 수 있다.
      → 서버에서 cert.pem 파일을 사용할 때는 서버 인증서, 중간 인증서, 루트 인증서가 모두 포함되어야 한다.
    • 만약 서버가 중간 인증서를 누락하면, 클라이언트는 루트 인증서까지 도달하지 못해 "인증서 체인이 불완전하다"는 오류가 발생.
cat server.crt intermediate.crt root.crt > cert.pem

각각 인증서를 발급하여 합쳐서 제공하는 경우도 있지만, 별도의 파일을 제공하는 경우도 있다.
후자라면, 사용자가 이 인증서들을 연결하여 하나의 파일로 만들어야 한다.


정리 ☆

루트 CA가 스스로 서명하여 루트 인증서 발급 → 루트 CA가 중간 인증서 생성 및 자신의 비공개키로 서명 → 중간 CA가 서버 인증서 생성 및 자신의 비공개키로 서명 → 서버가 클라이언트에게 서버 인증서와 중간 인증서 제공, 중간 인증서를 루트 인증서와 연결하여 신뢰 체계 검증

0. 중간 CA와 루트 CA

  • 루트 CA (Root Certificate Authority): 인증 체계의 최상위에 있는 신뢰 기관

    • 루트 인증서를 생성하며, 이 인증서는 스스로 서명(Self-Signed)됨
    • 루트 인증서는 브라우저나 운영체제에 미리 설치되어 신뢰받음
    • 중간 CA에게 인증서를 발급하고, 이를 통해 서버 인증서의 간접 서명을 가능하게 함
  • 중간 CA (Intermediate Certificate Authority)

    • 루트 CA가 발급한 중간 인증서를 사용하여 동작하는 하위 신뢰 기관
    • 서버 인증서를 발급하며, 이를 통해 최종 사용자(클라이언트)와 서버 간의 보안을 보장
    • 루트 CA를 직접 노출하지 않음으로써 루트 인증서의 보호와 손상 방지를 도모
      • 루트 인증서의 손상은
        루트 인증서부터 신뢰를 얻는 모든 인증서가 재발급되어야 하는 상황이 생기므로
        중간 인증서를 사용하여 최종 사용자에게 서버 인증서를 발급하는 방식으로 보안 수준을 높인다.

1. 인증서 유형 및 서명 관계

1) 루트 인증서 (Root Certificate)

  • 발급자: 루트 CA.
  • 서명자: 스스로 서명(Self-Signed).
  • 역할: 체계 전체의 신뢰 기반을 제공. 브라우저나 운영체제에 사전 설치되어 있음.
  • 예시: 루트 CA의 이름을 포함한 신뢰의 최상위 증명.
  • 사용: 중간 CA 인증서를 서명하여 발급.

2) 중간 인증서 (Intermediate Certificate)

  • 발급자: 루트 CA.
  • 서명자: 루트 CA의 비공개키로 서명.
  • 역할: 서버 인증서를 발급 및 서명.
  • 예시: 중간 CA의 이름과 서명을 포함한 증명.
  • 사용: 서버 인증서를 발급하고, 클라이언트가 신뢰 체인을 검증할 수 있게 함.

3) 서버 인증서 (Server Certificate)

  • 발급자: 중간 CA.
  • 서명자: 중간 CA의 비공개키로 서명.
  • 역할: 특정 서버의 도메인(예: example.com)에 대한 신뢰를 보장.
  • 예시: example.com 도메인의 소유자임을 증명.
  • 사용: 클라이언트(브라우저 등)와 서버 간의 TLS 통신 보안.

2-1. '서버 인증서'는 '중간 인증서'의 비공개키로 서명됨

  • 서버 인증서 ← (서명) ← 중간 인증서의 비공개키
  • 서버가 CA에게 도메인 소유 증명 및 CSR(서버의 공개키 포함한 요청 데이터) 전송
    → CA가 서버 인증서 발급 및 중간 인증서의 비공개키로 서명
    → 클라이언트는 중간 인증서의 공개키를 사용해 서버 인증서 검증

2-2. '중간 인증서'는 '루트 인증서'의 비공개키로 서명됨

  • 중간 인증서 ← (서명) ← 루트 인증서의 비공개키
  • 중간 인증서 발급 및 루트 인증서의 비공개키로 서명
    → 클라이언트는 루트 인증서의 공개키를 사용해 중간 인증서 검증
    → 클라이언트는 중간 인증서를 검증한 후, 최종적으로 루트 인증서가 신뢰할 수 있는 브라우저/OS에 미리 저장된 루트 인증서인지 확인

실제 통신 검증 과정

  • 서버는 클라이언트에게 서버 인증서와 중간 인증서 전송 (신뢰 체인 확인할 수 있도록)
    → 클라이언트는 중간 인증서의 공개키로 서버 인증서 검증
    → 클라이언트는 루트 인증서의 공개키로 중간 인증서 검증
    → 최종적으로, 루트 인증서가 사전 설치된 인증서인지 확인

profile
공부 기록용 24.08.05~ #LLM #RAG

0개의 댓글