crazy-goat/workerman-bundle
Symfony bundle integrating Workerman to run a high-performance async HTTP server, scheduler and supervisor in pure PHP. Keeps the Symfony kernel/container alive between requests for faster apps. Supports SO_REUSEPORT and optional direct Request creation for speed.
The RequestConverter applies security hardening when propagating HTTP headers from Workerman to Symfony:
When multiple Cookie header lines are present in the request, values are joined with ; as required by RFC 6265, rather than the standard HTTP , separator. This prevents cookie smuggling where a , byte in a cookie value could be misinterpreted as a separator between cookies.
Before (vulnerable):
Cookie: session=abc123
Cookie: token=xyz789
→ HTTP_COOKIE: "session=abc123, token=xyz789"
→ Cookies parsed as one cookie: session=abc123, token=xyz789
After (hardened):
Cookie: session=abc123
Cookie: token=xyz789
→ HTTP_COOKIE: "session=abc123; token=xyz789"
→ Cookies correctly parsed as two cookies: session=abc123, token=xyz789
Duplicate Host, Content-Length, and Authorization headers are suspicious and may indicate request smuggling or header injection attacks. Only the first value of each is propagated to Symfony; subsequent values are silently discarded.
Header values containing control characters (\x00-\x08, \x0B, \x0C, \x0E-\x1F, \x7F) are rejected with an \InvalidArgumentException. This prevents:
The RequestConverter also validates:
Host-header poisoning is a class of attack where an attacker controls the Host header sent to the server, potentially affecting password-reset links, cache keys, and routing decisions made by the application.
By default, all Host header values from incoming requests are accepted. To restrict which hostnames your application responds to, configure trusted_hosts in your Workerman configuration:
workerman:
trusted_hosts:
- '^example\.com$'
- '^api\.example\.com$'
Each entry is a regular expression pattern (without delimiters). Symfony adds the delimiters automatically. A request whose Host header does not match any pattern will be rejected with a SuspiciousOperationException, resulting in a 400 response.
framework.trusted_hostsIf you also configure framework.trusted_hosts in Symfony, note that:
workerman.trusted_hosts is enforced inside the Workerman worker process, before the Symfony kernel handles the request.Configure trusted_hosts when your application generates absolute URLs based on the incoming Host header (e.g., password-reset emails, webhook callbacks, OAuth redirects). Without it, an attacker can craft a request with a spoofed Host header and trick the application into generating URLs pointing to an attacker-controlled domain.
When serve_files is enabled on a server, StaticFilesMiddleware serves files from the configured root directory. This middleware applies security hardening to prevent accidental exposure of sensitive files:
The following are always blocked (requests return 404):
. is rejected (e.g., .env, .git/HEAD, .htaccess, .hidden/secret.txt)..php, .phar, and .phtml files are never served.composer.json, composer.lock, and package.json are blocked..htaccess and .htpasswd are blocked.To restrict which file types are served, configure an explicit extension allowlist:
workerman:
servers:
- name: 'Web'
listen: 'http://0.0.0.0:80'
serve_files: true
root_dir: '%kernel.project_dir%/public'
static_files:
allowed_extensions:
- 'css'
- 'js'
- 'png'
- 'jpg'
- 'jpeg'
- 'gif'
- 'webp'
- 'svg'
- 'woff'
- 'woff2'
- 'ico'
- 'html'
- 'json'
- 'txt'
When allowed_extensions is set, only files with one of the listed extensions are served — all others return 404. The denylist (dotfiles, .php, etc.) takes precedence and is always enforced regardless of the allowlist setting.
root_dir isolated: Point root_dir to a dedicated public directory (e.g., %kernel.project_dir%/public). Never set it to the project root or a directory containing .env, source code, or VCS metadata.allowed_extensions to only permit the file types your application actually serves as static assets.The SfxDownloader downloads and extracts phpmicro.sfx from upstream HTTPS mirrors. Before extracting a downloaded ZIP archive, each entry name is validated against path traversal attacks (zip-slip):
\) are rejected./ or a Windows drive letter (C:\) are rejected... segments after normalization are rejected.If any entry fails validation, the build aborts with a \RuntimeException.
When downloading phpmicro.sfx with --insecure (or build.sfx.allow_insecure: true), TLS peer
verification is disabled. To prevent an on-path attacker from downgrading the download from HTTPS
to plain HTTP via a redirect:
This defense is always active when allow_insecure is enabled, regardless of whether a checksum
is configured.
The build fails if no SHA-256 checksum is configured, unless --unsafe-no-checksum is explicitly
passed. This ensures supply-chain integrity by default:
--sfx-checksum=HASH or config build.sfx.sha256 → checksum verified after download--unsafe-no-checksum → no verification (not recommended)How can I help you explore Laravel packages today?