{"id":"8ea35fbd6392e1cd","repo":"aio-libs/aiohttp","slug":"forbidden-control-character-detected-in-headers-p","errorCode":null,"errorMessage":"Forbidden control character detected in headers. Potential header injection attack.","messagePattern":"Forbidden control character detected in headers\\. Potential header injection attack\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"critical","filePath":"aiohttp/http_writer.py","lineNumber":374,"sourceCode":"\n        The intended use is to write\n\n          await w.write(data)\n          await w.drain()\n        \"\"\"\n        protocol = self._protocol\n        if protocol.transport is not None and protocol._paused:\n            await protocol._drain_helper()\n\n\n# https://www.rfc-editor.org/info/rfc9110/#section-5.5-5\n# https://www.rfc-editor.org/info/rfc9112/#section-4-3\n_FORBIDDEN_HEADER_CHARS_RE = re.compile(r\"[\\x00-\\x08\\x0a-\\x1f\\x7f]\")\n\n\ndef _safe_header(string: str) -> str:\n    if _FORBIDDEN_HEADER_CHARS_RE.search(string) is not None:\n        raise ValueError(\n            \"Forbidden control character detected in headers. \"\n            \"Potential header injection attack.\"\n        )\n    return string\n\n\ndef _py_serialize_headers(status_line: str, headers: \"CIMultiDict[str]\") -> bytes:\n    _safe_header(status_line)\n    headers_gen = (_safe_header(k) + \": \" + _safe_header(v) for k, v in headers.items())\n    line = status_line + \"\\r\\n\" + \"\\r\\n\".join(headers_gen) + \"\\r\\n\\r\\n\"\n    return line.encode(\"utf-8\")\n\n\n_serialize_headers = _py_serialize_headers\n\ntry:\n    import aiohttp._http_writer as _http_writer  # type: ignore[import-not-found]\n","sourceCodeStart":356,"sourceCodeEnd":392,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/c0ef574e29109210e96e652771ae4e7b88615fa4/aiohttp/http_writer.py#L356-L392","documentation":"ValueError raised by _safe_header when a status line, header name, or header value contains a forbidden control character (0x00-0x08, 0x0a-0x1f, 0x7f). This guards against header injection / response splitting before serializing headers to the wire.","triggerScenarios":"Calling code sets a header or status line containing a CR/LF/NUL or other control char (e.g. embedding user input with a newline into a Location or Set-Cookie value). _py_serialize_headers runs _safe_header on every part and raises ValueError before any bytes go out.","commonSituations":"Putting unescaped user input into response headers (Location, Content-Disposition filenames); CRLF injection from a proxy; newline in a cookie value; copying a filename with control chars; header smuggling attempts.","solutions":["Sanitise/strip control characters from any value placed into a header (encode filenames, strip CR/LF).","Use urllib.parse.quote for filenames in Content-Disposition.","Never concatenate raw user input into the status line.","Reject or percent-encode header values at the trust boundary."],"exampleFix":"// before\n#   filename = request.query['name']   # may contain \\r\\n\n#   headers = {'Content-Disposition': f'attachment; filename={filename}'}\n\n# after\nimport re\nfrom urllib.parse import quote\n_FORBIDDEN = re.compile(r'[\\x00-\\x08\\x0a-\\x1f\\x7f]')\nsafe = _FORBIDDEN.sub('', filename)\nheaders = {'Content-Disposition': f\"attachment; filename=\\\"{quote(safe)}\\\"\"}","handlingStrategy":"validation","validationCode":"import re\n_FORBIDDEN = re.compile(r'[\\x00-\\x08\\x0a-\\x1f\\x7f]')\ndef safe_header(value: str) -> str:\n    if _FORBIDDEN.search(value) is not None:\n        raise ValueError('control char in header')\n    return value\n\n# validate before setting\nsafe_header(location_value)\nheaders['Location'] = location_value","typeGuard":"import re\n_FORBIDDEN = re.compile(r'[\\x00-\\x08\\x0a-\\x1f\\x7f]')\ndef is_safe_header(value: str) -> bool:\n    return _FORBIDDEN.search(value) is None","tryCatchPattern":"try:\n    resp = web.Response(headers=headers)\nexcept ValueError:\n    # header contained control char — sanitize first\n    ...","preventionTips":["Never put raw user input in headers.","Quote/strip CR/LF/NUL from filenames and redirect targets.","Validate at the trust boundary."],"tags":["http","writer","headers","security","header-injection","crlf"],"analyzedSha":"c0ef574e29109210e96e652771ae4e7b88615fa4","analyzedAt":"2026-08-04T19:51:05.467Z","schemaVersion":2}