pub struct OriginPolicy { /* private fields */ }Expand description
Which Origins this server answers.
Not a trait and not a port: the check is a pure function over two strings, and a port trait here would be a dynamic dispatch bought for nothing. The type exists so the default can be fail-secure and the comparison can be spelled once.
Implementations§
Source§impl OriginPolicy
impl OriginPolicy
Sourcepub const fn strict(own: Option<Origin>) -> Self
pub const fn strict(own: Option<Origin>) -> Self
A policy that answers only its own origin.
own is None when the server was not told its own origin — behind a
proxy that rewrites Host, or bound to 0.0.0.0 and reached by
several names. That is not a reason to answer everything: with no
known own-origin the policy refuses every Origin that is present,
which is the fail-secure direction. A server in that position has to
say what it serves, because nothing else can work it out.
Sourcepub fn allowing(self, origin: Origin) -> Self
pub fn allowing(self, origin: Origin) -> Self
Adds an origin this server answers for.
The list is exactly what the operator wrote: no wildcard, no suffix
match, no parent domain. A suffix rule (evil-example.com matching
not-evil-example.com) is the sort of thing that gets added once,
for a legitimate case, and never looked at again.
Sourcepub const fn own(&self) -> Option<&Origin>
pub const fn own(&self) -> Option<&Origin>
The origin this server considers its own, if it was told one.
Sourcepub fn permitted(&self) -> impl Iterator<Item = &Origin>
pub fn permitted(&self) -> impl Iterator<Item = &Origin>
The origins answered for, including this server’s own.
Sourcepub fn check(&self, header: Option<&str>) -> Result<(), Refusal>
pub fn check(&self, header: Option<&str>) -> Result<(), Refusal>
Decides whether a request carrying header is answered.
None means the header was absent, which is a non-browser client and
is allowed — see the module docs for why that asymmetry is the design
and not an oversight.
A loopback origin is answered whatever the allow-list says, and that is
not a hole in it. Origin is set by the browser and names the page the
request came from, so a page served from http://localhost:8080
genuinely sends http://localhost:8080 — and a rebound page at
evil.example sends http://evil.example, which is not loopback and is
refused here exactly as before. What a loopback allowlist would get
wrong is a suffix match, so the host is compared whole.
The comparison is exact and case-insensitive-in-scheme-and-host, done
once at parse. A substring or suffix match is the tempting
alternative and is the bug this type is shaped to prevent: an allowlist
of localhost that also answers localhost.evil.example is a
rebinding defence that rebinds.
Sourcepub fn check_host(&self, header: Option<&str>) -> Result<(), Refusal>
pub fn check_host(&self, header: Option<&str>) -> Result<(), Refusal>
Decides whether a request whose Host is header is answered.
Host is the header a rebinding attack actually controls, and checking
only Origin leaves the attack open to a client that omits it: a
browser rebound onto 127.0.0.1 sends Host: evil.example, and a
non-browser client can send whatever it likes. A browser cannot forge
Host — it is set from the URL it dialled — so a Host that is not
loopback and not one this server was told to serve names a request for
somewhere else.
Compared on the host rather than the whole authority, because the port is not part of the question: a client reaching this server on the port it was bound to names a host, and a loopback host on a different port is a different server that this policy has no opinion about.
None means the header was absent. HTTP/1.1 requires it and HTTP/2
carries it as :authority, so this is a proxy that stripped it rather
than a client that had none; it is refused, because a check that
answers “no evidence either way” cannot be the defence.