autoban.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. // autoban
  2. package main
  3. import (
  4. "bytes"
  5. "database/sql"
  6. // "fmt"
  7. "io/ioutil"
  8. "math/big"
  9. "net"
  10. "net/http"
  11. "regexp"
  12. "strconv"
  13. "strings"
  14. "time"
  15. "github.com/hjson/hjson-go"
  16. "github.com/gin-gonic/gin"
  17. _ "github.com/go-sql-driver/mysql"
  18. )
  19. var (
  20. siteKeys map[string]int
  21. searchRobots map[string]int
  22. validUID = regexp.MustCompile(`^([A-Fa-f0-9]{36})$`)
  23. rootKey = "rootkey" // key of service to reload configuration, use in test_autoban_reload.php and web-admin
  24. )
  25. type receiveIpStruct struct {
  26. siteid int
  27. ip int64
  28. date int
  29. url string
  30. ownervisit int
  31. agent string
  32. refer string
  33. method string
  34. }
  35. func main() {
  36. mainConfig := readCfgFile("config.json") // чтение конфигурации
  37. var dbConnect = mainConfig["dbUser"].(string) + ":" + mainConfig["dbPsw"].(string) + "@tcp(" + mainConfig["dbAddress"].(string) + ":" + FloatToString(mainConfig["dbPort"].(float64)) + ")/" + mainConfig["dbBase"].(string)
  38. // gin.SetMode(gin.ReleaseMode) // можно включить для скрытия debug информации
  39. // Initializing DB connection
  40. db, err := sql.Open("mysql", dbConnect)
  41. checkErr(err)
  42. defer db.Close()
  43. db.SetConnMaxLifetime(time.Second * 100)
  44. // загрузка базы ключ=сайт
  45. siteKeys := loadSiteKeys(db)
  46. searchRobots := loadRobotsKeys(db)
  47. banAgents := loadBanAgentsKeys(db)
  48. router := gin.Default()
  49. router.POST(mainConfig["httpAddFolder"].(string)+"/receive", func(c *gin.Context) {
  50. var receiveIP receiveIpStruct
  51. uid := c.Query("uid")
  52. if validUID.MatchString(uid) { // проверка на корректный uid
  53. siteid := siteKeys[uid]
  54. if siteid > 0 { // проверка на наличие uid в базе
  55. receiveIP = fillReceiveIP(siteid, c)
  56. // check searchrobots in UserAgent and mark it
  57. if checkSearchRobot(c.PostForm("agent"), searchRobots) {
  58. receiveIP.ownervisit = 2
  59. }
  60. dbSaveIp(db, receiveIP)
  61. c.String(http.StatusOK, "statusOK")
  62. if checkSuspicious(receiveIP.url, receiveIP.method) == 1 && receiveIP.ownervisit == 0 { // если подозрительное поведение и это не собственник, то в бан
  63. updateBanIPInBase(db, receiveIP.ip, 2, siteid)
  64. }
  65. } else {
  66. // c.String(http.StatusForbidden, "invalid uid")
  67. c.String(http.StatusOK, "invalid uid")
  68. }
  69. } else {
  70. // c.String(http.StatusForbidden, "invalid uid")
  71. c.String(http.StatusOK, "invalid uid")
  72. }
  73. })
  74. router.POST(mainConfig["httpAddFolder"].(string)+"/check", func(c *gin.Context) {
  75. var receiveIP receiveIpStruct
  76. uid := c.Query("uid")
  77. if validUID.MatchString(uid) { // проверка на корректный uid
  78. siteid := siteKeys[uid]
  79. if siteid > 0 { // проверка на наличие uid в базе
  80. receiveIP = fillReceiveIP(siteid, c)
  81. // check searchrobots in UserAgent and mark it
  82. if checkSearchRobot(c.PostForm("agent"), searchRobots) {
  83. receiveIP.ownervisit = 2
  84. }
  85. // c.String(http.StatusOK, "statusOK")
  86. // проверка на присутствие в банлисте
  87. checkIP := dbCheckIP(db, receiveIP, siteid)
  88. if checkBanAgent(c.PostForm("agent"), banAgents) { // если токсичный userAgent
  89. c.String(http.StatusOK, "IP banned") // отвечаем отказом
  90. // updateBanIPInBase(db, receiveIP.ip, 0, siteid)
  91. // dbSaveIp(db, receiveIP)
  92. } else {
  93. if checkIP == "IP permited" { // если нет в бан листе, то проверяем на подозрительное поведение
  94. checkSusp := checkSuspicious(receiveIP.url, receiveIP.method)
  95. if checkSusp == 0 || receiveIP.ownervisit != 0 { // проверка на подозрительное поведение
  96. c.String(http.StatusOK, checkIP)
  97. dbSaveIp(db, receiveIP)
  98. } else if checkSusp == 1 && receiveIP.ownervisit == 0 {
  99. c.String(http.StatusOK, "IP banned")
  100. updateBanIPInBase(db, receiveIP.ip, 2, siteid)
  101. dbSaveIp(db, receiveIP)
  102. } else {
  103. dbSaveIp(db, receiveIP)
  104. }
  105. } else {
  106. c.String(http.StatusOK, checkIP) // если в банлисте, то только отвечаем отказом
  107. }
  108. }
  109. } else {
  110. // c.String(http.StatusForbidden, "invalid uid")
  111. c.String(http.StatusOK, "invalid uid")
  112. }
  113. } else {
  114. // c.String(http.StatusForbidden, "invalid uid")
  115. c.String(http.StatusOK, "invalid uid")
  116. }
  117. })
  118. router.GET(mainConfig["httpAddFolder"].(string)+"/reload", func(c *gin.Context) {
  119. if c.Query("rootKey") == rootKey {
  120. siteKeys = loadSiteKeys(db)
  121. searchRobots = loadRobotsKeys(db)
  122. banAgents = loadBanAgentsKeys(db)
  123. c.String(http.StatusOK, "SiteKeys reloaded successfully")
  124. }
  125. })
  126. router.Run(":8080")
  127. }
  128. // ----- FUNCTIONS -----
  129. // чтение конфиг файла
  130. func readCfgFile(filename string) map[string]interface{} {
  131. configFileText, err := ioutil.ReadFile(filename)
  132. if err != nil {
  133. panic(err)
  134. }
  135. var dat map[string]interface{}
  136. // Decode and a check for errors.
  137. if err := hjson.Unmarshal(configFileText, &dat); err != nil {
  138. panic(err)
  139. }
  140. return dat
  141. }
  142. // запись в таблицу визитов
  143. func dbSaveIp(db *sql.DB, receiveIP receiveIpStruct) {
  144. stmt, err := db.Prepare("INSERT allvisits SET ip=?, date=?, url=?, siteid=?, ownervisit=?, agent=?, refer=?, method=?")
  145. var method uint
  146. // method = 0
  147. checkErr(err)
  148. if receiveIP.method == "GET" {
  149. method = 0
  150. } else {
  151. method = 1
  152. }
  153. res, err := stmt.Exec(receiveIP.ip, receiveIP.date, receiveIP.url, receiveIP.siteid, receiveIP.ownervisit, receiveIP.agent, receiveIP.refer, method)
  154. checkErr(err)
  155. _ = res
  156. }
  157. // проверка ip на наличие ban листе
  158. func dbCheckIP(db *sql.DB, receiveIP receiveIpStruct, siteid int) string {
  159. var ip int64 //uint
  160. query := "SELECT ip FROM blacklistip WHERE siteid = ?"
  161. mainQuery, err := db.Query(query, siteid)
  162. checkErr(err)
  163. defer mainQuery.Close()
  164. for mainQuery.Next() {
  165. err := mainQuery.Scan(&ip)
  166. checkErr(err)
  167. if compareIP(InttoIP4(receiveIP.ip), InttoIP4(ip)) == 1 {
  168. return "IP banned"
  169. }
  170. }
  171. return "IP permited"
  172. }
  173. // проверка соответствия двух ip адресов
  174. func compareIP(receivedIP_in string, bannedIP_in string) int {
  175. var (
  176. bannedIP_start net.IP
  177. bannedIP_end net.IP
  178. )
  179. bannedIP := net.ParseIP(bannedIP_in)
  180. receivedIP := net.ParseIP(receivedIP_in)
  181. IPv4Int := big.NewInt(0)
  182. IPv4Int.SetBytes(bannedIP.To4())
  183. ipInt := IPv4Int.Int64()
  184. b0 := strconv.FormatInt((ipInt>>24)&0xff, 10)
  185. b0i := (ipInt >> 24) & 0xff
  186. b1 := strconv.FormatInt((ipInt>>16)&0xff, 10)
  187. b1i := (ipInt >> 16) & 0xff
  188. b2 := strconv.FormatInt((ipInt>>8)&0xff, 10)
  189. b2i := (ipInt >> 8) & 0xff
  190. b3 := strconv.FormatInt((ipInt & 0xff), 10)
  191. b3i := ipInt & 0xff
  192. if b0i == 255 && b1i == 255 && b2i == 255 && b3i == 255 {
  193. bannedIP_start = net.ParseIP("0.0.0.0")
  194. bannedIP_end = net.ParseIP("255.255.255.255")
  195. } else if b1i == 255 && b2i == 255 && b3i == 255 {
  196. bannedIP_start = net.ParseIP(b0 + ".0.0.0")
  197. bannedIP_end = net.ParseIP(b0 + ".255.255.255")
  198. } else if b2i == 255 && b3i == 255 {
  199. bannedIP_start = net.ParseIP(b0 + "." + b1 + ".0.0")
  200. bannedIP_end = net.ParseIP(b0 + "." + b1 + ".255.255")
  201. } else if b3i == 255 {
  202. bannedIP_start = net.ParseIP(b0 + "." + b1 + "." + b2 + ".0")
  203. bannedIP_end = net.ParseIP(b0 + "." + b1 + "." + b2 + ".255")
  204. } else {
  205. bannedIP_start = net.ParseIP(b0 + "." + b1 + "." + b2 + "." + b3)
  206. bannedIP_end = bannedIP_start
  207. }
  208. if bytes.Compare(receivedIP, bannedIP_start) >= 0 && bytes.Compare(receivedIP, bannedIP_end) <= 0 {
  209. return 1
  210. } else {
  211. return 0
  212. }
  213. }
  214. // загрузка карты key и siteid
  215. func loadSiteKeys(db *sql.DB) map[string]int {
  216. var (
  217. uid string
  218. siteid int
  219. )
  220. siteKeys := make(map[string]int)
  221. siteKeysQuery, err := db.Query("SELECT uid, siteid FROM sites WHERE active = 1 AND disabled = 0")
  222. checkErr(err)
  223. defer siteKeysQuery.Close()
  224. for siteKeysQuery.Next() {
  225. err := siteKeysQuery.Scan(&uid, &siteid)
  226. checkErr(err)
  227. siteKeys[uid] = siteid
  228. }
  229. return siteKeys
  230. }
  231. // загрузка карты key и siteid
  232. func loadRobotsKeys(db *sql.DB) map[string]int {
  233. var (
  234. robotName string
  235. uid int
  236. )
  237. robotsKeys := make(map[string]int)
  238. robotsKeysQuery, err := db.Query("SELECT robotname FROM search_robots")
  239. checkErr(err)
  240. defer robotsKeysQuery.Close()
  241. for robotsKeysQuery.Next() {
  242. err := robotsKeysQuery.Scan(&robotName)
  243. checkErr(err)
  244. robotsKeys[robotName] = uid
  245. uid++
  246. }
  247. return robotsKeys
  248. }
  249. // загрузка карты key и siteid
  250. func loadBanAgentsKeys(db *sql.DB) map[string]int {
  251. var (
  252. robotName string
  253. uid int
  254. )
  255. robotsKeys := make(map[string]int)
  256. robotsKeysQuery, err := db.Query("SELECT robotname FROM ban_agents")
  257. checkErr(err)
  258. defer robotsKeysQuery.Close()
  259. for robotsKeysQuery.Next() {
  260. err := robotsKeysQuery.Scan(&robotName)
  261. checkErr(err)
  262. robotsKeys[robotName] = uid
  263. uid++
  264. }
  265. return robotsKeys
  266. }
  267. // проверка ошибок
  268. func checkErr(err error) {
  269. if err != nil {
  270. panic(err)
  271. }
  272. }
  273. // ipv4 2 int64
  274. func IP4toInt(ipv4 string) int64 {
  275. IPv4Address := net.ParseIP(ipv4)
  276. IPv4Int := big.NewInt(0)
  277. IPv4Int.SetBytes(IPv4Address.To4())
  278. return IPv4Int.Int64()
  279. }
  280. // int64 2 ipv4
  281. func InttoIP4(ipInt int64) string {
  282. // need to do two bit shifting and “0xff” masking
  283. b0 := strconv.FormatInt((ipInt>>24)&0xff, 10)
  284. b1 := strconv.FormatInt((ipInt>>16)&0xff, 10)
  285. b2 := strconv.FormatInt((ipInt>>8)&0xff, 10)
  286. b3 := strconv.FormatInt((ipInt & 0xff), 10)
  287. return b0 + "." + b1 + "." + b2 + "." + b3
  288. }
  289. // преобразование строки в число
  290. func str2int(val string) int {
  291. i, _ := strconv.Atoi(val)
  292. return i
  293. }
  294. // to convert a float number to a string
  295. func FloatToString(input_num float64) string {
  296. return strconv.FormatFloat(input_num, 'f', 0, 64)
  297. }
  298. // to convert a float number to uint
  299. func FloatToUint(input_num float64) uint {
  300. return uint(input_num)
  301. }
  302. // проверка наличия имени робота в UserAgent
  303. func checkSearchRobot(userAgent string, searchRobots map[string]int) bool {
  304. for v, _ := range searchRobots {
  305. if strings.Contains(userAgent, v) {
  306. return true
  307. }
  308. }
  309. return false
  310. }
  311. // проверка наличия имени нежелательного Agent в UserAgent
  312. func checkBanAgent(userAgent string, banAgent map[string]int) bool {
  313. for v, _ := range banAgent {
  314. if strings.Contains(userAgent, v) {
  315. return true
  316. }
  317. }
  318. return false
  319. }
  320. // проверка подозрительной активности
  321. func checkSuspicious(urlQuery string, method string) uint {
  322. // ФОРУМ, проверка публикации с активацией
  323. ifForumPostActivation_i := strings.Index(urlQuery, "mode=post")
  324. ifForumPostActivation_ii := strings.Index(urlQuery, "[+Activation+]")
  325. // if (ifForumPostActivation_i + ifForumPostActivation_ii) > -1 {
  326. if ifForumPostActivation_i > -1 && ifForumPostActivation_ii > -1 {
  327. return 1
  328. }
  329. // ФОРУМ, проверка захода на регистрацию (первая страница с правилами) в режиме POST - НЕ ПРАВИЛЬНО, т.к. регистрация проходит в этом же php
  330. // if urlQuery == "/forum/ucp.php?mode=register" && method == "POST" {
  331. // return 1
  332. // }
  333. // PMA
  334. if urlQuery == "/pma/scripts/setup.php" {
  335. return 1
  336. }
  337. // ifForumInfo := strings.Index(urlQuery, "/forum/app.php/post/")
  338. if strings.Index(urlQuery, "/forum/app.php/post/") > -1 {
  339. return 1
  340. }
  341. if strings.Index(urlQuery, "/component/users/") > -1 {
  342. return 1
  343. }
  344. return 0
  345. }
  346. // добавить адрес в банлист в базе
  347. func updateBanIPInBase(db *sql.DB, IP4Int int64, reason int, siteid int) {
  348. stmt, err := db.Prepare("INSERT INTO blacklistip SET siteid = ?, ip = ?, reason = ? ON DUPLICATE KEY UPDATE ban_count = ban_count + 1, lastupdate = NOW()")
  349. checkErr(err)
  350. res, err := stmt.Exec(siteid, IP4Int, reason)
  351. checkErr(err)
  352. _ = res
  353. }
  354. // обрезка строки до 250 символов
  355. func truncateString(str string, num int) string {
  356. bnoden := str
  357. if len(str) > num {
  358. if num > 3 {
  359. num -= 3
  360. }
  361. bnoden = str[0:num] + "..."
  362. }
  363. return bnoden
  364. }
  365. // заполнение струтуры данными о посетителе
  366. func fillReceiveIP(siteid int, c *gin.Context) receiveIpStruct {
  367. var receiveIP receiveIpStruct
  368. receiveIP.siteid = siteid
  369. receiveIP.ip = IP4toInt(truncateString(c.PostForm("ip"), 15))
  370. receiveIP.date = str2int(truncateString(c.PostForm("date"), 11))
  371. receiveIP.url = truncateString(c.PostForm("url"), 250)
  372. receiveIP.ownervisit = str2int(truncateString(c.PostForm("ownervisit"), 1))
  373. receiveIP.agent = truncateString(c.PostForm("agent"), 250)
  374. receiveIP.refer = truncateString(c.PostForm("refer"), 250)
  375. receiveIP.method = truncateString(c.PostForm("method"), 4)
  376. return receiveIP
  377. }