Do not include secrets in the config hash

This commit is contained in:
binwiederhier
2026-07-23 08:13:27 +02:00
parent 7680cb4906
commit f2d5c1ce6c
3 changed files with 75 additions and 44 deletions
+41 -36
View File
@@ -4,14 +4,46 @@ and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/release
## Current stable releases ## Current stable releases
| Component | Version | Release date | | Component | Version | Release date |
|------------------|---------|--------------| |------------------|---------|---------------|
| ntfy server | v2.26.3 | Jul 20, 2026 | | ntfy server | v2.26.3 | Jul 20, 2026 |
| ntfy Android app | v1.24.0 | Mar 5, 2026 | | ntfy Android app | v1.25.2 | July 23, 2026 |
| ntfy iOS app | v1.7.0 | May 30, 2026 | | ntfy iOS app | v1.7.0 | May 30, 2026 |
Please check out the release notes for [upcoming releases](#not-released-yet) below. Please check out the release notes for [upcoming releases](#not-released-yet) below.
## ntfy Android v1.25.2
Released July 23, 2026
This release makes the "connection lost" alert configurable and turns it off by default. Folks did not like it and many reached out
or even gave ntfy bad reviews. I heard you! You can re-enable the alert in the advanced settings.
The release also tries to be smarter about not retrying the connection at all if the app is in flight mode, or has no network. If there
is no network, ntfy now keeps the foreground service alive and shows a "Waiting for network" notification, then resumes automatically
once connectivity returns.
Another change related to the networking is that we now force-reconnect when the connection is changed, e.g. during transitions
from Wi-Fi to cellular network, or vice versa. That should allow for faster transitions during hand-overs.
We also increase the client-side WebSocket ping interval from 1 minute to 3 minutes, which should slightly improve battery life,
especially when paired with increaseing the server-side `keepalive-interval` in your self-hosted server.
**Features:**
* Add configurable "Alert when connection is lost" setting, turned off by default ([#1665](https://github.com/binwiederhier/ntfy/issues/1665), [#1662](https://github.com/binwiederhier/ntfy/issues/1662), [#1652](https://github.com/binwiederhier/ntfy/issues/1652), [#1655](https://github.com/binwiederhier/ntfy/issues/1655), thanks to [@tintamarre](https://github.com/tintamarre), [@sjozs](https://github.com/sjozs), [@TheRealOne78](https://github.com/TheRealOne78), and [@DAE51D](https://github.com/DAE51D) for reporting)
* Handle "no network" gracefully: when the device is offline or in airplane mode, ntfy now stops retrying, suppresses the connection-lost alert, and keeps the foreground service alive with a "Waiting for network" notification, resuming instant delivery automatically when connectivity returns ([ntfy-android#165](https://github.com/binwiederhier/ntfy-android/pull/165), thanks to [@tintamarre](https://github.com/tintamarre) for the contribution, and [#1709](https://github.com/binwiederhier/ntfy/issues/1709), thanks to [@isaitgirl](https://github.com/isaitgirl) for reporting)
* Improve battery life by increasing WebSocket client ping interval from 1 min to 3 min, and reconnect instantly on Wi-Fi/cellular/VPN transitions ([ntfy-android#113](https://github.com/binwiederhier/ntfy-android/pull/113), thanks to [@ftilde](https://github.com/ftilde) for the investigation)
* Disable UnifiedPush components when UnifiedPush is disabled in settings ([ntfy-android#168](https://github.com/binwiederhier/ntfy-android/pull/168), thanks to [@p1gp1g](https://github.com/p1gp1g) for the contribution)
**Bug fixes + maintenance:**
* Fix the "connection lost" alert briefly disappearing and re-firing when roaming between networks (e.g. Wi-Fi to cellular), by no longer cancelling it during the transient no-network gap of a handover
* Fix the "connection lost" alert repeatedly waking the screen while a server stayed unreachable, by no longer re-posting the alert once it is already showing
* Fix the "connection lost" alert firing late, erratically, or not at all when a connection kept dropping (e.g. a flaky server) rather than being refused outright, by tracking how long the connection has been down independently of whether the drop warrants a UI error
* Undo automatic phone number linking for numbers in message body ([ntfy-android#170](https://github.com/binwiederhier/ntfy-android/pull/170), thanks to [@acortelyou](https://github.com/acortelyou) for the contribution)
* Fix subscription icons disappearing after a few days due to Android clearing cache ([#1322](https://github.com/binwiederhier/ntfy/issues/1322), thanks to [@mcanning](https://github.com/mcanning) for reporting)
* Fix UnifiedPush `failed_reason` being sent as an enum instead of a string, which caused an exception in receiving apps that read it as a string extra ([ntfy-android#182](https://github.com/binwiederhier/ntfy-android/pull/182), thanks to [@p1gp1g](https://github.com/p1gp1g) for the contribution)
### ntfy server v2.26.3 ### ntfy server v2.26.3
Released July 20, 2026 Released July 20, 2026
@@ -2021,6 +2053,10 @@ and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/release
### ntfy server v2.27.0 (UNRELEASED) ### ntfy server v2.27.0 (UNRELEASED)
**Security:**
* Exclude secrets (Stripe/Twilio/web push keys, SMTP password, provisioned users and tokens) from the config hash served to the web app
**Features:** **Features:**
* Allow logging in with your verified primary email address (in addition to your username), so a password reset no longer leaves you unable to sign in when you only remember the email you signed up with * Allow logging in with your verified primary email address (in addition to your username), so a password reset no longer leaves you unable to sign in when you only remember the email you signed up with
@@ -2030,37 +2066,6 @@ and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/release
* Fix Twilio phone calls and phone number verifications failing silently when Twilio rejected the request, and move the Twilio integration into its own `twilio` package * Fix Twilio phone calls and phone number verifications failing silently when Twilio rejected the request, and move the Twilio integration into its own `twilio` package
* Move the Prometheus metrics into a dedicated `metrics` package * Move the Prometheus metrics into a dedicated `metrics` package
### ntfy Android v1.25.2 (UNRELEASED)
This release makes the "connection lost" alert configurable and turns it off by default. Folks did not like it and many reached out
or even gave ntfy bad reviews. I heard you! You can re-enable the alert in the advanced settings.
The release also tries to be smarter about not retrying the connection at all if the app is in flight mode, or has no network. If there
is no network, ntfy now keeps the foreground service alive and shows a "Waiting for network" notification, then resumes automatically
once connectivity returns.
Another change related to the networking is that we now force-reconnect when the connection is changed, e.g. during transitions
from Wi-Fi to cellular network, or vice versa. That should allow for faster transitions during hand-overs.
We also increase the client-side WebSocket ping interval from 1 minute to 3 minutes, which should slightly improve battery life,
especially when paired with increaseing the server-side `keepalive-interval` in your self-hosted server.
**Features:**
* Add configurable "Alert when connection is lost" setting, turned off by default ([#1665](https://github.com/binwiederhier/ntfy/issues/1665), [#1662](https://github.com/binwiederhier/ntfy/issues/1662), [#1652](https://github.com/binwiederhier/ntfy/issues/1652), [#1655](https://github.com/binwiederhier/ntfy/issues/1655), thanks to [@tintamarre](https://github.com/tintamarre), [@sjozs](https://github.com/sjozs), [@TheRealOne78](https://github.com/TheRealOne78), and [@DAE51D](https://github.com/DAE51D) for reporting)
* Handle "no network" gracefully: when the device is offline or in airplane mode, ntfy now stops retrying, suppresses the connection-lost alert, and keeps the foreground service alive with a "Waiting for network" notification, resuming instant delivery automatically when connectivity returns ([ntfy-android#165](https://github.com/binwiederhier/ntfy-android/pull/165), thanks to [@tintamarre](https://github.com/tintamarre) for the contribution, and [#1709](https://github.com/binwiederhier/ntfy/issues/1709), thanks to [@isaitgirl](https://github.com/isaitgirl) for reporting)
* Improve battery life by increasing WebSocket client ping interval from 1 min to 3 min, and reconnect instantly on Wi-Fi/cellular/VPN transitions ([ntfy-android#113](https://github.com/binwiederhier/ntfy-android/pull/113), thanks to [@ftilde](https://github.com/ftilde) for the investigation)
* Disable UnifiedPush components when UnifiedPush is disabled in settings ([ntfy-android#168](https://github.com/binwiederhier/ntfy-android/pull/168), thanks to [@p1gp1g](https://github.com/p1gp1g) for the contribution)
**Bug fixes + maintenance:**
* Fix the "connection lost" alert briefly disappearing and re-firing when roaming between networks (e.g. Wi-Fi to cellular), by no longer cancelling it during the transient no-network gap of a handover
* Fix the "connection lost" alert repeatedly waking the screen while a server stayed unreachable, by no longer re-posting the alert once it is already showing
* Fix the "connection lost" alert firing late, erratically, or not at all when a connection kept dropping (e.g. a flaky server) rather than being refused outright, by tracking how long the connection has been down independently of whether the drop warrants a UI error
* Undo automatic phone number linking for numbers in message body ([ntfy-android#170](https://github.com/binwiederhier/ntfy-android/pull/170), thanks to [@acortelyou](https://github.com/acortelyou) for the contribution)
* Fix subscription icons disappearing after a few days due to Android clearing cache ([#1322](https://github.com/binwiederhier/ntfy/issues/1322), thanks to [@mcanning](https://github.com/mcanning) for reporting)
* Fix UnifiedPush `failed_reason` being sent as an enum instead of a string, which caused an exception in receiving apps that read it as a string extra ([ntfy-android#182](https://github.com/binwiederhier/ntfy-android/pull/182), thanks to [@p1gp1g](https://github.com/p1gp1g) for the contribution)
### ntfy iOS app v1.8.0 (UNRELEASED) ### ntfy iOS app v1.8.0 (UNRELEASED)
**Features:** **Features:**
+12 -8
View File
@@ -130,9 +130,9 @@ type Config struct {
AuthFile string AuthFile string
AuthStartupQueries string AuthStartupQueries string
AuthDefault user.Permission AuthDefault user.Permission
AuthUsers []*user.User AuthUsers []*user.User `hash:"-"`
AuthAccess map[string][]*user.Grant AuthAccess map[string][]*user.Grant
AuthTokens map[string][]*user.Token AuthTokens map[string][]*user.Token `hash:"-"`
AuthBcryptCost int AuthBcryptCost int
AuthStatsQueueWriterInterval time.Duration AuthStatsQueueWriterInterval time.Duration
AuthAccessCacheEnabled bool // Enables the in-memory ACL cache (high volume servers only) AuthAccessCacheEnabled bool // Enables the in-memory ACL cache (high volume servers only)
@@ -153,17 +153,17 @@ type Config struct {
FirebasePollInterval time.Duration FirebasePollInterval time.Duration
FirebaseQuotaExceededPenaltyDuration time.Duration FirebaseQuotaExceededPenaltyDuration time.Duration
UpstreamBaseURL string UpstreamBaseURL string
UpstreamAccessToken string UpstreamAccessToken string `hash:"-"`
SMTPSenderAddr string SMTPSenderAddr string
SMTPSenderUser string SMTPSenderUser string
SMTPSenderPass string SMTPSenderPass string `hash:"-"`
SMTPSenderFrom string SMTPSenderFrom string
SMTPSenderVerify bool SMTPSenderVerify bool
SMTPServerListen string SMTPServerListen string
SMTPServerDomain string SMTPServerDomain string
SMTPServerAddrPrefix string SMTPServerAddrPrefix string
TwilioAccount string TwilioAccount string
TwilioAuthToken string TwilioAuthToken string `hash:"-"`
TwilioPhoneNumber string TwilioPhoneNumber string
TwilioCallsBaseURL string TwilioCallsBaseURL string
TwilioVerifyBaseURL string TwilioVerifyBaseURL string
@@ -198,8 +198,8 @@ type Config struct {
BehindProxy bool // If true, the server will trust the proxy client IP header to determine the client IP address (IPv4 and IPv6 supported) BehindProxy bool // If true, the server will trust the proxy client IP header to determine the client IP address (IPv4 and IPv6 supported)
ProxyForwardedHeader string // The header field to read the real/client IP address from, if BehindProxy is true, defaults to "X-Forwarded-For" (IPv4 and IPv6 supported) ProxyForwardedHeader string // The header field to read the real/client IP address from, if BehindProxy is true, defaults to "X-Forwarded-For" (IPv4 and IPv6 supported)
ProxyTrustedPrefixes []netip.Prefix // List of trusted proxy networks (IPv4 or IPv6) that will be stripped from the Forwarded header if BehindProxy is true ProxyTrustedPrefixes []netip.Prefix // List of trusted proxy networks (IPv4 or IPv6) that will be stripped from the Forwarded header if BehindProxy is true
StripeSecretKey string StripeSecretKey string `hash:"-"`
StripeWebhookKey string StripeWebhookKey string `hash:"-"`
StripePriceCacheDuration time.Duration StripePriceCacheDuration time.Duration
BillingContact string BillingContact string
EnableSignup bool // Enable creation of accounts via API and UI EnableSignup bool // Enable creation of accounts via API and UI
@@ -208,7 +208,7 @@ type Config struct {
EnableReservations bool // Allow users with role "user" to own/reserve topics EnableReservations bool // Allow users with role "user" to own/reserve topics
EnableMetrics bool EnableMetrics bool
AccessControlAllowOrigin string // CORS header field to restrict access from web clients AccessControlAllowOrigin string // CORS header field to restrict access from web clients
WebPushPrivateKey string WebPushPrivateKey string `hash:"-"`
WebPushPublicKey string WebPushPublicKey string
WebPushFile string WebPushFile string
WebPushEmailAddress string WebPushEmailAddress string
@@ -342,6 +342,10 @@ func (c *Config) Hash() string {
for i := 0; i < v.NumField(); i++ { for i := 0; i < v.NumField(); i++ {
field := v.Field(i) field := v.Field(i)
fieldName := t.Field(i).Name fieldName := t.Field(i).Name
// Secrets must not feed the hash
if t.Field(i).Tag.Get("hash") == "-" {
continue
}
// Try to marshal the field and skip if it fails (e.g. *template.Template, netip.Prefix) // Try to marshal the field and skip if it fails (e.g. *template.Template, netip.Prefix)
if b, err := json.Marshal(field.Interface()); err == nil { if b, err := json.Marshal(field.Interface()); err == nil {
result += fmt.Sprintf("%s:%s|", fieldName, string(b)) result += fmt.Sprintf("%s:%s|", fieldName, string(b))
+22
View File
@@ -3,6 +3,7 @@ package server_test
import ( import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"heckel.io/ntfy/v2/server" "heckel.io/ntfy/v2/server"
"heckel.io/ntfy/v2/user"
"testing" "testing"
) )
@@ -11,3 +12,24 @@ func TestConfig_New(t *testing.T) {
assert.Equal(t, ":80", c.ListenHTTP) assert.Equal(t, ":80", c.ListenHTTP)
assert.Equal(t, server.DefaultKeepaliveInterval, c.KeepaliveInterval) assert.Equal(t, server.DefaultKeepaliveInterval, c.KeepaliveInterval)
} }
func TestConfig_HashExcludesSecrets(t *testing.T) {
// The config hash is served to browsers (ConfigHash, for webapp change detection), so
// secret material must not feed it: a weak secret would otherwise be offline-brute-forceable
// against a publicly visible hash.
conf1 := server.NewConfig()
conf2 := server.NewConfig()
conf2.StripeSecretKey = "sk_live_topsecret"
conf2.StripeWebhookKey = "whsec_topsecret"
conf2.TwilioAuthToken = "twilio-auth-token"
conf2.UpstreamAccessToken = "tk_upstream"
conf2.WebPushPrivateKey = "web-push-private-key"
conf2.SMTPSenderPass = "hunter2"
conf2.AuthUsers = []*user.User{{Name: "phil", Hash: "$2a$10$somebcrypthash"}}
conf2.AuthTokens = map[string][]*user.Token{"phil": {{Value: "tk_secrettoken"}}}
assert.Equal(t, conf1.Hash(), conf2.Hash())
// Non-secret fields must still change the hash
conf3 := server.NewConfig()
conf3.BaseURL = "https://ntfy.example.com"
assert.NotEqual(t, conf1.Hash(), conf3.Hash())
}