Caddy is the rising star of web servers. It is fast, easy to configure, fully featured (automatic TLS certificate generation and renewal, HTTP/3, cloud-native, config hot reloading…), and secure (it is written in Go, not in C).
Thanks to its unmatched extensibility that makes it a top-notch app platform, Caddy has a thriving ecosystem!
To see how it is powerful, take a look at my Mercure (push capabilities) and Vulcain (client-driven hypermedia APIs) modules! Caddy is also the webserver powering API Platform and Symfony Docker.
4 days ago, after 1 year of work, Matt Holt released Caddy 2.5! This new version contains some new features I contributed or helped to design, improving the security of the logging system.
Redacting Sensitive HTTP Headers
Unlike most other web servers, Caddy emits verbose structured logs. While it’s convenient to be able to inspect and analyze every single detail of the HTTP messages, this can also be dangerous. Until version 2.5, all HTTP headers were logged whenever logging was enabled. Even sensitive headers.
This can lead to serious security issues if the system processing and storing logs (ELK, Datadog…) isn’t configured properly. And they aren’t by default! Even companies such as Facebook, Twitter, and GitHub stored millions of user passwords in plain text because of similar misconfigurations.
In version 2.5, standard headers that may contain credentials, as defined in the fetch()
specification, are automatically redacted. These headers are Cookie
, Set-Cookie
, Authorization
and Proxy-Authorization
.
If you need to log these headers, for instance during a debugging session, Francis Lavoie added a new option to re-enable the old behavior: log_credentials
.
Introducing New Log Filters
But even this change isn’t enough to cover all cases where sensitive data need to be redacted from the logs. Although it’s considered bad security practice, it’s common for query parameters to contain sensitive information. Some examples:
- OAuth access tokens
- “magic links” and other temporary URLs
- PHP session IDs
- Mercure JWTs (the reason why I started working on this topic in the first time)
In many cases, you may want to log some query parameters but remove or replace others that contain sensitive data. E.g.: the access_token
query parameter defined in the OAuth 2 specifications.
Similarly, instead of deleting the Cookie
header entirely, which means removing all cookies, you may want to remove some specific cookies but keep others. To cover these more advanced but fairly common use cases, I added a bunch of new filters.
The first new filter allows to delete, replace, or hash specific query parameters:
log {
format filter {
wrap console
fields {
uri query {
replace access_token REDACTED
delete my_sensitive_param
hash my_parameter
}
}
}
}
I also contributed a similar filter to manipulate cookies:
log {
format filter {
wrap console
fields {
request>headers>Cookie cookie {
replace PHPSESSID REDACTED
delete my_sensitive_cookie
hash my_cookie
}
}
}
}
Finally, I added the ability to hash or apply a regular expression to the value of any logged field:
log {
format filter {
wrap console
fields {
request>headers>MyCustomHeader regexp secret REDACTED
request>headers>AnotherCustomHeader hash
}
}
}
The hash
filter is especially useful to prevent different queries from looking the same in the logs, or when you want to be able to group together queries that have the same redacted values.
If you need help setting up or even patching Caddy, if you want to hire a team of experimented Go developers, or if you need experts to secure your application: contact us! Les-Tilleuls.coop can help you!