Go言語でWebサービスのセキュリティを向上させたい開発者必見!本記事では、crypto/tls
パッケージを用いたTLSv1.3サーバーおよびクライアントの実装方法を詳細に解説。MinVersion/MaxVersion設定、自己署名証明書の取り扱い、実際のコード例を通じて、高速かつセキュアな通信プロトコルの導入をサポートします。
秘密鍵と証明書
server.crt
とserver.key
を作成します
openssl req -x509 -newkey rsa:4096 -keyout server.key -out server.crt -days 365 -nodes -subj "/CN=localhost" -addext "subjectAltName = DNS:localhost"
このコマンドは、OpenSSL を使用して自己署名 (Self-Signed) のX.509証明書とそれに対応する秘密鍵を生成するためのものです。主に、開発環境やテスト環境でHTTPS通信を試す際などに利用されます。
各オプションの意味は以下の通りです。
openssl req
:- X.509証明書署名要求 (Certificate Signing Request: CSR) や、自己署名証明書を生成するためのOpenSSLコマンドです。
-x509
:- CSRを生成するのではなく、直接自己署名証明書を生成することを示します。これにより、認証局 (CA) を介さずに、自身で証明書を発行することができます。
-newkey rsa:4096
:- 新しい秘密鍵を生成することを示します。
rsa:4096
: RSAアルゴリズムを使用し、鍵長を4096ビットとすることを指定します。鍵長が長いほどセキュリティは向上しますが、処理負荷も増加します。一般的に2048ビット以上が推奨されます。
-keyout server.key
:- 生成された秘密鍵の出力ファイル名を指定します。この例では
server.key
という名前で保存されます。 - 重要: このファイルは非常に機密性が高く、外部に漏洩しないよう厳重に管理する必要があります。
- 生成された秘密鍵の出力ファイル名を指定します。この例では
-out server.crt
:- 生成された証明書の出力ファイル名を指定します。この例では
server.crt
という名前で保存されます。 .crt
は通常PEM (Privacy-Enhanced Mail) 形式の証明書ファイルに使用される拡張子です。
- 生成された証明書の出力ファイル名を指定します。この例では
-days 365
:- 生成される証明書の有効期間を日単位で指定します。この例では365日間(約1年間)有効な証明書が作成されます。
-nodes
:no DES
の略で、生成される秘密鍵を暗号化しないことを意味します。- このオプションがない場合、秘密鍵にパスフレーズを設定するよう求められます。パスフレーズを設定すると、秘密鍵を使用するたびにパスフレーズの入力が必要になり、自動化されたスクリプトなどで利用する際には不便です。テスト環境などでは
-nodes
を使用することが多いですが、本番環境ではセキュリティのためパスフレーズを設定することが推奨されます。
-subj "/CN=localhost"
:- 証明書のSubject (主体者情報) を直接指定します。通常、このオプションがない場合は、証明書に必要な情報を対話形式で入力するよう求められます。
"/CN=localhost"
: Common Name (CN) をlocalhost
に設定します。CNは、通常、証明書が発行されるサーバーのドメイン名やホスト名と一致させる必要があります。HTTPS通信では、クライアントは接続先のホスト名と証明書のCN (またはSubject Alternative Name: SAN) が一致するかを検証します。localhost
はローカル環境でのテストによく使われます。
-addext "subjectAltName = DNS:localhost"
:- 証明書に拡張機能 (Extension) を追加します。ここではSubject Alternative Name (SAN) を追加しています。
subjectAltName = DNS:localhost
: SANとしてDNS名localhost
を追加します。- 重要: 現代のブラウザやHTTPSクライアントは、
Common Name (CN)
よりもSubject Alternative Name (SAN)
を優先してホスト名の検証を行います。CNのみでホスト名を検証するケースは減少しており、SANが含まれていない証明書は警告が表示されたり、接続が拒否されたりする可能性があります。そのため、特に最近の環境で自己署名証明書を作成する際には、このsubjectAltName
を含めることが強く推奨されます。
Serverプログラム
package main
import (
"crypto/tls"
"fmt"
"log"
"net/http"
)
func main() {
// TLSv1.3のみを許可する設定
tlsConfig := &tls.Config{
MinVersion: tls.VersionTLS13,
MaxVersion: tls.VersionTLS13, // オプション:TLSv1.3のみに厳密に制限する場合
}
// ルーターの設定
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello from TLSv1.3 server!")
// 接続に使用されているTLSバージョンを確認
if r.TLS != nil {
log.Printf("Client connected with TLS version: %x", r.TLS.Version)
if r.TLS.Version == tls.VersionTLS13 {
log.Println("Confirmed TLSv1.3 connection.")
} else {
log.Println("Warning: Not TLSv1.3 connection. Version:", r.TLS.Version)
}
}
})
server := &http.Server{
Addr: ":8443",
TLSConfig: tlsConfig,
}
log.Println("Starting TLSv1.3 server on :8443")
// 証明書と秘密鍵を指定してHTTPSサーバーを起動
err := server.ListenAndServeTLS("server.crt", "server.key")
if err != nil {
log.Fatalf("Server failed: %v", err)
}
}
クライアント
package main
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io"
"log"
"net/http"
"os"
)
func main() {
// サーバーの証明書を読み込む
caCert, err := os.ReadFile("server.crt")
if err != nil {
log.Fatalf("Failed to read server.crt: %v", err)
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
// TLSv1.3のみを許可する設定
tlsConfig := &tls.Config{
MinVersion: tls.VersionTLS13,
MaxVersion: tls.VersionTLS13, // オプション:TLSv1.3のみに厳密に制限する場合
RootCAs: caCertPool,
}
// カスタムTLS設定を持つHTTPクライアントを作成
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: tlsConfig,
},
}
// サーバーにリクエストを送信
resp, err := client.Get("https://localhost:8443")
if err != nil {
log.Fatalf("Failed to make request: %v", err)
}
defer resp.Body.Close()
// レスポンスボディを読み込む
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatalf("Failed to read response body: %v", err)
}
fmt.Printf("Response from server: %s\n", body)
// 接続に使用されたTLSバージョンを確認
if resp.TLS != nil {
log.Printf("Client connected with TLS version: %x", resp.TLS.Version)
if resp.TLS.Version == tls.VersionTLS13 {
log.Println("Confirmed TLSv1.3 connection.")
} else {
log.Println("Warning: Not TLSv1.3 connection. Version:", resp.TLS.Version)
}
}
}
実行結果
サーバ側
go run .\server.go
2025/06/10 00:19:41 Starting TLSv1.3 server on :8443
2025/06/10 00:19:44 Client connected with TLS version: localhost
2025/06/10 00:19:44 Confirmed TLSv1.3 connection.
クライアント側
go run .\client.go
Response from server: Hello from TLSv1.3 server!
2025/06/10 00:25:36 Client connected with TLS version: 304
2025/06/10 00:25:36 Confirmed TLSv1.3 connection.
コメント