implemented security counter of IP answering incorrect to captcha, to ban the IP for some time
arnaucode idoctnef@openmailbox.org
Wed, 09 Aug 2017 20:34:09 +0200
11 files changed,
95 insertions(+),
20 deletions(-)
M
README.md
→
README.md
@@ -1,6 +1,13 @@
# goCaptcha captcha server, with own datasets, to train own machine learning AI +## 0 - Why? +Captcha systems are useful to avoid bots posting data on databases. But modern captcha systems are from enterprises to train machine learning algorithms, and monetize results. +When user answers a captcha, is training the AI from the enterprise. + +This project, aims to be a self hosted captcha system, that trains own AI. +To avoid feeding AI from companies. + ## 1 - How to use? ### 1.1 - Frontend Insert this lines in the html file:@@ -27,8 +34,11 @@ {
"serverIP": "127.0.0.1", "serverPort": "3025", "imgsFolder": "imgs", - "numImgsCaptcha": 9 + "numImgsCaptcha": 9, + "suspiciousIPCountLimit": 2, + "timeBan": 30 } + ``` - Run MongoDB.@@ -194,3 +204,5 @@ - If the captcha is resolved in less than 1 second, it's not valid.
- If the captcha is resolved in more than 1 minute, it's not valid. - The images url, are UUIDs generated each time, in order to give different names for the images each time. - The ip of requested captcha and answered captcha petitions must be the same. +- Each time a user fails answering the captcha, the server adds a counter to the IP and stores in to MongoDB. When the counter on that IP is greather than the value 'suspiciousIPCountLimit' defined in serverConfig.json, the IP is blocked for 'timeBan' seconds, also defined in serverConfig.json. +If before the counter exceeds the 'suspictiousIPCountLimit' the user answers correctly the captcha, the counter is deleted.
M
captcha.go
→
captcha.go
@@ -1,7 +1,6 @@
package main import ( - "fmt" "math/rand" "os/exec" "strings"@@ -21,7 +20,7 @@ Id string `json:"id"`
ImgsSolution []string `json:"imgssolution"` Question string `json:"question"` //select all X Date int64 `json:"date"` - Ip string `json:"ip"` + IP string `json:"ip"` } type CaptchaAnswer struct { CaptchaId string `json:"captchaid"`@@ -32,6 +31,11 @@ CaptchaId string `json:"captchaid"`
Real string `json:"real"` Fake string `json:"fake"` } +type SuspiciousIP struct { + Date int64 `json:"date"` + IP string `json:"ip"` + Count int `json:"count"` +} func generateUUID() string { out, err := exec.Command("uuidgen").Output()@@ -72,7 +76,7 @@ question := generateQuestionFromCategoriesArray(captchaSol.ImgsSolution)
captcha.Question = question captchaSol.Question = question captchaSol.Date = time.Now().Unix() - captchaSol.Ip = ip + captchaSol.IP = ip err := captchaSolCollection.Insert(captchaSol) check(err) return captcha@@ -103,7 +107,6 @@
//time elapsed from captcha generation comprovation date := time.Unix(captchaSol.Date, 0) elapsed := time.Since(date) - fmt.Println(elapsed.Seconds()) if elapsed.Seconds() < 1 { solved = false }@@ -111,7 +114,7 @@ if elapsed.Seconds() > 60 {
solved = false } //ip comprovation - if captchaSol.Ip != ip { + if captchaSol.IP != ip { solved = false } return solved
M
main.go
→
main.go
@@ -19,9 +19,10 @@ //connect with mongodb
readMongodbConfig("./mongodbConfig.json") session, err := getSession() check(err) - captchaCollection = getCollection(session, "captchas") + //captchaCollection = getCollection(session, "captchas") captchaSolCollection = getCollection(session, "captchassolutions") imgFakePathCollection = getCollection(session, "imgfakepath") + suspiciousIPCollection = getCollection(session, "suspiciousip") //start the server //http server start
M
mongoConfig.go
→
mongoConfig.go
@@ -16,9 +16,10 @@ }
var mongoConfig MongoConfig -var captchaCollection *mgo.Collection +//var captchaCollection *mgo.Collection var captchaSolCollection *mgo.Collection var imgFakePathCollection *mgo.Collection +var suspiciousIPCollection *mgo.Collection func readMongodbConfig(path string) { file, e := ioutil.ReadFile(path)
M
serverConfig.go
→
serverConfig.go
@@ -20,10 +20,12 @@ }
//server config type ServerConfig struct { - ServerIP string `json:"serverIP"` - ServerPort string `json:"serverPort"` - ImgsFolder string `json:"imgsFolder"` - NumImgsCaptcha int `json:"numImgsCaptcha"` + ServerIP string `json:"serverIP"` + ServerPort string `json:"serverPort"` + ImgsFolder string `json:"imgsFolder"` + NumImgsCaptcha int `json:"numImgsCaptcha"` + SuspiciousIPCountLimit int `json:"suspiciousIPCountLimit"` + TimeBan float64 `json:"timeBan"` } var serverConfig ServerConfig
M
serverConfig.json
→
serverConfig.json
@@ -2,5 +2,7 @@ {
"serverIP": "127.0.0.1", "serverPort": "3025", "imgsFolder": "imgs", - "numImgsCaptcha": 9 + "numImgsCaptcha": 9, + "suspiciousIPCountLimit": 2, + "timeBan": 30 }
M
serverRoutes.go
→
serverRoutes.go
@@ -5,8 +5,10 @@ "encoding/json"
"fmt" "image/jpeg" "io/ioutil" + "log" "net/http" "strings" + "time" "gopkg.in/mgo.v2/bson"@@ -89,6 +91,51 @@ defer r.Body.Close()
ip := strings.Split(r.RemoteAddr, ":")[0] resp := validateCaptcha(captchaAnswer, ip) + //if resp==false add ip to blacklist + if resp == false { + //get SuspiciousIP + suspiciousIP := SuspiciousIP{} + err := suspiciousIPCollection.Find(bson.M{"ip": ip}).One(&suspiciousIP) + if err != nil { + //if not exist, add + suspiciousIP.Date = time.Now().Unix() + suspiciousIP.IP = ip + suspiciousIP.Count = 0 + //store suspiciousIP in MongoDB + err := suspiciousIPCollection.Insert(suspiciousIP) + check(err) + } else { + //if exist + suspiciousIP.Date = time.Now().Unix() + suspiciousIP.Count++ + err := suspiciousIPCollection.Update(bson.M{"ip": ip}, suspiciousIP) + check(err) + } + } + //if count > limit, resp=false + suspiciousIP := SuspiciousIP{} + err = suspiciousIPCollection.Find(bson.M{"ip": ip}).One(&suspiciousIP) + if err == nil { + //if exist, and time.Since(suspiciousIP.Date) < serverConfig.TimeBan, increase counter + if time.Since(time.Unix(suspiciousIP.Date, 0)).Seconds() < serverConfig.TimeBan { + if suspiciousIP.Count > serverConfig.SuspiciousIPCountLimit { + if resp == false { + log.Println("IP: " + ip + ", has reached limit count of SuspiciousIP") + } + resp = false + } + } else { + //timeBan is completed, delete counter + err := suspiciousIPCollection.Remove(bson.M{"ip": ip}) + check(err) + } + if resp == true { + //answered correct, delete counter + err := suspiciousIPCollection.Remove(bson.M{"ip": ip}) + check(err) + } + } + jsonResp, err := json.Marshal(resp) check(err) fmt.Fprintln(w, string(jsonResp))
M
web/goCaptcha.css
→
web/goCaptcha.css
@@ -1,10 +1,16 @@
- +#goCaptcha{ + max-width: 380px; +} .c_blue300{ background: #64B5F6!important; color: #ffffff!important; } .c_red300{ background: #E57373!important; + color: #ffffff!important; +} +.c_green300{ + background: #81C784!important; color: #ffffff!important; } .g_button{@@ -31,8 +37,8 @@ .g_floatRight{
float: right; } .g_selected{ - border: 4px solid yellow; + border: 5px solid #64B5F6; } .g_unselected{ - border: 4px solid white; + border: 5px solid white; }
M
web/goCaptcha.js
→
web/goCaptcha.js
@@ -33,7 +33,7 @@ var html;
html = ""; html += "<h2>Select all " + captcha.question + "s</h2>"; for (k in captcha.imgs) { - html += "<img id='" + k + "' onclick='selectCaptchaImg(this)' src='" + goCaptchaURL + "/image/" + captcha.imgs[k] + "' style='width:150px;cursor:pointer;' />"; + html += "<img id='" + k + "' onclick='selectCaptchaImg(this)' src='" + goCaptchaURL + "/image/" + captcha.imgs[k] + "' style='width:120px;cursor:pointer;' class='g_unselected' />"; } html += "<div onclick='validateCaptcha()' class='g_button c_blue300 g_floatRight'>Validate</div>"; document.getElementById("goCaptcha").innerHTML = html;@@ -59,6 +59,7 @@ resp = JSON.parse(data);
var html = ""; if (resp) { html += "<h2>goCaptcha validated</h2>"; + html += "<div onclick='getCaptcha()' class='g_button c_green300 g_floatRight'>Reload Captcha</div>"; } else { html += "<h2>goCaptcha failed</h2>"; html += "<div onclick='getCaptcha()' class='g_button c_red300 g_floatRight'>Reload Captcha</div>";
M
web/index.html
→
web/index.html
@@ -13,14 +13,14 @@ <body>
<br> <div class="container"> <div class="row"> - <div class="col-sm-3"></div> - <div class="col-sm-6"> + <div class="col-sm-4"></div> + <div class="col-sm-5"> <div class="panel panel-default"> <div class="panel-heading c_blue300"> <h3 class="panel-title">goCaptcha</h3> </div> <div class="panel-body"> - <div id="goCaptcha"></div> + <div id="goCaptcha">Getting goCaptcha data from server</div> </div> </div> </div>