From cb3f5f8b932156df9078085e77bef493eca1581b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Sun, 16 Apr 2017 18:49:06 +0300 Subject: cmd/faucet: double check user against the GH website --- cmd/faucet/faucet.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'cmd/faucet/faucet.go') diff --git a/cmd/faucet/faucet.go b/cmd/faucet/faucet.go index fd34cdec1..c418da818 100644 --- a/cmd/faucet/faucet.go +++ b/cmd/faucet/faucet.go @@ -306,7 +306,7 @@ func (f *faucet) apiHandler(conn *websocket.Conn) { websocket.JSON.Send(conn, map[string]string{"error": "URL doesn't link to GitHub Gists"}) continue } - log.Info("Faucet funds requested", "gist", msg.URL) + log.Info("Faucet funds requested", "addr", conn.RemoteAddr(), "gist", msg.URL) // Retrieve the gist from the GitHub Gist APIs parts := strings.Split(msg.URL, "/") @@ -348,6 +348,17 @@ func (f *faucet) apiHandler(conn *websocket.Conn) { websocket.JSON.Send(conn, map[string]string{"error": "No Ethereum address found to fund"}) continue } + // Validate the user's existence since the API is unhelpful here + if res, err = http.Head("https://github.com/%s", gist.Owner.Login); err != nil { + websocket.JSON.Send(conn, map[string]string{"error": err.Error()}) + continue + } + res.Body.Close() + + if res.StatusCode != 200 { + websocket.JSON.Send(conn, map[string]string{"error": "Invalid user... boom!"}) + continue + } // Ensure the user didn't request funds too recently f.lock.Lock() var ( -- cgit v1.2.3 From 03dffe3efdde4fd0d849b9523249d0ad42037dce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Sun, 16 Apr 2017 19:49:40 +0300 Subject: cmd/faucet: add optional recaptcha validation support --- cmd/faucet/faucet.go | 47 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 7 deletions(-) (limited to 'cmd/faucet/faucet.go') diff --git a/cmd/faucet/faucet.go b/cmd/faucet/faucet.go index c418da818..f87cfdb72 100644 --- a/cmd/faucet/faucet.go +++ b/cmd/faucet/faucet.go @@ -29,6 +29,7 @@ import ( "io/ioutil" "math/big" "net/http" + "net/url" "os" "path/filepath" "strings" @@ -73,6 +74,9 @@ var ( githubUser = flag.String("github.user", "", "GitHub user to authenticate with for Gist access") githubToken = flag.String("github.token", "", "GitHub personal token to access Gists with") + captchaToken = flag.String("captcha.token", "", "Recaptcha site key to authenticate client side") + captchaSecret = flag.String("captcha.secret", "", "Recaptcha secret key to authenticate server side") + logFlag = flag.Int("loglevel", 3, "Log level to use for Ethereum and the faucet") ) @@ -96,9 +100,10 @@ func main() { } website := new(bytes.Buffer) template.Must(template.New("").Parse(string(tmpl))).Execute(website, map[string]interface{}{ - "Network": *netnameFlag, - "Amount": *payoutFlag, - "Period": period, + "Network": *netnameFlag, + "Amount": *payoutFlag, + "Period": period, + "Recaptcha": *captchaToken, }) // Load and parse the genesis block requested by the user blob, err := ioutil.ReadFile(*genesisFlag) @@ -297,7 +302,8 @@ func (f *faucet) apiHandler(conn *websocket.Conn) { for { // Fetch the next funding request and validate against github var msg struct { - URL string `json:"url"` + URL string `json:"url"` + Captcha string `json:"captcha"` } if err := websocket.JSON.Receive(conn, &msg); err != nil { return @@ -306,8 +312,35 @@ func (f *faucet) apiHandler(conn *websocket.Conn) { websocket.JSON.Send(conn, map[string]string{"error": "URL doesn't link to GitHub Gists"}) continue } - log.Info("Faucet funds requested", "addr", conn.RemoteAddr(), "gist", msg.URL) + log.Info("Faucet funds requested", "gist", msg.URL) + + // If captcha verifications are enabled, make sure we're not dealing with a robot + if *captchaToken != "" { + form := url.Values{} + form.Add("secret", *captchaSecret) + form.Add("response", msg.Captcha) + res, err := http.PostForm("https://www.google.com/recaptcha/api/siteverify", form) + if err != nil { + websocket.JSON.Send(conn, map[string]string{"error": err.Error()}) + continue + } + var result struct { + Success bool `json:"success"` + Errors json.RawMessage `json:"error-codes"` + } + err = json.NewDecoder(res.Body).Decode(&result) + res.Body.Close() + if err != nil { + websocket.JSON.Send(conn, map[string]string{"error": err.Error()}) + continue + } + if !result.Success { + log.Warn("Captcha verification failed", "err", string(result.Errors)) + websocket.JSON.Send(conn, map[string]string{"error": "Beep-boop, you're a robot!"}) + continue + } + } // Retrieve the gist from the GitHub Gist APIs parts := strings.Split(msg.URL, "/") req, _ := http.NewRequest("GET", "https://api.github.com/gists/"+parts[len(parts)-1], nil) @@ -334,7 +367,7 @@ func (f *faucet) apiHandler(conn *websocket.Conn) { continue } if gist.Owner.Login == "" { - websocket.JSON.Send(conn, map[string]string{"error": "Nice try ;)"}) + websocket.JSON.Send(conn, map[string]string{"error": "Anonymous Gists not allowed"}) continue } // Iterate over all the files and look for Ethereum addresses @@ -349,7 +382,7 @@ func (f *faucet) apiHandler(conn *websocket.Conn) { continue } // Validate the user's existence since the API is unhelpful here - if res, err = http.Head("https://github.com/%s", gist.Owner.Login); err != nil { + if res, err = http.Head("https://github.com/" + gist.Owner.Login); err != nil { websocket.JSON.Send(conn, map[string]string{"error": err.Error()}) continue } -- cgit v1.2.3