Current design flaws¶
This section documents some bad/buggy designs in PixivFE's design, both front-end and back-end.
Low Quality Go Module: net/url¶
url.Path is stored decoded (no %XX). url.Scheme is stored without :// (mandated by RFC). Not sure why Go does that. Felt like this is bound to cause some nasty bug on decoding and encoding.
Current proxied URLs don't have weird characters in them. Hopefully it stays this way.
Solution: Replace net/url with a better third-party module
Mitigation: replaced url.Parse with urlx.Parse from github.com/goware/urlx
Update as of 2024-10-14: swapping out url.Parse for urlx.Parse caused several issues and was essentially reverted over multiple bug fix commits
Missing CSRF protection on settings endpoints¶
Settings routes (POST /settings/...) accept state-changing requests (proxy URL, content filters, tag blacklists, etc.) without server-side CSRF token validation. A CSRF token IS extracted from Pixiv during login (core/settings.go:SetToken, line 246) and stored in an HttpOnly cookie, but it is never verified on subsequent form submissions.
Mitigations in place: SameSite=Lax, HttpOnly cookies, and HTMX's custom headers prevent naive CSRF. But these are browser-enforced, not server-enforced. A determined attacker on a browser that doesn't enforce SameSite correctly could still forge requests.
Fix: Validate the CSRF token cookie against a form/header value on all mutating endpoints.
User-supplied proxy URLs injected into CSP¶
handleCustomProxy (server/routes/settings.go) stores arbitrary user-provided URLs as the image/static/ugoira proxy. These URLs are then injected directly into the CSP's img-src, media-src, and form-action directives via buildCSP (server/middleware/set_response_headers.go). A malicious proxy URL or a compromised upstream could weaken the CSP or enable data exfiltration through image/media loads.
Fix: Validate proxy URLs against an allowlist of known safe origins, or require explicit operator opt-in per proxy type.
ProxyHandler passes through upstream Content-Type without filtering¶
core/requests/requests.go:ProxyHandler writes the upstream response's status code and body bytes without checking Content-Type. If i.pximg.net serves HTML instead of an image (misconfiguration, compromise, or redirect to a login page), it would render under PixivFE's origin with full access to the page context.
Fix: Validate Content-Type against an allowlist (image/*, video/* for ugoira) before proxying. Return an error page for unexpected types.
FNV-32 hash for cache keys¶
core/requests/caching.go:generateCacheKey uses FNV-32 (32-bit output, ~4 billion possible values) for cache key hashing. With key material of url + ":" + userToken, collision probability is negligible at the default cache size (100 entries), but grows with cache size. Non-critical at current scale; worth revisiting if cache size increases significantly or if the cache becomes shared across instances.