Browse Source

implemented security counter of IP answering incorrect to captcha, to ban the IP for some time

master
arnaucode 7 years ago
parent
commit
9262080f54
11 changed files with 95 additions and 20 deletions
  1. +13
    -1
      README.md
  2. +8
    -5
      captcha.go
  3. BIN
      goCaptcha
  4. +2
    -1
      main.go
  5. +2
    -1
      mongoConfig.go
  6. +6
    -4
      serverConfig.go
  7. +3
    -1
      serverConfig.json
  8. +47
    -0
      serverRoutes.go
  9. +9
    -3
      web/goCaptcha.css
  10. +2
    -1
      web/goCaptcha.js
  11. +3
    -3
      web/index.html

+ 13
- 1
README.md

@ -1,6 +1,13 @@
# goCaptcha # goCaptcha
captcha server, with own datasets, to train own machine learning AI 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 - How to use?
### 1.1 - Frontend ### 1.1 - Frontend
Insert this lines in the html file: Insert this lines in the html file:
@ -27,8 +34,11 @@ It will place the goCaptcha box in the div:
"serverIP": "127.0.0.1", "serverIP": "127.0.0.1",
"serverPort": "3025", "serverPort": "3025",
"imgsFolder": "imgs", "imgsFolder": "imgs",
"numImgsCaptcha": 9
"numImgsCaptcha": 9,
"suspiciousIPCountLimit": 2,
"timeBan": 30
} }
``` ```
- Run MongoDB. - Run MongoDB.
@ -194,3 +204,5 @@ If the selection is correct, returns 'true', if the selection is not correct, re
- If the captcha is resolved in more than 1 minute, 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 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. - 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.

+ 8
- 5
captcha.go

