Documentation ¶
Overview ¶
Package fcors provides net/http middleware for Cross-Origin Resource Sharing (CORS).
To create a CORS middleware that only allows anonymous access, use the AllowAccess function. To create a CORS middleware that allows both anonymous access and credentialed access (e.g. with cookies), use the AllowAccessWithCredentials function.
To avoid negative interference from reverse proxies, other middleware in the chain, or from the handler at the end of the chain, follow the rules listed below. The key words "MUST", "MUST NOT", "SHOULD", "SHOULD NOT", and "MAY" used below are to be interpreted as described in RFC 2119.
- Because CORS-preflight requests use OPTIONS as their method, you SHOULD NOT prevent OPTIONS requests from reaching your CORS middleware. Otherwise, preflight requests will not get properly handled and browser-based clients will likely experience CORS-related errors. The examples provided by this package contain further guidance for avoiding such pitfalls.
- Because CORS-preflight requests are not authenticated, authentication SHOULD NOT take place "ahead of" a CORS middleware (e.g. in a reverse proxy or an earlier middleware). However, a CORS middleware MAY wrap an authentication middleware.
- Multiple CORS middleware MUST NOT be stacked.
- Other middleware (if any) in the chain MUST NOT alter any CORS response headers that are set by this library's middleware and MUST NOT add more CORS response headers.
- Other middleware (if any) in the chain SHOULD NOT alter any Vary header that is set by this library's middleware, but they MAY add more Vary headers.
This package provides basic options for configuring a CORS middleware, but more advanced (and potentially dangerous) options can be found in the github.com/jub0bs/fcors/risky package.
Index ¶
- type Middleware
- type Option
- func ExposeResponseHeaders(one string, others ...string) Option
- func FromOrigins(one string, others ...string) Option
- func MaxAgeInSeconds(delta uint) Option
- func PreflightSuccessStatus(code uint) Option
- func WithAnyMethod() Option
- func WithAnyRequestHeaders() Option
- func WithMethods(one string, others ...string) Option
- func WithRequestHeaders(one string, others ...string) Option
- type OptionAnon
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Middleware ¶
Middleware is a convenience alias for the type of a function that takes a http.Handler and returns a http.Handler.
The middleware provided by this package are, of course, safe for concurrent use by multiple goroutines.
func AllowAccess ¶
func AllowAccess(one OptionAnon, others ...OptionAnon) (Middleware, error)
AllowAccess creates a CORS middleware that only allows anonymous access, according to the specified options. The behavior of the resulting middleware is insensitive to the order in which the options that configure it are specified.
AllowAccess requires a single call to option FromOrigins or option FromAnyOrigin as one of its arguments.
Using a given option more than once in a call to AllowAccess results in a failure to build the corresponding middleware.
If the specified options are invalid or mutually incompatible, AllowAccess returns a nil Middleware and some non-nil error. Otherwise, it returns a functioning Middleware and a nil error.
Any occurrence of a nil option results in a panic.
Example ¶
package main import ( "io" "log" "net/http" "github.com/jub0bs/fcors" ) func main() { mux := http.NewServeMux() mux.HandleFunc("GET /hello", handleHello) // note: not configured for CORS // create CORS middleware cors, err := fcors.AllowAccess( fcors.FromOrigins("https://example.com"), fcors.WithMethods(http.MethodGet, http.MethodPost), fcors.WithRequestHeaders("Authorization"), ) if err != nil { log.Fatal(err) } api := http.NewServeMux() api.HandleFunc("GET /users", handleUsersGet) api.HandleFunc("POST /users", handleUsersPost) mux.Handle("/api/", http.StripPrefix("/api", cors(api))) // note: method-less pattern here log.Fatal(http.ListenAndServe(":8080", mux)) } func handleHello(w http.ResponseWriter, _ *http.Request) { io.WriteString(w, "Hello, World!") } func handleUsersGet(w http.ResponseWriter, _ *http.Request) { // omitted } func handleUsersPost(w http.ResponseWriter, _ *http.Request) { // omitted }
Output:
Example (Incorrect) ¶
The example below illustrates a common pitfall.
A good rule of thumb for avoiding this pitfall consists in registering the result of a Middleware, not for a method-full pattern (e.g. "GET /api/dogs"), but for a "method-less" pattern; see the other example.
package main import ( "log" "net/http" "github.com/jub0bs/fcors" ) // The example below illustrates a common pitfall. // // A good rule of thumb for avoiding this pitfall consists in // registering the result of a Middleware, // not for a method-full pattern (e.g. "GET /api/dogs"), // but for a "method-less" pattern; see the other example. func main() { cors, err := fcors.AllowAccess( fcors.FromOrigins("https://example"), ) if err != nil { log.Fatal(err) } mux := http.NewServeMux() // Because the pattern for which the result of Middleware is registered // unduly specifies a method (other than OPTIONS), // CORS-preflight requests to /api/dogs cannot reach the CORS middleware. // Therefore, CORS preflight will systematically fail // and you'll have a bad day... mux.Handle("GET /api/dogs", cors(http.HandlerFunc(handleDogsGet))) // incorrect! log.Fatal(http.ListenAndServe(":8080", mux)) } func handleDogsGet(w http.ResponseWriter, _ *http.Request) { // omitted }
Output:
func AllowAccessWithCredentials ¶
func AllowAccessWithCredentials(one Option, others ...Option) (Middleware, error)
AllowAccessWithCredentials creates a CORS middleware that allows both anonymous access and credentialed access (e.g. with cookies), according to the specified options. The behavior of the resulting middleware is insensitive to the order in which the options that configure it are specified.
AllowAccessWithCredentials requires a single call to option FromOrigins as one of its arguments.
Using a given option more than once in a call to AllowAccessWithCredentials results in a failure to build the corresponding middleware.
If the specified options are invalid or mutually incompatible, AllowAccessWithCredentials returns a nil Middleware and some non-nil error. Otherwise, it returns a functioning Middleware and a nil error.
Any occurrence of a nil option results in a panic.
type Option ¶
An Option configures a CORS middleware that allows both anonymous access and credentialed access (e.g. with cookies).
You're not meant to implement this interface.
func ExposeResponseHeaders ¶
ExposeResponseHeaders configures a CORS middleware to expose the specified response headers to clients.
Using this option in conjunction with option ExposeAllResponseHeaders in a call to AllowAccess results in a failure to build the corresponding middleware.
Any occurrence of an invalid header name results in a failure to build the corresponding middleware.
Header names are case-insensitive.
The CORS protocol defines a number of so-called "CORS-safelisted response-header names", which need not be explicitly specified as exposed. The CORS protocol also defines a number of so-called "forbidden response-header names", which cannot be exposed to clients. Accordingly, specifying one or more safelisted or forbidden response-header name(s) results in a failure to build the desired middleware.
Finally, some header names that have no place in a response are prohibited:
- Access-Control-Request-Headers
- Access-Control-Request-Method
- Access-Control-Request-Private-Network
- Origin
Although a valid response-header name, a literal * is also prohibited; to expose all response headers, use option ExposeAllResponseHeaders instead of this one.
func FromOrigins ¶
FromOrigins configures a CORS middleware to allow access from any of the Web origins encompassed by the specified origin patterns.
Using this option in conjunction with option FromAnyOrigin in a call to AllowAccess results in a failure to build the corresponding middleware. Any occurrence of a prohibited pattern results in a failure to build the corresponding middleware.
Permitted schemes are limited to http (with a caveat explained further down) and https:
http://example.com // permitted https://example.com // permitted chrome-extension://example.com // prohibited
Origins must be specified in ASCII serialized form; Unicode is prohibited:
https://example.com // permitted https://www.xn--xample-9ua.com // permitted (Punycode) https://www.éxample.com // prohibited (Unicode)
For security reasons, the null origin is prohibited.
Hosts that are IPv4 addresses must be specified in dotted-quad notation:
http://255.0.0.0 // permitted http://0xFF000000 // prohibited
Hosts that are IPv6 addresses must be specified in their compressed form:
http://[::1]:9090 // permitted http://[0:0:0:0:0:0:0:0001]:9090 // prohibited http://[0000:0000:0000:0000:0000:0000:0000:0001]:9090 // prohibited
Valid port values range from 1 to 65,535 (inclusive):
https://example.com // permitted (no port) https://example.com:1 // permitted https://example.com:65535 // permitted https://example.com:0 // prohibited https://example.com:65536 // prohibited
Default ports (80 for http, 443 for https) must be elided:
http://example.com // permitted https://example.com // permitted http://example.com:80 // prohibited https://example.com:443 // prohibited
In addition to support for exact origins, this option provides limited support for origin patterns that encompass multiple origins. A leading asterisk (followed by a full stop) in a host pattern denotes exactly one arbitrary DNS label or several period-separated arbitrary DNS labels. For instance, the pattern
https://*.example.com
encompasses the following origins (among others):
https://foo.example.com https://bar.example.com https://bar.foo.example.com https://baz.bar.foo.example.com
An asterisk in place of a port denotes an arbitrary (possibly implicit) port. For instance,
http://localhost:*
encompasses the following origins (among others),
http://localhost http://localhost:80 http://localhost:9090
Specifying both arbitrary subdomains and arbitrary ports in a given origin pattern is prohibited:
https://*.example.com // permitted https://*.example.com:9090 // permitted https://example.com:* // permitted https://*.example.com:* // prohibited
No other types of origin patterns are supported. In particular, an origin pattern consisting of a single asterisk is prohibited. If you want to allow (anonymous) access from all origins, use option FromAnyOrigin instead of this one.
Origin patterns whose scheme is http and whose host is neither localhost nor a loopback IP address are deemed insecure; as such, for security reasons, they are by default prohibited when credentialed access and/or some form of Private-Network Access is enabled. If, even in such cases, you wish to deliberately tolerate insecure origins anyway, you must also activate option github.com/jub0bs/fcors/risky.DangerouslyTolerateInsecureOrigins. Otherwise, any occurence of an insecure origin without activating option github.com/jub0bs/fcors/risky.DangerouslyTolerateInsecureOrigins results in a failure to build the corresponding middleware.
Also for security reasons, allowing arbitrary subdomains of a base domain that happens to be a public suffix is by default prohibited:
https://*.example.com // permitted: example.com is not a public suffix https://*.com // prohibited (by default): com is a public suffix https://*.github.io // prohibited (by default): github.io is a public suffix
If you need to deliberately allow arbitrary subdomains of a public suffix (danger!), you must also activate option github.com/jub0bs/fcors/risky.DangerouslyTolerateSubdomainsOfPublicSuffixes. Any occurrence of such a prohibited origin pattern without activating option github.com/jub0bs/fcors/risky.DangerouslyTolerateSubdomainsOfPublicSuffixes results in a failure to build the corresponding middleware.
func MaxAgeInSeconds ¶
MaxAgeInSeconds configures a CORS middleware to instruct browsers to cache preflight responses for a maximum duration of delta seconds.
Specifying a max-age value of 0 instructs browsers to eschew caching of preflight responses altogether, whereas omitting to specify a max age causes browsers to cache preflight responses with a default max-age value of 5 seconds.
Because all modern browsers cap the max-age value (the larger upper bound currently is Firefox's: 86,400 seconds), this option accordingly imposes an upper bound on its argument: attempts to specify a max-age value larger than 86400 result in a failure to build the corresponding middleware.
func PreflightSuccessStatus ¶
PreflightSuccessStatus configures a CORS middleware to use the specified status code in successful preflight responses.
When this option is not used, the status used in successful preflight responses is 204 No Content.
Specifying a custom status code outside the 2xx range results in a failure to build the corresponding middleware.
func WithAnyMethod ¶
func WithAnyMethod() Option
WithAnyMethod configures a CORS middleware to allow any HTTP method.
Using this option in conjunction with option WithMethods in a call to AllowAccess or AllowAccessWithCredentials results in a failure to build the corresponding middleware.
func WithAnyRequestHeaders ¶
func WithAnyRequestHeaders() Option
WithAnyRequestHeaders configures a CORS middleware to allow any request headers.
Using this option in conjunction with option WithRequestHeaders in a call to AllowAccess or AllowAccessWithCredentials results in a failure to build the corresponding middleware.
func WithMethods ¶
WithMethods configures a CORS middleware to allow any of the specified HTTP methods.
Using this option in conjunction with option WithAnyMethod in a call to AllowAccess or AllowAccessWithCredentials results in a failure to build the corresponding middleware.
Method names are case-sensitive.
The three so-called "CORS-safelisted methods" (GET, HEAD, and POST) are by default allowed by the CORS protocol. As such, allowing them explicitly in your CORS configuration is harmless but never actually necessary.
Moreover, the CORS protocol forbids the use of some method names. Accordingly, any occurrence of an invalid or forbidden method name results in a failure to build the corresponding middleware.
Although a valid method name, a literal * is also prohibited; to allow all methods, use option WithAnyMethod instead of this one.
Note that, contrary to popular belief, listing OPTIONS as an allowed method in your CORS configuration is only required if you wish to allow clients to make explicit use of that method, e.g. via the following client code:
fetch('https://example.com', {method: 'OPTIONS'})
In the great majority of cases, listing OPTIONS as an allowed method in your CORS configuration is unnecessary.
func WithRequestHeaders ¶
WithRequestHeaders configures a CORS middleware to allow any of the specified request headers.
Using this option in conjunction with option WithAnyRequestHeaders in a call to AllowAccess or AllowAccessWithCredentials results in a failure to build the corresponding middleware.
Any occurrence of an invalid header name results in a failure to build the corresponding middleware.
Header names are case-insensitive.
The CORS protocol defines a number of so-called "forbidden request-header names", which are never allowed and that browsers silently drop from client requests. Specifying one or more forbidden request-header name(s) results in a failure to build the corresponding middleware.
Finally, some header names that have no place in a request are prohibited:
- Access-Control-Allow-Credentials
- Access-Control-Allow-Headers
- Access-Control-Allow-Methods
- Access-Control-Allow-Origin
- Access-Control-Allow-Private-Network
- Access-Control-Expose-Headers
- Access-Control-Max-Age
Although a valid request-header name, a literal * is also prohibited; to allow all request headers, use option WithAnyRequestHeaders instead of this one.
type OptionAnon ¶
type OptionAnon = internal.OptionAnon
An OptionAnon configures a CORS middleware that only allows anonymous access.
You're not meant to implement this interface.
func ExposeAllResponseHeaders ¶
func ExposeAllResponseHeaders() OptionAnon
ExposeAllResponseHeaders configures a CORS middleware to expose all response headers to clients.
Using this option in conjunction with option ExposeResponseHeaders in a call to AllowAccess results in a failure to build the corresponding middleware.
func FromAnyOrigin ¶
func FromAnyOrigin() OptionAnon
FromAnyOrigin configures a CORS middleware to allow any Web origin.
Using this option in conjunction with option FromOrigins in a call to AllowAccess results in a failure to build the corresponding middleware.
Directories ¶
Path | Synopsis |
---|---|
Package internal hides all implementation details of packages github.com/jub0bs/fcors and github.com/jub0bs/fcors/risky.
|
Package internal hides all implementation details of packages github.com/jub0bs/fcors and github.com/jub0bs/fcors/risky. |
origin
Package origin implements parsing of origins and origin patterns and provides data structures useful for representing a set of origins.
|
Package origin implements parsing of origins and origin patterns and provides data structures useful for representing a set of origins. |
radix
Package radix provides an implementation of a specialized radix tree.
|
Package radix provides an implementation of a specialized radix tree. |
util
Package util provides a generic type representing a mathematical set, and convenience functions for generating error values for packages github.com/jub0bs/fcors and github.com/jub0bs/fcors/risky.
|
Package util provides a generic type representing a mathematical set, and convenience functions for generating error values for packages github.com/jub0bs/fcors and github.com/jub0bs/fcors/risky. |
Package risky provides additional options that complement those provided by package github.com/jub0bs/fcors but that are potentially dangerous.
|
Package risky provides additional options that complement those provided by package github.com/jub0bs/fcors but that are potentially dangerous. |