recaptcha implemented
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
run:
|
||||
go run *.go
|
||||
SKIP_CAPTCHA=1 go run *.go
|
||||
|
||||
test:
|
||||
jasmine-node .
|
||||
|
||||
@@ -264,3 +264,7 @@ form {
|
||||
flex: 1 0;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#captcha {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -3,22 +3,24 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>NoteHub — {{if .ID}}Edit{{else}}Add{{end}} note</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport">
|
||||
<meta charset="UTF-8" />
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
|
||||
<link href="/style.css" rel="stylesheet" type="text/css" />
|
||||
<base target="_blank">
|
||||
<base target="_blank" />
|
||||
<script src='https://www.google.com/recaptcha/api.js'></script>
|
||||
</head>
|
||||
<body>
|
||||
<form action="/note" autocomplete="off" method="POST" target="_self">
|
||||
<textarea autofocus name="text">{{.Text}}</textarea>
|
||||
<fieldset>
|
||||
<input id="id" name="id" value="{{.ID}}" type="hidden" />
|
||||
<input class="ui-elem" id="password" name="password" placeholder="Password for editing" type="text" autocomplete="off">
|
||||
<input class="ui-elem" name="password" placeholder="Password for editing" type="text" autocomplete="off" />
|
||||
<label style="margin-right: 1em">
|
||||
<input id="tos" name="tos" type="checkbox" onClick="toggleButton()">
|
||||
<input id="tos" name="tos" type="checkbox" onClick="toggleButton()" />
|
||||
Accept <a href="/TOS.md">Terms of Service</a>
|
||||
</label>
|
||||
<input class="button ui-elem" disabled id="publish-button" name="button" type="submit" value="Publish">
|
||||
<div id="captcha" class="g-recaptcha" data-sitekey="6LemnDEUAAAAAC6A4VNRefz0BSLiC343W4sXQd6I"></div>
|
||||
<input class="button ui-elem" disabled id="publish-button" name="button" type="submit" value="Publish" />
|
||||
</fieldset>
|
||||
</form>
|
||||
<footer>
|
||||
@@ -28,7 +30,10 @@
|
||||
</footer>
|
||||
<script>
|
||||
function $(id) { return document.getElementById(id) }
|
||||
function toggleButton() { $('publish-button').disabled = !$('tos').checked }
|
||||
function toggleButton() {
|
||||
$('publish-button').disabled = !$('tos').checked;
|
||||
$('captcha').style.display = $('tos').checked ? 'block' : 'none';
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -17,6 +17,7 @@ var (
|
||||
statuses = map[int]string{
|
||||
400: "Bad request",
|
||||
401: "Unauthorized",
|
||||
403: "Forbidden",
|
||||
404: "Not found",
|
||||
412: "Precondition failed",
|
||||
429: "Too many requests",
|
||||
|
||||
@@ -2,11 +2,13 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"html/template"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
@@ -36,6 +38,8 @@ func main() {
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
skipCaptcha := os.Getenv("SKIP_CAPTCHA") != ""
|
||||
|
||||
var ads []byte
|
||||
adsFName := os.Getenv("ADS")
|
||||
if adsFName != "" {
|
||||
@@ -102,6 +106,11 @@ func main() {
|
||||
|
||||
e.POST("/note", func(c echo.Context) error {
|
||||
c.Logger().Debug("POST /note requested")
|
||||
if !skipCaptcha && !checkRecaptcha(c, c.FormValue("g-recaptcha-response")) {
|
||||
c.Logger().Warnf("captcha validation failed for %s", c.Request().RemoteAddr)
|
||||
code := http.StatusForbidden
|
||||
return c.Render(code, "Note", responsePage(code))
|
||||
}
|
||||
if !legitAccess(c) {
|
||||
code := http.StatusTooManyRequests
|
||||
c.Logger().Errorf("rate limit exceeded for %s", c.Request().RemoteAddr)
|
||||
@@ -169,3 +178,31 @@ func fraudelent(n *Note) bool {
|
||||
return n.Views > 100 &&
|
||||
int(math.Ceil(100*float64(l1-l2)/float64(l1))) > fraudThreshold
|
||||
}
|
||||
|
||||
func checkRecaptcha(c echo.Context, captchaResp string) bool {
|
||||
resp, err := http.PostForm("https://www.google.com/recaptcha/api/siteverify", url.Values{
|
||||
"secret": []string{os.Getenv("RECAPTCHA_SECRET")},
|
||||
"response": []string{captchaResp},
|
||||
"remoteip": []string{c.Request().RemoteAddr},
|
||||
})
|
||||
if err != nil {
|
||||
c.Logger().Errorf("captcha response verification failed: %v", err)
|
||||
return false
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
respJson := &struct {
|
||||
Success bool `json:"success"`
|
||||
ErrorCodes []string `json:"error-codes"`
|
||||
}{}
|
||||
s, err := ioutil.ReadAll(resp.Body)
|
||||
err = json.Unmarshal(s, respJson)
|
||||
if err != nil {
|
||||
c.Logger().Errorf("captcha response parse recaptcha response: %v", err)
|
||||
return false
|
||||
}
|
||||
if !respJson.Success {
|
||||
c.Logger().Errorf("captcha response validation failed: %v", respJson.ErrorCodes)
|
||||
}
|
||||
return respJson.Success
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user