{"record":{"id":"66886df85df75aa0","repo":"valyala/fasthttp","slug":"proxy-dial-target-address-contains-cr-or-lf-q","errorCode":null,"errorMessage":"proxy dial target address contains cr or lf: %q","messagePattern":"proxy dial target address contains cr or lf: %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"fasthttpproxy/dialer.go","lineNumber":229,"sourceCode":"// In the httpProxyDial function, the proxy.Dialer that implements\n// this interface can retrieve timeout information when sending the CONNECT\n// method to the HTTP proxy.\ntype httpProxyDialer interface {\n\tconnectTimeout() time.Duration\n}\n\n// DialerFunc Make a function of type func(network, addr string) (net.Conn, error)\n// implement the proxy.Dialer interface.\ntype DialerFunc func(network, addr string) (net.Conn, error)\n\nfunc (d DialerFunc) Dial(network, addr string) (net.Conn, error) {\n\treturn d(network, addr)\n}\n\n// Establish a connection through an HTTP proxy.\nfunc httpProxyDial(dialer proxy.Dialer, network, addr, proxyAddr, auth string) (net.Conn, error) {\n\tif strings.ContainsAny(addr, \"\\r\\n\") {\n\t\treturn nil, fmt.Errorf(\"proxy dial target address contains cr or lf: %q\", addr)\n\t}\n\n\tconn, err := dialer.Dial(network, proxyAddr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar connectTimeout time.Duration\n\thp, ok := dialer.(httpProxyDialer)\n\tif ok {\n\t\tconnectTimeout = hp.connectTimeout()\n\t}\n\n\tif connectTimeout > 0 {\n\t\tif err = conn.SetDeadline(time.Now().Add(connectTimeout)); err != nil {\n\t\t\t_ = conn.Close()\n\t\t\treturn nil, err\n\t\t}\n\t\tdefer func() {","sourceCodeStart":211,"sourceCodeEnd":247,"githubUrl":"https://github.com/valyala/fasthttp/blob/c96f600972c6f4a7a30d664257b340ebe9d60124/fasthttpproxy/dialer.go#L211-L247","documentation":"httpProxyDial validates the target address before building the CONNECT request through an HTTP proxy. Addresses containing CR or LF would enable request-splitting/header injection in the proxy request, so dialing is refused with this error.","triggerScenarios":"Calling (&fasthttpproxy.HttpProxyDialer{...}).Dial (or ProxyDialer) with an addr like \"example.com:80\\r\\nX-Evil: 1\", typically from unsanitized user input or a malicious URL host.","commonSituations":"Proxying URLs built from raw user input, CRLF injection attempts from untrusted clients, Host headers copied verbatim from inbound requests.","solutions":["Sanitize the target host: strip/reject any control characters before dialing","Parse the URL with net/url and use only u.Host, which normalizes out CR/LF","Reject the request at the application layer with a 400-style response when the host contains \\r or \\n","Log the rejected address for security monitoring"],"exampleFix":"// before\ndialer.Dial(\"tcp\", rawHostFromUser) // rawHostFromUser = \"evil.com:80\\r\\nX: y\"\n// after\nu, err := url.Parse(\"http://\" + rawHostFromUser)\nif err != nil || strings.ContainsAny(u.Host, \"\\r\\n\") {\n    return fmt.Errorf(\"invalid proxy target host: %q\", rawHostFromUser)\n}\ndialer.Dial(\"tcp\", u.Host)","handlingStrategy":"validation","validationCode":"if strings.ContainsAny(targetAddr, \"\\r\\n\") {\n    return errors.New(\"target address must not contain CR or LF\")\n}","typeGuard":"func isSafeProxyTarget(addr string) bool {\n    u, err := url.Parse(\"http://\" + addr)\n    return err == nil && u.Host == addr && !strings.ContainsAny(addr, \"\\r\\n\\x00\")\n}","tryCatchPattern":"conn, err := proxyDialer.Dial(\"tcp\", addr)\nif err != nil {\n    if strings.Contains(err.Error(), \"contains cr or lf\") {\n        return fmt.Errorf(\"rejected unsafe proxy target: %q\", addr)\n    }\n    return err\n}","preventionTips":["Parse user-supplied URLs with net/url and use u.Host only","Reject control characters in hosts at the edge (server-side request validation)","Treat CRLF-in-host input as a potential SSRF/injection attack and log it"],"tags":["go","http-proxy","security","crlf-injection"],"backgroundTag":"crlf-injection","analyzedSha":"c96f600972c6f4a7a30d664257b340ebe9d60124","analyzedAt":"2026-08-31T22:48:28.265Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}