~bigbes/sr-ht-dolt

ref: 88a1d379f707062ebac734104a35a83065ef6b78 sr-ht-dolt/web/csrf.go -rw-r--r-- 1.8 KiB
88a1d379 — Eugene Blikh feat(web/beads): filters for the parade board 30 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package web

import (
	"net/http"
	"net/url"
)

// checkSameOrigin is dolt.sr.ht's CSRF defence for state-changing POSTs. core-go
// ships no CSRF helper, so we keep it simple and explicit: a mutating request
// must carry an Origin (or, failing that, a Referer) header whose scheme+host
// matches our own configured origin. Cross-site form posts from a browser always
// send an Origin that differs from ours, so this blocks them; same-origin form
// submissions from our own pages always match.
//
// Rationale and limits (documented deliberately): we trust the Origin/Referer
// header, which browsers set and script cannot forge cross-origin. A request
// with NEITHER header is rejected — our own forms are same-origin and browsers
// send Origin on form POSTs, so a missing header signals a non-browser or
// stripped request, which we decline rather than wave through. This is a
// header-check, not a token scheme; it is sufficient because dolt.sr.ht uses the
// shared unified-login cookie (SameSite handling lives in meta) and has no
// cross-origin embedding.
func (a *app) checkSameOrigin(r *http.Request) bool {
	selfOrigin := a.newBasePage(r, "").SelfOrigin
	self, err := url.Parse(selfOrigin)
	if err != nil || self.Host == "" {
		return false
	}

	if origin := r.Header.Get("Origin"); origin != "" {
		return originMatches(origin, self)
	}
	if referer := r.Header.Get("Referer"); referer != "" {
		return originMatches(referer, self)
	}
	// No Origin and no Referer: refuse rather than assume same-origin.
	return false
}

// originMatches reports whether raw (a full URL from an Origin or Referer
// header) has the same scheme and host as self.
func originMatches(raw string, self *url.URL) bool {
	u, err := url.Parse(raw)
	if err != nil {
		return false
	}
	return u.Scheme == self.Scheme && u.Host == self.Host
}