Crypto-View

caddyserver/caddy

Cryptographic posture

169 cryptographic locations: 10 already broken, 92 quantum-vulnerable, 3 reduced-margin, 41 undetermined, 23 quantum-safe

31 establish keys, so traffic protected by them and recorded today becomes readable once the algorithm falls. 2 imported cryptographic libraries are listed separately. 419 files analysed.

Quantum-vulnerable 92 Already broken 10 Reduced margin 3 Could not be determined 41 Quantum-safe 23
To address63
Key establishment31
Inventory only2
Total findings171
What was analysed
Branch master
Commit 128b9e75e0f30e853cb20c9a97ce06f9c9fe90a6 core: synchronize Stop with concurrent config reloads (#8038)
Committed 2026-09-19 16:02 UTC
Scanned 2026-09-21 11:00 UTC 9 hours ago
Coverage 419 files, 350 go, 1 javascript

List of cryptographic assets

MD5 Already broken 5 places See details

Broken hash function

An import of `crypto/md5` or `crypto/sha1`, or a call to `New()` on either.

This is already unsafe today, with no quantum computer involved. Practical chosen-prefix collisions exist; MD5 has no remaining security as a digest.

What to do. `crypto/sha256`, or `crypto/sha512` for long-lived signatures.

go.hash.weak · CWE-328
RSA-1024512-bit Already broken 2 places See details

X.509 certificate

A PEM certificate. The subject public-key algorithm and key size are read from the SubjectPublicKeyInfo structure, and the not-after date is reported so certificates that outlive the migration window are visible.

This is a digital identity document. Its expiry date matters: one valid for many years locks in today's algorithms for that long.

What to do. Shorten certificate lifetimes so re-issuing with a post-quantum key is routine.

  1. caddytest/leafcert.pem:1 -----BEGIN CERTIFICATE-----
  2. modules/caddytls/leafpemloader_test.go:15 test path -----BEGIN CERTIFICATE-----
pem.certificate
RSA-10241024-bit Already broken 2 places See details

X.509 certificate

A PEM certificate. The subject public-key algorithm and key size are read from the SubjectPublicKeyInfo structure, and the not-after date is reported so certificates that outlive the migration window are visible.

This is a digital identity document. Its expiry date matters: one valid for many years locks in today's algorithms for that long.

What to do. Shorten certificate lifetimes so re-issuing with a post-quantum key is routine.

  1. modules/caddyhttp/celmatcher_test.go:30 test path -----BEGIN CERTIFICATE-----
  2. modules/caddyhttp/replacer_test.go:40 test path -----BEGIN CERTIFICATE-----
pem.certificate
TLS Quantum-vulnerable Recorded traffic 16 places See details

TLS configuration in code

A `tls.Config` field pinning versions, cipher suites or curve preferences. Every available suite negotiates a classical key exchange.

A quantum computer of sufficient size breaks this completely. It has to be replaced, not tuned. Every TLS cipher suite in general use negotiates a classical key exchange, so a recorded session is decryptable once that exchange falls.

What to do. Set `MinVersion: tls.VersionTLS13`. Go 1.24 enables the hybrid X25519MLKEM768 group by default, which is the single highest-value change available today.

  1. caddyconfig/httploader.go:186 tlsConfig = &tls.Config{Certificates: certs}
  2. caddyconfig/httploader.go:193 tlsConfig = &tls.Config{Certificates: []tls.Certificate{cert}}
  3. caddytest/caddytest.go:360 TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //nolint:gosec
  4. caddytest/integration/proxyprotocol_test.go:212 test path func (b *tlsProxyProtoBackend) tlsConfig() *tls.Config {
  5. listeners.go:465 quicTlsConfig := &tls.Config{
  6. listeners_test.go:184 test path initialConfig := &tls.Config{
  7. listeners_test.go:203 test path updatedConfig := &tls.Config{
  8. modules/caddyevents/handshake_bench_test.go:81 test path CipherSuites: []uint16{tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
  9. modules/caddytls/connpolicy.go:98 func (cp ConnectionPolicies) TLSConfig(ctx caddy.Context) *tls.Config {
  10. modules/caddytls/connpolicy.go:142 tlsCfg := &tls.Config{
  11. modules/caddytls/connpolicy.go:280 cfg := &tls.Config{
  12. modules/caddytls/connpolicy_test.go:33 test path targetTLSConfig := &tls.Config{ClientAuth: tls.RequireAnyClientCert}
  13. modules/caddytls/connpolicy_test.go:45 test path TLSConfig: &tls.Config{},
  14. modules/caddytls/connpolicy_test.go:50 test path TLSConfig: &tls.Config{ClientAuth: tls.NoClientCert},
  15. modules/caddytls/ech.go:674 CipherSuites: []hpkeSymmetricCipherSuite{
  16. modules/caddytls/storage_clean_test.go:356 test path CipherSuites: []hpkeSymmetricCipherSuite{
go.tls.config · CWE-757
ECDH Quantum-vulnerable Recorded traffic 13 places See details

TLS cipher suite named in source

A cipher suite written into the code rather than into a configuration file - `TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256`, or the OpenSSL spelling `ECDHE-RSA-AES128-GCM-SHA256`. The key exchange is reported, and the bulk cipher separately when it is one of the broken ones.

This code names the exact cryptography its TLS connections may use. Because the list is in the program rather than in a settings file, changing it needs a new release - which is the thing that makes a migration slow.

What to do. A hardcoded suite list ships with the binary and cannot be changed without a release, so move it to configuration first. The key exchange changes when the TLS library offers a hybrid group, not before.

  1. modules/caddyevents/handshake_bench_test.go:81 test path CipherSuites: []uint16{tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
  2. modules/caddytls/values.go:53 tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
  3. modules/caddytls/values.go:54 tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
  4. modules/caddytls/values.go:55 tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
  5. modules/caddytls/values.go:56 tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
  6. modules/caddytls/values.go:57 tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
  7. modules/caddytls/values.go:58 tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
  8. modules/caddytls/values.go:65 tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
  9. modules/caddytls/values.go:66 tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
  10. modules/caddytls/values.go:67 tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
  11. modules/caddytls/values.go:68 tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
  12. modules/caddytls/values.go:69 tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
  13. modules/caddytls/values.go:70 tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
config.cipher-suite · CWE-757
ECDSA256-bitsecg/secp256r1 Quantum-vulnerable 9 places See details

ECDSA in the Go standard library

`ecdsa.GenerateKey()` or `ecdsa.Sign*()`. The curve is read from the `elliptic.P###()` argument.

A quantum computer of sufficient size breaks this completely. It has to be replaced, not tuned. Broken by Shor's algorithm on a cryptographically relevant quantum computer.

What to do. ML-DSA-65 (FIPS 204).

  1. caddytest/integration/acme_test.go:61 test path accountPrivateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
  2. caddytest/integration/acme_test.go:77 test path certPrivateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
  3. caddytest/integration/acme_test.go:130 test path accountPrivateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
  4. caddytest/integration/acme_test.go:146 test path certPrivateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
  5. caddytest/integration/acmeserver_test.go:89 test path accountPrivateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
  6. caddytest/integration/acmeserver_test.go:105 test path certPrivateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
  7. caddytest/integration/acmeserver_test.go:178 test path accountPrivateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
  8. caddytest/integration/acmeserver_test.go:194 test path certPrivateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
  9. modules/caddyevents/handshake_bench_test.go:35 test path key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
go.ecdsa · CWE-327
RSA2048-bit Quantum-vulnerable 3 places See details

X.509 certificate

A PEM certificate. The subject public-key algorithm and key size are read from the SubjectPublicKeyInfo structure, and the not-after date is reported so certificates that outlive the migration window are visible.

This is a digital identity document. Its expiry date matters: one valid for many years locks in today's algorithms for that long.

What to do. Shorten certificate lifetimes so re-issuing with a post-quantum key is routine.

  1. caddytest/a.caddy.localhost.crt:1 -----BEGIN CERTIFICATE-----
  2. caddytest/caddy.ca.cer:1 -----BEGIN CERTIFICATE-----
  3. caddytest/caddy.localhost.crt:1 -----BEGIN CERTIFICATE-----
pem.certificate
RSA Quantum-vulnerable Recorded traffic 2 places See details

Private key committed to the repository

A PEM private-key block. The header names the algorithm where the format is the legacy one; for PKCS#8 the algorithm OID is read out of the DER. A private key in version control is a present-day incident before it is a quantum question.

A secret key is stored in this repository's files. Anyone who can read the repository can use it. This needs attention today, regardless of quantum computers.

What to do. Revoke and rotate the key, then keep key material out of the repository. Record the algorithm in the inventory.

  1. caddytest/a.caddy.localhost.key:1 -----BEGIN RSA PRIVATE KEY-----
  2. caddytest/caddy.localhost.key:1 -----BEGIN RSA PRIVATE KEY-----
pem.private-key · CWE-321, CWE-327
unknown Could not be determined 9 places See details

X.509 certificate handling

`x509.CreateCertificate()` or a PKCS#1/PKCS#8/EC key parser. Certificates are the longest-lived cryptographic artefacts most systems own, and their validity periods often outrun the migration.

The algorithm could not be established from the source. Usually it is selected while the program runs, but it may equally be that the call gives no clue, or that it is decided somewhere this scan does not reach. Somebody has to check.

What to do. Shorten certificate lifetimes now so the eventual re-issue with a post-quantum key is routine rather than an incident.

  1. modules/caddyevents/handshake_bench_test.go:46 test path der, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &key.PublicKey, key)
  2. modules/caddypki/crypto_test.go:42 test path rootBytes, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, rootSigner.Public(), rootSigner)
  3. modules/caddypki/crypto_test.go:57 test path intermediateBytes, err := x509.CreateCertificate(rand.Reader, &x509.Certificate{
  4. modules/caddypki/crypto_test.go:183 test path derBytes, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, signer.Public(), signer)
  5. modules/caddypki/crypto_test.go:255 test path derBytes, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, signer.Public(), signer)
  6. modules/caddytls/internalissuer_test.go:49 test path rootBytes, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, rootSigner.Public(), rootSigner)
  7. modules/caddytls/internalissuer_test.go:64 test path firstIntermediateBytes, err := x509.CreateCertificate(rand.Reader, &x509.Certificate{
  8. modules/caddytls/internalissuer_test.go:85 test path secondIntermediateBytes, err := x509.CreateCertificate(rand.Reader, &x509.Certificate{
  9. modules/caddytls/internalissuer_test.go:139 test path csrBytes, err := x509.CreateCertificateRequest(rand.Reader, &x509.CertificateRequest{
go.x509
unknown Could not be determined 1 place See details

X.509 certificate

A PEM certificate. The subject public-key algorithm and key size are read from the SubjectPublicKeyInfo structure, and the not-after date is reported so certificates that outlive the migration window are visible.

This is a digital identity document. Its expiry date matters: one valid for many years locks in today's algorithms for that long.

What to do. Shorten certificate lifetimes so re-issuing with a post-quantum key is routine.

  1. admin_test.go:1069 test path -----BEGIN CERTIFICATE-----
pem.certificate
unknown Could not be determined 1 place See details

Private key committed to the repository

A PEM private-key block. The header names the algorithm where the format is the legacy one; for PKCS#8 the algorithm OID is read out of the DER. A private key in version control is a present-day incident before it is a quantum question.

A secret key is stored in this repository's files. Anyone who can read the repository can use it. This needs attention today, regardless of quantum computers.

What to do. Revoke and rotate the key, then keep key material out of the repository. Record the algorithm in the inventory.

  1. admin_test.go:1089 test path -----BEGIN PRIVATE KEY-----
pem.private-key · CWE-321, CWE-327
unknown Could not be determined 1 place See details

Cryptographic library in the dependency manifest

A dependency known to implement classical asymmetric cryptography. Its presence is inventory, not a finding about this codebase: the library may never be called with a quantum-vulnerable algorithm.

The project depends on a cryptography library. Listed so the inventory is complete; it is not by itself a problem.

What to do. Check the version. Several of these libraries have shipped ML-KEM and ML-DSA support since 2024, so the migration may need an upgrade rather than a replacement.

  1. go.mod:45 golang.org/x/crypto v0.55.0
dep.crypto-library
CSPRNG Quantum-safe 5 places See details

Random number generation

`crypto/rand`. Recorded so the inventory names its randomness source. `math/rand` reaching key material would be a present-day defect, and is a different thing entirely.

This is where the software gets its random numbers. Quantum computers do not weaken it.

What to do. No action.

  1. caddytest/integration/stream_test.go:417 test path _, err := rand.Read(b)
  2. cmd/commandfuncs.go:108 _, err = rand.Read(expect)
  3. modules/caddyhttp/caddyauth/argon2id.go:184 if _, err := rand.Read(salt); err != nil {
  4. modules/caddyhttp/reverseproxy/fastcgi/client_test.go:183 test path p = filepath.Join(os.TempDir(), "fcgict"+strconv.Itoa(rand.Int()))
  5. modules/caddyhttp/reverseproxy/reverseproxy.go:509 _, randErr := rand.Read(key)
go.rng
Argon2 Quantum-safe 1 place See details

Password hashing or key derivation

`golang.org/x/crypto/bcrypt`, `scrypt`, `argon2` or `pbkdf2`. Not a quantum exposure, and part of a complete inventory.

This is how passwords are stored. Quantum computers do not meaningfully weaken it.

What to do. Argon2id for new work.

  1. modules/caddyhttp/caddyauth/argon2id.go:67 computedKey := argon2.IDKey(
go.kdf
HMAC Quantum-safe 1 place See details

Keyed hash in use

`hmac.New()`. Not broken by Shor and only marginally affected by Grover.

This is a fingerprint proving a message was not altered. Quantum computers do not break it.

What to do. No action, unless the digest is MD5 or SHA-1.

  1. modules/caddyhttp/reverseproxy/selectionpolicies.go:726 if err == nil && hmac.Equal([]byte(sha), []byte(cookieValue)) {
go.hmac
ML-KEM-768 Quantum-safe 1 place See details

Cryptographic library in the dependency manifest

A dependency known to implement classical asymmetric cryptography. Its presence is inventory, not a finding about this codebase: the library may never be called with a quantum-vulnerable algorithm.

The project depends on a cryptography library. Listed so the inventory is complete; it is not by itself a problem.

What to do. Check the version. Several of these libraries have shipped ML-KEM and ML-DSA support since 2024, so the migration may need an upgrade rather than a replacement.

  1. go.mod:15 github.com/cloudflare/circl v1.6.4
dep.crypto-library
bcrypt Quantum-safe 1 place See details

Password hashing or key derivation

`golang.org/x/crypto/bcrypt`, `scrypt`, `argon2` or `pbkdf2`. Not a quantum exposure, and part of a complete inventory.

This is how passwords are stored. Quantum computers do not meaningfully weaken it.

What to do. Argon2id for new work.

  1. modules/caddyhttp/caddyauth/bcrypt.go:52 err := bcrypt.CompareHashAndPassword(hashed, plaintext)
go.kdf

Cryptographic assets

Algorithm Assessment What it means Occurrences
TLS Quantum-vulnerable Every TLS cipher suite in general use negotiates a classical key exchange, so a recorded session is decryptable once that exchange falls. 53
unknown Could not be determined The algorithm could not be established from the source - chosen at runtime, or decided somewhere this scan does not reach. 41
ECDSA Quantum-vulnerable Broken by Shor's algorithm on a cryptographically relevant quantum computer. 17
CSPRNG Quantum-safe A cryptographically secure random number generator provided by the platform. Not weakened by a quantum computer. 15
ECDH Quantum-vulnerable Broken by Shor's algorithm on a cryptographically relevant quantum computer. 13
RSA Quantum-vulnerable Broken by Shor's algorithm on a cryptographically relevant quantum computer. 7
MD5 Already broken Practical chosen-prefix collisions exist; MD5 has no remaining security as a digest. 6
RSA-1024 Already broken A modulus of 1024 bits or less is below the NIST SP 800-57 floor and is within reach of classical factorisation. Shor is not the nearest problem here. 4
SHA-256 Reduced margin Pre-image resistance falls to about 128 bits of quantum work. Adequate for most uses; SHA-384 restores the full margin where a signature must last decades. 3
HMAC Quantum-safe A keyed MAC is not affected by Shor and only marginally by Grover. 2
Argon2 Quantum-safe The current recommended password hashing function. Not affected by Shor, and memory-hard against Grover. 2
bcrypt Quantum-safe A password hashing function. Grover offers only a marginal speed-up against a deliberately slow function, so quantum computing is not the concern here. 2
Ed25519 Quantum-vulnerable Broken by Shor's algorithm on a cryptographically relevant quantum computer. 2
ML-KEM-768 Quantum-safe No known quantum algorithm changes the security margin. 2

Imported cryptographic libraries

Library Files
crypto/md5 the MD5 digest, which has practical collisions 1
crypto/tls the TLS client and server 37
crypto/ecdsa elliptic-curve signatures 5
crypto/ed25519 Edwards-curve signatures 2
crypto/rsa RSA key generation, signing and OAEP encryption 2
crypto/sha256 the SHA-2 digests 3
crypto/x509 certificate and key parsing 30
crypto/rand the platform random source 10
github.com/cloudflare/circl CIRCL, including post-quantum algorithms 2
golang.org/x/crypto/argon2 password hashing 1
crypto/hmac keyed message authentication 1
golang.org/x/crypto/bcrypt password hashing 1