Add CSRF, API-key auth, and rate-limiting hardening - #59
Open
robomello wants to merge 1 commit into
Open
Conversation
Follow-up to the yt-dlp argument injection fix. reclip has no login system, so a malicious web page could silently POST to a user's locally-bound instance to trigger downloads/SSRF-style requests on their behalf. This adds defense-in-depth, all off-by-default so the zero-config quick start keeps working: - CSRF: reject state-changing /api/* requests whose Origin/Referer doesn't match the server's own host (browsers always send Origin on cross-origin POST/fetch). Non-browser clients with no Origin/Referer are unaffected. - Optional API key: set RECLIP_API_KEY to require a matching X-API-Key header on all /api/* routes (constant-time compare). - Rate limiting: dependency-free, in-memory per-IP sliding-window limiter on the yt-dlp-invoking endpoints (/api/info, /api/playlist, /api/download), tunable via RECLIP_RATE_LIMIT / RECLIP_RATE_WINDOW. Documents all of this in a new README "Security" section.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Follow-up to #58 (yt-dlp argument injection / RCE fix). reclip has no login system by default, so the main residual risk is a confused-deputy / CSRF-triggered SSRF: a malicious web page a user has open in their browser could silently
POSTto their locally-bound reclip instance (e.g.http://localhost:8899/api/download) and trigger downloads on their behalf, or hammer theyt-dlp-invoking endpoints. This PR adds defense-in-depth for that, entirely off-by-default so the zero-config./reclip.shquick start is unaffected.Changes
/api/*requests are rejected with403unless theirOrigin/Referermatches the server's ownHost. Requests with noOrigin/Referer(curl, scripts, non-browser clients) are unaffected — protection is specifically against silent browser-driven cross-site requests.RECLIP_API_KEYand every/api/*request must send a matchingX-API-Keyheader (constant-time comparison viahmac.compare_digest). Useful when binding beyond127.0.0.1.yt-dlp-invoking endpoints —/api/infoand/api/playlist(30 req/60s),/api/download(10 req/60s, stricter since it spawns a longer-running subprocess and writes files). Tunable viaRECLIP_RATE_LIMIT/RECLIP_RATE_WINDOW. Returns429withRetry-Afterwhen exceeded.Design notes
Flask-Limiter, no session/cookie machinery) to preserve the project's "2 dependencies" footprint and keep the change small and auditable.RECLIP_TRUST_PROXYguards against IP spoofing viaX-Forwarded-Forunless explicitly opted into.Testing
Manually smoke-tested locally:
POST /api/download(mismatchedOrigin) →403 {"error":"Cross-site request blocked"}POST /api/download→ passes CSRF check, proceeds to normal validationRECLIP_API_KEYset, no/incorrectX-API-Key→401; correct key → passes authPOST /api/downloadrequests against the default 10/60s limit → first 10 succeed past rate limiting, 11th/12th →429python3 -m py_compile app.pycleanBackward compatibility
No breaking changes. All new behavior is either always-on-but-permissive (CSRF check only blocks genuinely cross-site browser requests) or opt-in via env vars. Existing bundled web UI (same-origin
fetchcalls) works unmodified.