| 470 | } |
| 471 | |
| 472 | func (s *Server) serveHTTP(w http.ResponseWriter, r *http.Request) error { |
| 473 | // reject very long methods; probably a mistake or an attack |
| 474 | if len(r.Method) > 32 { |
| 475 | if s.shouldLogRequest(r) { |
| 476 | if c := s.accessLogger.Check(zapcore.DebugLevel, "rejecting request with long method"); c != nil { |
| 477 | c.Write( |
| 478 | zap.String("method_trunc", r.Method[:32]), |
| 479 | zap.String("remote_addr", r.RemoteAddr), |
| 480 | ) |
| 481 | } |
| 482 | } |
| 483 | return HandlerError{StatusCode: http.StatusMethodNotAllowed} |
| 484 | } |
| 485 | |
| 486 | // RFC 9112 section 3.2: "A server MUST respond with a 400 (Bad Request) status |
| 487 | // code to any HTTP/1.1 request message that lacks a Host header field and to any |
| 488 | // request message that contains more than one Host header field line or a Host |
| 489 | // header field with an invalid field value." |
| 490 | if r.ProtoMajor == 1 && r.ProtoMinor == 1 && r.Host == "" { |
| 491 | return HandlerError{ |
| 492 | Err: errors.New("rfc9112 forbids empty Host"), |
| 493 | StatusCode: http.StatusBadRequest, |
| 494 | } |
| 495 | } |
| 496 | |
| 497 | // Drop headers whose names contain `_`: once FastCGI/CGI/FrankenPHP etc. rewrites `-` to |
| 498 | // `_`, an underscore alias collides with the legitimate hyphenated header |
| 499 | // and can bypass `forward_auth copy_headers` (GHSA-f59h-q822-g45g). |
| 500 | for k := range r.Header { |
| 501 | if strings.ContainsRune(k, '_') { |
| 502 | delete(r.Header, k) |
| 503 | |
| 504 | if c := s.logger.Check(zapcore.DebugLevel, "dropping header containing underscore"); c != nil { |
| 505 | c.Write(zap.String("header", k)) |
| 506 | } |
| 507 | } |
| 508 | } |
| 509 | |
| 510 | // execute the primary handler chain |
| 511 | return s.primaryHandlerChain.ServeHTTP(w, r) |
| 512 | } |
| 513 | |
| 514 | // wrapPrimaryRoute wraps stack (a compiled middleware handler chain) |
| 515 | // in s.enforcementHandler which performs crucial security checks, etc. |