CSRF & XSS Prevention
CSRF Protection
Implemented Controls
Browser Pipeline (:browser pipeline in router)
protect_from_forgeryplug applied to all browser routes- Generates CSRF token per session
- Token validated on POST/PUT/DELETE/PATCH requests
- Token stored in session and compared against request
Token Transmission
- Form submissions: token included via hidden input field
- AJAX/fetch requests:
x-csrf-tokenheader accepted- Phoenix automatically includes CSRF token in meta tag
- JavaScript frameworks can read from
document.querySelector('meta[name="csrf-token"]').content
Cookie Attributes
- SameSite=Lax on session cookies
- Prevents cross-site cookie sending on top-level navigation
- Blocks CSRF on GET requests and same-site navigation
- Allows GET requests to cross-site via browser navigation (e.g., clicking a link)
Exclusions (Intentional)
:apipipeline — Used by Gitea-compatible REST API and runner endpoints- No CSRF protection — acceptable because endpoints use token authentication (PAT, runner JWT)
- Token-authenticated clients don’t automatically send cookies
:mobile_apipipeline — Mobile API endpoints- Uses personal access token authentication
- No CSRF protection — acceptable for token-based auth
XSS Prevention
Implemented Controls
LiveView Auto-Escaping
- Phoenix LiveView automatically escapes all interpolated values in HEEx templates
{@value}— HTML-escaped interpolation<%= @value %>— HTML-escaped interpolation in tag bodies{@value}in attribute values — attribute-escaped
- Template compilation provides compile-time escaping guarantees
Markdown Sanitization
lib/git_gud/markdown.ex— HTML sanitization pipeline- Strips dangerous HTML elements (
<script>,<iframe>,<object>,<embed>) - Strips event handler attributes (
onload,onclick, etc.) - Strips
javascript:URIs in href/src attributes - Allows safe subset: bold, italic, links, code blocks, tables, images
- Processes user-generated content before rendering in templates
- Strips dangerous HTML elements (
Content-Type Headers
- All responses include appropriate
Content-Typeheaders - JSON responses:
application/json - HTML responses:
text/html - Prevents MIME-type confusion attacks
Security Observations
Medium
No Content-Security-Policy (CSP) header
- CSP provides defense-in-depth against XSS
- Prevents inline script execution (except explicitly allowed)
- Restricts sources for scripts, styles, images, fonts, etc.
- Recommendation: add CSP header via Plug
- Example:
default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; - Start with
Content-Security-Policy-Report-Onlyfor testing
- Example:
No X-Frame-Options or X-Content-Type-Options headers
X-Frame-Options: DENY— prevents clickjackingX-Content-Type-Options: nosniff— prevents MIME-type sniffing- Recommendation: add via
put_secure_browser_headers(already in:browserpipeline)- Verify these headers are not being stripped by reverse proxy
LiveDashboard accessible
- Phoenix LiveDashboard available at
/dashboard/* - Exposes runtime metrics, process info, logger, IEx console
- Risk: IEx console allows arbitrary code execution
- Recommendation: disable
dev_routesin productionconfig :git_gud, GitGudWeb.Endpoint, dev_routes: false- Or restrict to admin users only via plug
MailboxPreview accessible
- Swoosh MailboxPreview at
/dev/mailboxin development - Exposes received emails (including magic links with tokens)
- Recommendation: disable in production via
config :swoosh, local: false
Low
Markdown sanitization needs periodic review
- XSS vectors evolve over time (e.g., SVG onload, iframe src, data URIs)
- Recommendation: add security test suite with known XSS payloads
- Test against latest OWASP XSS cheatsheet regularly
- Consider using a well-maintained sanitization library (e.g.,
bleach-like Elixir port)
Inline JavaScript in templates
- If
phx-hookorcolocated hooksused in templates, inline scripts may be injected - Recommendation: ensure all JavaScript is loaded via assets bundle (app.js)
- Use
phx-hookwith external hooks defined inassets/js/where possible