cors icon indicating copy to clipboard operation
cors copied to clipboard

Middleware unduly allow OPTIONS requests

Open jub0bs opened this issue 10 months ago • 1 comments

Problem

Contrary to popular belief,

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

jub0bs avatar Mar 07 '25 11:03 jub0bs

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.

jub0bs avatar Mar 16 '25 14:03 jub0bs