@@ -15,6 +15,7 @@ const isPi = require('detect-rpi');
15
15
const clc = require ( 'cli-color' ) ;
16
16
const util = require ( 'util' ) ;
17
17
const express = require ( 'express' ) ;
18
+ const { RateLimiterMemory } = require ( 'rate-limiter-flexible' ) ;
18
19
const bodyParser = require ( 'body-parser' ) ;
19
20
const axios = require ( 'axios' ) ;
20
21
const http = require ( 'http' ) ;
@@ -26,6 +27,21 @@ const jspack = require('jspack').jspack;
26
27
const os = require ( 'os' ) // For getting available Network interfaces on host device
27
28
const findRemoveSync = require ( 'find-remove' ) ;
28
29
30
+ //Rate limiter configurations
31
+ const maxWrongAttemptsByIPperDay = 100 ;
32
+ const maxConsecutiveFailsByUsernameAndIP = 10 ;
33
+ const limiterSlowBruteByIP = new RateLimiterMemory ( {
34
+ keyPrefix : 'login_fail_ip_per_day' ,
35
+ points : maxWrongAttemptsByIPperDay ,
36
+ duration : 60 * 60 * 24 , // Store number for 1 day since first fail
37
+ blockDuration : 60 * 60 * 24 , // Block for 1 day
38
+ } ) ;
39
+ const limiterConsecutiveFailsByUsernameAndIP = new RateLimiterMemory ( {
40
+ keyPrefix : 'login_fail_consecutive_username_and_ip' ,
41
+ points : maxConsecutiveFailsByUsernameAndIP ,
42
+ duration : 60 * 60 * 24 , // Store number for 1 day since first fail
43
+ blockDuration : 60 * 60 * 2 , // Block for 2 hours
44
+ } ) ;
29
45
30
46
//Tally Arbiter variables
31
47
const listenPort = process . env . PORT || 4455 ;
@@ -741,6 +757,22 @@ function startUp() {
741
757
});*/
742
758
}
743
759
760
+ //based on https://stackoverflow.com/a/37096512
761
+ //used in login function for displaying rate limits
762
+ function secondsToHms ( d ) {
763
+ d = Number ( d ) ;
764
+ var h = Math . floor ( d / 3600 ) ;
765
+ var m = Math . floor ( d % 3600 / 60 ) ;
766
+ var s = Math . floor ( d % 3600 % 60 ) ;
767
+
768
+ var hDisplay = h > 0 ? h + ( h == 1 ? " hour, " : " hours, " ) : "" ;
769
+ var mDisplay = m > 0 ? m + ( m == 1 ? " minute, " : " minutes, " ) : "" ;
770
+ var sDisplay = s > 0 ? s + ( s == 1 ? " second" : " seconds" ) : "" ;
771
+ hmsString = hDisplay + mDisplay + sDisplay ;
772
+ if ( hmsString . endsWith ( ", " ) ) hmsString = hmsString . slice ( 0 , - 2 ) ;
773
+ return hmsString ;
774
+ }
775
+
744
776
//sets up the REST API and GUI pages and starts the Express server that will listen for incoming requests
745
777
function initialSetup ( ) {
746
778
logger ( 'Setting up the REST API.' , 'info-quiet' ) ;
@@ -927,10 +959,39 @@ function initialSetup() {
927
959
logger ( 'Starting socket.IO Setup.' , 'info-quiet' ) ;
928
960
929
961
io . sockets . on ( 'connection' , function ( socket ) {
962
+ const ipAddr = socket . handshake . address ;
930
963
931
964
socket . on ( 'login' , function ( type , username , password ) {
932
- socket . emit ( 'login_result' , ( type === "producer" && username == username_producer && password == password_producer )
933
- || ( type === "settings" && username == username_settings && password == password_settings ) ) ;
965
+ if ( ( type === "producer" && username == username_producer && password == password_producer )
966
+ || ( type === "settings" && username == username_settings && password == password_settings ) ) {
967
+ //login successfull
968
+ socket . emit ( 'login_result' , true ) ; //old response, for compatibility with old UI clients
969
+ socket . emit ( 'login_response' , { loginOk : true , message : "" } ) ;
970
+ } else {
971
+ //wrong credentials
972
+ Promise . all ( [
973
+ limiterConsecutiveFailsByUsernameAndIP . consume ( ipAddr ) ,
974
+ limiterSlowBruteByIP . consume ( `${ username } _${ ipAddr } ` )
975
+ ] ) . then ( ( values ) => {
976
+ //rate limits not exceeded
977
+ let points = values [ 0 ] . remainingPoints ;
978
+ let message = "Wrong username or password!" ;
979
+ if ( points < 4 ) {
980
+ message += " Remaining attemps:" + points ;
981
+ }
982
+ socket . emit ( 'login_result' , false ) ; //old response, for compatibility with old UI clients
983
+ socket . emit ( 'login_response' , { loginOk : false , message : message } ) ;
984
+ } ) . catch ( ( error ) => {
985
+ //rate limits exceeded
986
+ socket . emit ( 'login_result' , false ) ; //old response, for compatibility with old UI clients
987
+ try {
988
+ retrySecs = Math . round ( error . msBeforeNext / 1000 ) || 1 ;
989
+ } catch ( e ) {
990
+ retrySecs = Math . round ( error [ 0 ] . msBeforeNext / 1000 ) || 1 ;
991
+ }
992
+ socket . emit ( 'login_response' , { loginOk : false , message : "Too many attemps! Please try " + secondsToHms ( retrySecs ) + " later." } ) ;
993
+ } ) ;
994
+ }
934
995
} ) ;
935
996
936
997
socket . on ( 'version' , function ( ) {
0 commit comments