feat: implement CSRF protection on RPC layer
Double-submit cookie pattern: backend generates csrf_token cookie on login (non-HttpOnly so JS can read it), validates X-CSRF-Token header matches cookie on all authenticated RPC calls. Returns 403 if missing/mismatched. Frontend reads cookie and sends header automatically. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,11 @@ export interface RPCResponse<T> {
|
||||
}
|
||||
}
|
||||
|
||||
function getCsrfToken(): string | null {
|
||||
const match = document.cookie.match(/(?:^|;\s*)csrf_token=([^;]+)/)
|
||||
return match ? match[1]! : null
|
||||
}
|
||||
|
||||
class RPCClient {
|
||||
private baseUrl: string
|
||||
|
||||
@@ -31,12 +36,18 @@ class RPCClient {
|
||||
const timeoutId = setTimeout(() => controller.abort(), timeout)
|
||||
|
||||
try {
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
const csrfToken = getCsrfToken()
|
||||
if (csrfToken) {
|
||||
headers['X-CSRF-Token'] = csrfToken
|
||||
}
|
||||
|
||||
const response = await fetch(this.baseUrl, {
|
||||
method: 'POST',
|
||||
credentials: 'include', // Important for session cookies
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
headers,
|
||||
body: JSON.stringify({ method, params }),
|
||||
signal: controller.signal,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user