github.com/kayoticsully/syncthing@v0.8.9-0.20140724133906-c45a2fdc03f8/cmd/syncthing/gui_csrf.go (about) 1 package main 2 3 import ( 4 "bufio" 5 "crypto/rand" 6 "encoding/base64" 7 "fmt" 8 "net/http" 9 "os" 10 "path/filepath" 11 "strings" 12 "sync" 13 "time" 14 15 "github.com/calmh/syncthing/osutil" 16 ) 17 18 var csrfTokens []string 19 var csrfMut sync.Mutex 20 21 // Check for CSRF token on /rest/ URLs. If a correct one is not given, reject 22 // the request with 403. For / and /index.html, set a new CSRF cookie if none 23 // is currently set. 24 func csrfMiddleware(prefix string, next http.Handler) http.Handler { 25 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 26 // Allow requests carrying a valid API key 27 if validAPIKey(r.Header.Get("X-API-Key")) { 28 next.ServeHTTP(w, r) 29 return 30 } 31 32 // Allow requests for the front page, and set a CSRF cookie if there isn't already a valid one. 33 if !strings.HasPrefix(r.URL.Path, prefix) { 34 cookie, err := r.Cookie("CSRF-Token") 35 if err != nil || !validCsrfToken(cookie.Value) { 36 cookie = &http.Cookie{ 37 Name: "CSRF-Token", 38 Value: newCsrfToken(), 39 } 40 http.SetCookie(w, cookie) 41 } 42 next.ServeHTTP(w, r) 43 return 44 } 45 46 // Verify the CSRF token 47 token := r.Header.Get("X-CSRF-Token") 48 if !validCsrfToken(token) { 49 http.Error(w, "CSRF Error", 403) 50 return 51 } 52 53 next.ServeHTTP(w, r) 54 }) 55 } 56 57 func validCsrfToken(token string) bool { 58 csrfMut.Lock() 59 defer csrfMut.Unlock() 60 for _, t := range csrfTokens { 61 if t == token { 62 return true 63 } 64 } 65 return false 66 } 67 68 func newCsrfToken() string { 69 bs := make([]byte, 30) 70 _, err := rand.Reader.Read(bs) 71 if err != nil { 72 l.Fatalln(err) 73 } 74 75 token := base64.StdEncoding.EncodeToString(bs) 76 77 csrfMut.Lock() 78 csrfTokens = append(csrfTokens, token) 79 if len(csrfTokens) > 10 { 80 csrfTokens = csrfTokens[len(csrfTokens)-10:] 81 } 82 defer csrfMut.Unlock() 83 84 saveCsrfTokens() 85 86 return token 87 } 88 89 func saveCsrfTokens() { 90 name := filepath.Join(confDir, "csrftokens.txt") 91 tmp := fmt.Sprintf("%s.tmp.%d", name, time.Now().UnixNano()) 92 93 f, err := os.OpenFile(tmp, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0644) 94 if err != nil { 95 return 96 } 97 defer os.Remove(tmp) 98 99 for _, t := range csrfTokens { 100 _, err := fmt.Fprintln(f, t) 101 if err != nil { 102 return 103 } 104 } 105 106 err = f.Close() 107 if err != nil { 108 return 109 } 110 111 osutil.Rename(tmp, name) 112 } 113 114 func loadCsrfTokens() { 115 name := filepath.Join(confDir, "csrftokens.txt") 116 f, err := os.Open(name) 117 if err != nil { 118 return 119 } 120 defer f.Close() 121 122 s := bufio.NewScanner(f) 123 for s.Scan() { 124 csrfTokens = append(csrfTokens, s.Text()) 125 } 126 }