@ -1,7 +1,6 @@
package main package main
import ( import (
"fmt"
"math/rand" "math/rand"
"os/exec" "os/exec"
"strings" "strings"
@ -21,7 +20,7 @@ type CaptchaSol struct {
ImgsSolution []string `json:"imgssolution"` ImgsSolution []string `json:"imgssolution"`
Question string `json:"question"` //select all X Question string `json:"question"` //select all X
Date int64 `json:"date"` Date int64 `json:"date"`
Ip string `json:"ip"`
IP string `json:"ip"`
} }
type CaptchaAnswer struct { type CaptchaAnswer struct {
CaptchaId string `json:"captchaid"` CaptchaId string `json:"captchaid"`
@ -32,6 +31,11 @@ type ImgFakePath struct {
Real string `json:"real"` Real string `json:"real"`
Fake string `json:"fake"` Fake string `json:"fake"`
} }
type SuspiciousIP struct {
Date int64 `json:"date"`
IP string `json:"ip"`
Count int `json:"count"`
}
func generateUUID() string { func generateUUID() string {
out, err := exec.Command("uuidgen").Output() out, err := exec.Command("uuidgen").Output()
@ -72,7 +76,7 @@ func generateCaptcha(count int, ip string) Captcha {
captcha.Question = question captcha.Question = question
captchaSol.Question = question captchaSol.Question = question
captchaSol.Date = time.Now().Unix() captchaSol.Date = time.Now().Unix()
captchaSol.Ip = ip
captchaSol.IP = ip
err := captchaSolCollection.Insert(captchaSol) err := captchaSolCollection.Insert(captchaSol)
check(err) check(err)
return captcha return captcha
@ -103,7 +107,6 @@ func validateCaptcha(captchaAnswer CaptchaAnswer, ip string) bool {
//time elapsed from captcha generation comprovation //time elapsed from captcha generation comprovation
date := time.Unix(captchaSol.Date, 0) date := time.Unix(captchaSol.Date, 0)
elapsed := time.Since(date) elapsed := time.Since(date)
fmt.Println(elapsed.Seconds())
if elapsed.Seconds() < 1 { if elapsed.Seconds() < 1 {
solved = false solved = false
} }
@ -111,7 +114,7 @@ func validateCaptcha(captchaAnswer CaptchaAnswer, ip string) bool {
solved = false solved = false
} }
//ip comprovation //ip comprovation
if captchaSol.Ip != ip {
if captchaSol.IP != ip {
solved = false solved = false
} }
return solved return solved

BIN
goCaptcha


+ 2
- 1
main.go

@ -19,9 +19,10 @@ func main() {
readMongodbConfig("./mongodbConfig.json") readMongodbConfig("./mongodbConfig.json")
session, err := getSession() session, err := getSession()
check(err) check(err)
captchaCollection = getCollection(session, "captchas")
//captchaCollection = getCollection(session, "captchas")
captchaSolCollection = getCollection(session, "captchassolutions") captchaSolCollection = getCollection(session, "captchassolutions")
imgFakePathCollection = getCollection(session, "imgfakepath") imgFakePathCollection = getCollection(session, "imgfakepath")
suspiciousIPCollection = getCollection(session, "suspiciousip")
//start the server //start the server
//http server start //http server start

+ 2
- 1
mongoConfig.go

@ -16,9 +16,10 @@ type MongoConfig struct {
var mongoConfig MongoConfig var mongoConfig MongoConfig
var captchaCollection *mgo.Collection
//var captchaCollection *mgo.Collection
var captchaSolCollection *mgo.Collection var captchaSolCollection *mgo.Collection
var imgFakePathCollection *mgo.Collection var imgFakePathCollection *mgo.Collection
var suspiciousIPCollection *mgo.Collection
func readMongodbConfig(path string) { func readMongodbConfig(path string) {
file, e := ioutil.ReadFile(path) file, e := ioutil.ReadFile(path)

+ 6
- 4
serverConfig.go

@ -20,10 +20,12 @@ type Route struct {
//server config //server config
type ServerConfig struct { 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 var serverConfig ServerConfig

+ 3
- 1
serverConfig.json

@ -2,5 +2,7 @@
"serverIP": "127.0.0.1", "serverIP": "127.0.0.1",
"serverPort": "3025", "serverPort": "3025",
"imgsFolder": "imgs", "imgsFolder": "imgs",
"numImgsCaptcha": 9
"numImgsCaptcha": 9,
"suspiciousIPCountLimit": 2,
"timeBan": 30
} }

+ 47
- 0
serverRoutes.go

@ -5,8 +5,10 @@ import (
"fmt" "fmt"
"image/jpeg" "image/jpeg"
"io/ioutil" "io/ioutil"
"log"
"net/http" "net/http"
"strings" "strings"
"time"
"gopkg.in/mgo.v2/bson" "gopkg.in/mgo.v2/bson"
@ -89,6 +91,51 @@ func AnswerCaptcha(w http.ResponseWriter, r *http.Request) {
ip := strings.Split(r.RemoteAddr, ":")[0] ip := strings.Split(r.RemoteAddr, ":")[0]
resp := validateCaptcha(captchaAnswer, ip) 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) jsonResp, err := json.Marshal(resp)
check(err) check(err)
fmt.Fprintln(w, string(jsonResp)) fmt.Fprintln(w, string(jsonResp))

+ 9
- 3
web/goCaptcha.css

@ -1,4 +1,6 @@
#goCaptcha{
max-width: 380px;
}
.c_blue300{ .c_blue300{
background: #64B5F6!important; background: #64B5F6!important;
color: #ffffff!important; color: #ffffff!important;
@ -7,6 +9,10 @@
background: #E57373!important; background: #E57373!important;
color: #ffffff!important; color: #ffffff!important;
} }
.c_green300{
background: #81C784!important;
color: #ffffff!important;
}
.g_button{ .g_button{
border: none; border: none;
border-radius: 2px; border-radius: 2px;
@ -31,8 +37,8 @@
float: right; float: right;
} }
.g_selected{ .g_selected{
border: 4px solid yellow;
border: 5px solid #64B5F6;
} }
.g_unselected{ .g_unselected{
border: 4px solid white;
border: 5px solid white;
} }

+ 2
- 1
web/goCaptcha.js

@ -33,7 +33,7 @@ function showCaptcha(captcha) {
html = ""; html = "";
html += "<h2>Select all " + captcha.question + "s</h2>"; html += "<h2>Select all " + captcha.question + "s</h2>";
for (k in captcha.imgs) { 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>"; html += "<div onclick='validateCaptcha()' class='g_button c_blue300 g_floatRight'>Validate</div>";
document.getElementById("goCaptcha").innerHTML = html; document.getElementById("goCaptcha").innerHTML = html;
@ -59,6 +59,7 @@ function validateCaptcha() {
var html = ""; var html = "";
if (resp) { if (resp) {
html += "<h2>goCaptcha validated</h2>"; html += "<h2>goCaptcha validated</h2>";
html += "<div onclick='getCaptcha()' class='g_button c_green300 g_floatRight'>Reload Captcha</div>";
} else { } else {
html += "<h2>goCaptcha failed</h2>"; html += "<h2>goCaptcha failed</h2>";
html += "<div onclick='getCaptcha()' class='g_button c_red300 g_floatRight'>Reload Captcha</div>"; html += "<div onclick='getCaptcha()' class='g_button c_red300 g_floatRight'>Reload Captcha</div>";

+ 3
- 3
web/index.html

@ -13,14 +13,14 @@
<br> <br>
<div class="container"> <div class="container">
<div class="row"> <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 panel-default">
<div class="panel-heading c_blue300"> <div class="panel-heading c_blue300">
<h3 class="panel-title">goCaptcha</h3> <h3 class="panel-title">goCaptcha</h3>
</div> </div>
<div class="panel-body"> <div class="panel-body">
<div id="goCaptcha"></div>
<div id="goCaptcha">Getting goCaptcha data from server</div>
</div> </div>
</div> </div>
</div> </div>

Loading…
Cancel
Save