From 3c4dc97d5b798bb135a426ab85fa787c6cb25bf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonio=20Ca=C3=B1as=20Vargas?= Date: Wed, 13 Feb 2019 15:06:02 +0100 Subject: [PATCH] Version 18.40 --- sql/swad.sql | 14 ++++++- swad_changelog.h | 21 ++++++++--- swad_database.c | 26 +++++++++++-- swad_firewall.c | 95 +++++++++++++++++++++++++++++++++++++++++------- swad_firewall.h | 4 +- swad_main.c | 1 + 6 files changed, 136 insertions(+), 25 deletions(-) diff --git a/sql/swad.sql b/sql/swad.sql index f091ad32..e668c0a4 100644 --- a/sql/swad.sql +++ b/sql/swad.sql @@ -522,9 +522,19 @@ CREATE TABLE IF NOT EXISTS files ( INDEX(ZoneUsrCod), INDEX(PublisherUsrCod)); -- --- Table firewall: stores the most recent IPs in order to mitigate denial of service attacks +-- Table firewall_banned: stores the banned IPs in order to mitigate denial of service attacks -- -CREATE TABLE IF NOT EXISTS firewall ( +CREATE TABLE IF NOT EXISTS firewall_banned ( + IP CHAR(15) NOT NULL, + BanTime DATETIME NOT NULL, + UnbanTime DATETIME NOT NULL, + INDEX(IP,UnbanTime), + INDEX(BanTime), + INDEX(UnbanTime)); +-- +-- Table firewall_log: stores the most recent IPs in order to mitigate denial of service attacks +-- +CREATE TABLE IF NOT EXISTS firewall_log ( ClickTime DATETIME NOT NULL, IP CHAR(15) NOT NULL, INDEX(ClickTime), diff --git a/swad_changelog.h b/swad_changelog.h index ca6ede48..b81bb264 100644 --- a/swad_changelog.h +++ b/swad_changelog.h @@ -335,9 +335,6 @@ Buenos d // TODO: Que la opción por defecto en "Permitir que los profesores vean este examen" la configuren los profesores en cada asignatura" // TODO: URGENTE: Bego del Pino, una columna en resultados de test que indique los descriptores de ese examen -// TODO: Pedro Villar Castro: -// Al asignar un TFG a alumnos, no escribir el DNI del alumno, sino escogerlo de una lista de entre los alumnos inscritos en la asignatura. - // TODO: Cuando se muestre un usuario duplicado, que SWAD calcule automáticamente sus cifras no calculadas sin tener que pulsar en Calcular // TODO: Miguel Damas Hermoso sugiere poder editar texto enriquecido (Markdown) en las preguntas de tipo test @@ -361,6 +358,15 @@ Buenos d // TODO: Tabla de asistencia con símbolos tip ok como entidades HTML +// TODO: Pedro Villar Castro: +// Al asignar un TFG a alumnos, no escribir el DNI del alumno, sino escogerlo de una lista de entre los alumnos inscritos en la asignatura. + +// TODO: Pedro Villar Castro: +// Bloquear individualmente la edición con una casilla de configuración para cada TFG, sólo el profesor de la asignatura (Perico) podría bloquear/desbloquear +// Que haya una opción general que los bloquee todos y que los desbloquee todos +// Para bloquear/desbloquear se usará un icono candado +// Para preasignado/no preasignado usar otro icono (usuario/usuario tachado, por ej.) + /*****************************************************************************/ /****************************** Public constants *****************************/ /*****************************************************************************/ @@ -380,11 +386,16 @@ En OpenSWAD: ps2pdf source.ps destination.pdf */ -#define Log_PLATFORM_VERSION "SWAD 18.39 (2019-02-13)" +#define Log_PLATFORM_VERSION "SWAD 18.40 (2019-02-13)" #define CSS_FILE "swad18.33.css" #define JS_FILE "swad18.32.1.js" /* - Version 18.39: Feb 13, 2019 New module swad_firewall to mitigate mitigate DoS attacks. (239101 lines) + Version 18.40: Feb 13, 2019 New table for banned IPs to mitigate DoS attacks. (239198 lines) + 2 changes necessary in database: +RENAME TABLE firewall TO firewall_log; +CREATE TABLE IF NOT EXISTS firewall_banned (IP CHAR(15) NOT NULL,BanTime DATETIME NOT NULL,UnbanTime DATETIME NOT NULL,INDEX(IP,UnbanTime),INDEX(BanTime),INDEX(UnbanTime)); + + Version 18.39: Feb 13, 2019 New module swad_firewall to mitigate DoS attacks. (239101 lines) 1 change necessary in database: CREATE TABLE IF NOT EXISTS firewall (ClickTime DATETIME NOT NULL,IP CHAR(15) NOT NULL,INDEX(ClickTime),INDEX(IP)); diff --git a/swad_database.c b/swad_database.c index a19dd4e3..f678c082 100644 --- a/swad_database.c +++ b/swad_database.c @@ -1149,9 +1149,29 @@ mysql> DESCRIBE files; "INDEX(ZoneUsrCod)," "INDEX(PublisherUsrCod))"); - /***** Table firewall *****/ + /***** Table firewall_banned *****/ /* -mysql> DESCRIBE firewall; +mysql> DESCRIBE firewall_banned; ++-----------+----------+------+-----+---------+-------+ +| Field | Type | Null | Key | Default | Extra | ++-----------+----------+------+-----+---------+-------+ +| IP | char(15) | NO | MUL | NULL | | +| BanTime | datetime | NO | MUL | NULL | | +| UnbanTime | datetime | NO | MUL | NULL | | ++-----------+----------+------+-----+---------+-------+ +3 rows in set (0.00 sec) +*/ + DB_CreateTable ("CREATE TABLE IF NOT EXISTS firewall_banned (" + "IP CHAR(15) NOT NULL," // Cns_MAX_BYTES_IP + "BanTime DATETIME NOT NULL," + "UnbanTime DATETIME NOT NULL," + "INDEX(IP,UnbanTime)," + "INDEX(BanTime)," + "INDEX(UnbanTime));"); + + /***** Table firewall_log *****/ +/* +mysql> DESCRIBE firewall_log; +-----------+----------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-----------+----------+------+-----+---------+-------+ @@ -1160,7 +1180,7 @@ mysql> DESCRIBE firewall; +-----------+----------+------+-----+---------+-------+ 2 rows in set (0.00 sec) */ - DB_CreateTable ("CREATE TABLE IF NOT EXISTS firewall (" + DB_CreateTable ("CREATE TABLE IF NOT EXISTS firewall_log (" "ClickTime DATETIME NOT NULL," "IP CHAR(15) NOT NULL," // Cns_MAX_BYTES_IP "INDEX(ClickTime)," diff --git a/swad_firewall.c b/swad_firewall.c index 59192921..5b61c5d2 100644 --- a/swad_firewall.c +++ b/swad_firewall.c @@ -38,9 +38,14 @@ extern struct Globals Gbl; /***************************** Private constants *****************************/ /*****************************************************************************/ +/* The maximum number of clicks in the interval + should be large enough to prevent an IP from being banned + due to automatic refresh when the user is viewing the last clicks. */ #define Fw_CHECK_INTERVAL ((time_t)(10UL)) // Check clicks in the last 10 seconds #define Fw_MAX_CLICKS_IN_INTERVAL 30 // Maximum of 30 clicks allowed in 10 seconds +#define Fw_TIME_BANNED ((time_t)(60UL*60UL)) // Ban IP for 1 hour + #define Fw_TIME_TO_DELETE_OLD_CLICKS Fw_CHECK_INTERVAL // Remove clicks older than these seconds /*****************************************************************************/ @@ -51,6 +56,8 @@ extern struct Globals Gbl; /***************************** Internal prototypes ***************************/ /*****************************************************************************/ +static void FW_BanIP (void); + /*****************************************************************************/ /************************** Log access into firewall *************************/ /*****************************************************************************/ @@ -58,13 +65,67 @@ extern struct Globals Gbl; void FW_LogAccess (void) { /***** Log access in firewall recent log *****/ - DB_QueryINSERT ("can not log access into firewall", - "INSERT INTO firewall (ClickTime,IP) VALUES (NOW(),'%s')", + DB_QueryINSERT ("can not log access into firewall_log", + "INSERT INTO firewall_log" + " (ClickTime,IP)" + " VALUES" + " (NOW(),'%s')", Gbl.IP); } /*****************************************************************************/ -/************************** Log access into firewall *************************/ +/********************** Remove old clicks from firewall **********************/ +/*****************************************************************************/ + +void FW_PurgeFirewall (void) + { + /***** Remove old clicks *****/ + DB_QueryDELETE ("can not purge firewall log", + "DELETE LOW_PRIORITY FROM firewall_log" + " WHERE ClickTimeNOW()", + Gbl.IP); + + /***** Exit with status 403 if banned *****/ + /* RFC 6585 suggests "429 Too Many Requests", according to + https://stackoverflow.com/questions/7447283/proper-http-status-to-return-for-hacking-attempts + https://tools.ietf.org/html/rfc2616#section-10.4.4 */ + if (NumCurrentBans) + { + /* Return status 403 Forbidden */ + fprintf (stdout,"Content-Type: text/html; charset=windows-1252\n" + "Status: 403\r\n\r\n" + "" + "" + "Forbidden" + "" + "" + "

You are banned temporarily

" + "" + "\n"); + + /* Close database connection and exit */ + DB_CloseDBConnection (); + exit (0); + } + } + +/*****************************************************************************/ +/**************** Check if too many connections from this IP *****************/ /*****************************************************************************/ void FW_CheckFirewallAndExitIfTooManyRequests (void) @@ -72,8 +133,8 @@ void FW_CheckFirewallAndExitIfTooManyRequests (void) unsigned long NumClicks; /***** Get number of clicks from database *****/ - NumClicks = DB_QueryCOUNT ("can not check firewall", - "SELECT COUNT(*) FROM firewall" + NumClicks = DB_QueryCOUNT ("can not check firewall log", + "SELECT COUNT(*) FROM firewall_log" " WHERE IP='%s'" " AND ClickTime>FROM_UNIXTIME(UNIX_TIMESTAMP()-%lu)", Gbl.IP, @@ -85,9 +146,12 @@ void FW_CheckFirewallAndExitIfTooManyRequests (void) https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429 */ if (NumClicks > Fw_MAX_CLICKS_IN_INTERVAL) { + /* Ban this IP */ + FW_BanIP (); + /* Return status 429 Too Many Requests */ fprintf (stdout,"Content-Type: text/html; charset=windows-1252\n" - "Retry-After: 3600\n" + "Retry-After: %lu\n" "Status: 429\r\n\r\n" "" "" @@ -96,7 +160,8 @@ void FW_CheckFirewallAndExitIfTooManyRequests (void) "" "

Please stop that

" "" - "\n"); + "\n", + (unsigned long) Fw_TIME_BANNED); /* Close database connection and exit */ DB_CloseDBConnection (); @@ -105,14 +170,16 @@ void FW_CheckFirewallAndExitIfTooManyRequests (void) } /*****************************************************************************/ -/********************** Remove old clicks from firewall **********************/ +/********************************* Ban an IP *********************************/ /*****************************************************************************/ -void FW_PurgeFirewall (void) +static void FW_BanIP (void) { - /***** Remove old clicks *****/ - DB_QueryDELETE ("can not purge firewall", - "DELETE LOW_PRIORITY FROM firewall" - " WHERE ClickTime