mirror of
https://github.com/zitadel/zitadel.git
synced 2025-07-13 10:18:32 +00:00

# Which Problems Are Solved ZITADEL uses the notification triggering requests Forwarded or X-Forwarded-Proto header to build the button link sent in emails for confirming a password reset with the emailed code. If this header is overwritten and a user clicks the link to a malicious site in the email, the secret code can be retrieved and used to reset the users password and take over his account. Accounts with MFA or Passwordless enabled can not be taken over by this attack. # How the Problems Are Solved - The `X-Forwarded-Proto` and `proto` of the Forwarded headers are validated (http / https). - Additionally, when exposing ZITADEL through https. An overwrite to http is no longer possible. # Additional Changes None # Additional Context None
208 lines
4.6 KiB
Go
208 lines
4.6 KiB
Go
package middleware
|
|
|
|
import (
|
|
"net/http"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
http_util "github.com/zitadel/zitadel/internal/api/http"
|
|
)
|
|
|
|
func Test_composeOrigin(t *testing.T) {
|
|
type args struct {
|
|
h http.Header
|
|
enforceHttps bool
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
want *http_util.DomainCtx
|
|
}{{
|
|
name: "no proxy headers",
|
|
want: &http_util.DomainCtx{
|
|
InstanceHost: "host.header",
|
|
Protocol: "http",
|
|
},
|
|
}, {
|
|
name: "forwarded proto",
|
|
args: args{
|
|
h: http.Header{
|
|
"Forwarded": []string{"proto=https"},
|
|
},
|
|
enforceHttps: false,
|
|
},
|
|
want: &http_util.DomainCtx{
|
|
InstanceHost: "host.header",
|
|
Protocol: "https",
|
|
},
|
|
}, {
|
|
name: "forwarded host",
|
|
args: args{
|
|
h: http.Header{
|
|
"Forwarded": []string{"host=forwarded.host"},
|
|
},
|
|
enforceHttps: false,
|
|
},
|
|
want: &http_util.DomainCtx{
|
|
InstanceHost: "forwarded.host",
|
|
Protocol: "http",
|
|
},
|
|
}, {
|
|
name: "forwarded proto and host",
|
|
args: args{
|
|
h: http.Header{
|
|
"Forwarded": []string{"proto=https;host=forwarded.host"},
|
|
},
|
|
enforceHttps: false,
|
|
},
|
|
want: &http_util.DomainCtx{
|
|
InstanceHost: "forwarded.host",
|
|
Protocol: "https",
|
|
},
|
|
}, {
|
|
name: "forwarded proto and host with multiple complete entries",
|
|
args: args{
|
|
h: http.Header{
|
|
"Forwarded": []string{"proto=https;host=forwarded.host, proto=http;host=forwarded.host2"},
|
|
},
|
|
enforceHttps: false,
|
|
},
|
|
want: &http_util.DomainCtx{
|
|
InstanceHost: "forwarded.host",
|
|
Protocol: "https",
|
|
},
|
|
}, {
|
|
name: "forwarded proto and host with multiple incomplete entries",
|
|
args: args{
|
|
h: http.Header{
|
|
"Forwarded": []string{"proto=https;host=forwarded.host, proto=http"},
|
|
},
|
|
enforceHttps: false,
|
|
},
|
|
want: &http_util.DomainCtx{
|
|
InstanceHost: "forwarded.host",
|
|
Protocol: "https",
|
|
},
|
|
}, {
|
|
name: "forwarded proto and host with incomplete entries in different values",
|
|
args: args{
|
|
h: http.Header{
|
|
"Forwarded": []string{"proto=http", "proto=https;host=forwarded.host", "proto=http"},
|
|
},
|
|
enforceHttps: true,
|
|
},
|
|
want: &http_util.DomainCtx{
|
|
InstanceHost: "forwarded.host",
|
|
Protocol: "https",
|
|
},
|
|
}, {
|
|
name: "x-forwarded-proto https",
|
|
args: args{
|
|
h: http.Header{
|
|
"X-Forwarded-Proto": []string{"https"},
|
|
},
|
|
enforceHttps: false,
|
|
},
|
|
want: &http_util.DomainCtx{
|
|
InstanceHost: "host.header",
|
|
Protocol: "https",
|
|
},
|
|
}, {
|
|
name: "x-forwarded-proto http",
|
|
args: args{
|
|
h: http.Header{
|
|
"X-Forwarded-Proto": []string{"http"},
|
|
},
|
|
enforceHttps: true,
|
|
},
|
|
want: &http_util.DomainCtx{
|
|
InstanceHost: "host.header",
|
|
Protocol: "https",
|
|
},
|
|
}, {
|
|
name: "fallback to http",
|
|
args: args{
|
|
enforceHttps: false,
|
|
},
|
|
want: &http_util.DomainCtx{
|
|
InstanceHost: "host.header",
|
|
Protocol: "http",
|
|
},
|
|
}, {
|
|
name: "enforce https",
|
|
args: args{
|
|
enforceHttps: true,
|
|
},
|
|
want: &http_util.DomainCtx{
|
|
InstanceHost: "host.header",
|
|
Protocol: "https",
|
|
},
|
|
}, {
|
|
name: "x-forwarded-host",
|
|
args: args{
|
|
h: http.Header{
|
|
"X-Forwarded-Host": []string{"x-forwarded.host"},
|
|
},
|
|
enforceHttps: false,
|
|
},
|
|
want: &http_util.DomainCtx{
|
|
InstanceHost: "x-forwarded.host",
|
|
Protocol: "http",
|
|
},
|
|
}, {
|
|
name: "x-forwarded-proto and x-forwarded-host",
|
|
args: args{
|
|
h: http.Header{
|
|
"X-Forwarded-Proto": []string{"https"},
|
|
"X-Forwarded-Host": []string{"x-forwarded.host"},
|
|
},
|
|
enforceHttps: false,
|
|
},
|
|
want: &http_util.DomainCtx{
|
|
InstanceHost: "x-forwarded.host",
|
|
Protocol: "https",
|
|
},
|
|
}, {
|
|
name: "forwarded host and x-forwarded-host",
|
|
args: args{
|
|
h: http.Header{
|
|
"Forwarded": []string{"host=forwarded.host"},
|
|
"X-Forwarded-Host": []string{"x-forwarded.host"},
|
|
},
|
|
enforceHttps: false,
|
|
},
|
|
want: &http_util.DomainCtx{
|
|
InstanceHost: "forwarded.host",
|
|
Protocol: "http",
|
|
},
|
|
}, {
|
|
name: "forwarded host and x-forwarded-proto",
|
|
args: args{
|
|
h: http.Header{
|
|
"Forwarded": []string{"host=forwarded.host"},
|
|
"X-Forwarded-Proto": []string{"https"},
|
|
},
|
|
enforceHttps: false,
|
|
},
|
|
want: &http_util.DomainCtx{
|
|
InstanceHost: "forwarded.host",
|
|
Protocol: "https",
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
assert.Equalf(t, tt.want, composeDomainContext(
|
|
&http.Request{
|
|
Host: "host.header",
|
|
Header: tt.args.h,
|
|
},
|
|
tt.args.enforceHttps,
|
|
[]string{http_util.Forwarded, http_util.ForwardedFor, http_util.ForwardedHost, http_util.ForwardedProto},
|
|
[]string{"x-zitadel-public-host"},
|
|
), "headers: %+v, enforceHttps: %t", tt.args.h, tt.args.enforceHttps)
|
|
})
|
|
}
|
|
}
|