Middleware unduly allow OPTIONS requests
Problem
Contrary to popular belief,
- not all
OPTIONSrequests are CORS-preflight requests, and OPTIONSonly needs to be listed as an allowed method in the CORS configuration when the server wishes to allow clients to send actual (as opposed to preflight)OPTIONSrequests, which is rare.
However, all CORS middleware produced by this library implicitly allow OPTIONS requests, even when OPTIONS isn't listed in their configuration's AllowedMethods field. As a result, those middleware unduly allow browser-based clients to send OPTIONS requests to the server.
Proof of concept
Consider the following server (borrowed from the README):
package main
import (
"net/http"
"github.com/rs/cors"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte("{\"hello\": \"world\"}"))
})
handler := cors.Default().Handler(mux)
http.ListenAndServe(":8080", handler)
}
Note that cors.Default() lists GET, HEAD, POST as allowed methods, but does not list OPTIONS as an allowed method.
Start the server locally, then simulate a preflight request preceding an actual OPTIONS request by running the following command:
curl -v -XOPTIONS \
-H "Access-Control-Request-Method: OPTIONS" \
-H "Origin: https://example.com" \
localhost:8080
Note that OPTIONS is (but shouldn't be) listed in the response's Access-Control-Allow-Methods header:
HTTP/1.1 204 No Content
Access-Control-Allow-Methods: OPTIONS
Access-Control-Allow-Origin: *
Vary: Origin, Access-Control-Request-Method, Access-Control-Request-Headers
-snip-
Therefore, preflight would succeed and the browser would (undesirably) proceed to send the client's actual OPTIONS request.
Remediation
// isMethodAllowed checks if a given method can be used as part of a cross-domain request
// on the endpoint
func (c *Cors) isMethodAllowed(method string) bool {
- if len(c.allowedMethods) == 0 {
- // If no method allowed, always return false, even for preflight request
- return false
- }
- if method == http.MethodOptions {
- // Always allow preflight requests
- return true
- }
for _, m := range c.allowedMethods {
if m == method {
return true
The proposed change is a breaking one (in terms of behaviour), but I don't expect that many CORS-aware servers wish to allow clients to send actual OPTIONS requests.