Repository navigation
Expand file tree
/
Copy pathValidate.java
More file actions
516 lines (484 loc) · 17 KB
/
Copy pathValidate.java
File metadata and controls
516 lines (484 loc) · 17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
package utils;
import jakarta.mail.internet.AddressException;
import jakarta.mail.internet.InternetAddress;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpSession;
import java.math.BigInteger;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* Class is used to validate various inputs <br>
* <br>
* This file is part of the Security Shepherd Project.
*
* <p>The Security Shepherd project is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software Foundation, either
* version 3 of the License, or (at your option) any later version.<br>
*
* <p>The Security Shepherd project is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.<br>
*
* <p>You should have received a copy of the GNU General Public License along with the Security
* Shepherd project. If not, see <http://www.gnu.org/licenses/>.
*
* @author Mark Denihan
*/
public class Validate {
private static final Logger log = LogManager.getLogger(Validate.class);
/**
* Finds JSession token from user's cookies[], validates and returns.
*
* @param userCookies Cookies from users browser
* @return JSession Id
*/
public static Cookie getSessionId(Cookie[] userCookies) {
int i = 0;
Cookie theSessionId = null;
for (i = 0; i < userCookies.length; i++) {
if (userCookies[i].getName().compareTo("JSESSIONID") == 0) {
theSessionId = userCookies[i];
break; // End Loop, because we found the theSessionId
}
}
return theSessionId;
}
/**
* Finds CSRF token from user's cookies[], validates.
*
* @param userCookies All of the user's cookies from their browser
* @return csrfCookie
*/
public static Cookie getToken(Cookie[] userCookies) {
int i = 0;
Cookie theToken = null;
for (i = 0; i < userCookies.length; i++) {
if (userCookies[i].getName().compareTo("token") == 0) {
theToken = userCookies[i];
break; // End Loop, because we found the token
}
}
if (theToken != null) {
// log.debug("Found Cookie " + theToken.getName() + " with value " +
// theToken.getValue());
// The Token is currently designed to be a random Big Integer. If the Big
// Integer Case does not work, the token has been modified. Potentially in a
// malicious manner
try {
BigInteger theTokenCasted = new BigInteger(theToken.getValue());
BigInteger tenGrand = new BigInteger("10000");
BigInteger tenGrandNeg = new BigInteger("-10000");
if (!(theTokenCasted.compareTo(tenGrand) > 0
|| theTokenCasted.compareTo(tenGrandNeg) < 0)) {
log.error("CSRF Cookie Token was modified in some manor!");
theToken = null;
}
} catch (Exception e) {
log.error("CSRF Cookie Token was modified in some manor: " + e.toString());
theToken = null;
}
}
return theToken;
}
/**
* Validates class year when creating classes. Class year should be YY/YY, e.g. 11/12. So the
* first year must be less than the second.
*
* @param classYear Class Year in YY/YY format, e.g. 11/12.
* @return Boolean value stating weather or not these supplied attributes make a valid class year
*/
public static boolean isValidClassYear(String classYear) {
boolean result = false;
result = classYear.length() == 4;
if (result) {
try {
result = Integer.parseInt(classYear) > 2010;
} catch (NumberFormatException e) {
log.error("Could not parse classYear " + classYear);
result = false;
throw new RuntimeException(e);
}
}
return result;
}
/**
* Email validation
*
* @param email
* @return Boolean reflect email validity
*/
public static boolean isValidEmailAddress(String email) {
boolean result = true;
try {
log.debug("Validating email");
InternetAddress emailAddr = new InternetAddress(email);
log.debug("Did we crash");
emailAddr.validate();
log.debug("Didn't crash");
} catch (AddressException ex) {
result = false;
}
return result;
}
/**
* Invalid password detecter
*
* @param passWord
* @return
*/
public static boolean isValidPassword(String passWord) {
boolean result = false;
result = passWord.length() > 7 && passWord.length() <= 512;
if (!result) {
log.debug("Invalid Password detected");
}
return result;
}
/**
* Used to validate user creation requests
*
* @param userName User Name
* @param passWord User Password
* @return Boolean value stating weather or not these supplied attributes make a valid user
*/
public static boolean isValidUser(String userName, String passWord) {
int userLength = userName.length();
int passLength = passWord.length();
boolean userOK = userLength > 2 && userLength <= 32;
boolean passOK = passLength > 7 && passLength <= 512;
boolean result = userOK && passOK;
if (!result) {
log.debug("Invalid Data detected in Validate.isValidUser()");
}
return result;
}
/**
* Used to validate user creation requests
*
* @param userName User Name
* @param passWord User Password
* @param userAddress User address
* @return Boolean value stating weather or not these supplied attributes make a valid user
*/
public static boolean isValidUser(String userName, String passWord, String userAddress) {
boolean result = false;
result =
userName.length() > 2
&& passWord.length() >= 8
&& userName.length() <= 32
&& passWord.length() <= 512
&& userAddress.length() <= 128;
if (!result) {
log.debug("Invalid Data detected in Validate.isValidUser()");
}
return result;
}
/**
* Quick method to prevent data and javascript URLs
*
* @param theUrl
* @return
*/
public static String makeValidUrl(String theUrl) {
theUrl = theUrl.toLowerCase();
if (!theUrl.startsWith("http")) {
theUrl = "http" + theUrl;
log.debug("Transformed to: " + theUrl);
}
return theUrl;
}
/**
* Session is checked for credentials and ensures that they have not been modified and that they
* are valid for an administrator
*
* @param ses HttpSession from users browser
* @return Boolean value that reflects the validity of the admins session
*/
public static boolean validateAdminSession(HttpSession ses) {
boolean result = false;
String userName = new String();
if (ses == null) {
log.debug("No Session Found");
} else {
if (ses.getAttribute("logout") != null) {
log.debug("Logout Attribute Found: Invalidating session...");
ses.invalidate(); // make servlet engine forget the session
} else {
// log.debug("Active Session Found");
if (ses.getAttribute("userRole") != null && ses.getAttribute("userName") != null) {
try {
userName = (String) ses.getAttribute("userName");
// log.debug("Session holder is " + userName);
String role = (String) ses.getAttribute("userRole");
result = (role.compareTo("admin") == 0);
if (!result) {
log.fatal(
"User " + userName + " Attempting Admin functions! (CSRF Tokens Not Checked)");
}
} catch (Exception e) {
log.fatal("Tampered Parameter Detected!!! Could not parameters");
}
} else {
log.debug("Session has no credentials");
}
}
}
return result;
}
/**
* Session is checked for credentials and ensures that they have not been modified and that they
* are valid for an administrator. This function also validates CSRF tokens
*
* @param ses HttpSession from users browser
* @return Boolean value that reflects the validity of the admins session
*/
public static boolean validateAdminSession(
HttpSession ses, Cookie cookieToken, Object requestToken) {
boolean result = false;
String userName = new String();
if (ses == null) {
log.debug("No Session Found");
} else {
if (ses.getAttribute("logout") != null) {
log.debug("Logout Attribute Found: Invalidating session...");
ses.invalidate(); // make servlet engine forget the session
} else {
// log.debug("Active Session Found");
if (ses.getAttribute("userRole") != null && ses.getAttribute("userName") != null) {
try {
userName = (String) ses.getAttribute("userName");
// log.debug("Session holder is " + userName);
String role = (String) ses.getAttribute("userRole");
result = (role.compareTo("admin") == 0);
if (!result) {
// Check CSRF Tokens of User to ensure they are not being CSRF'd into causing
// Unauthorised Access Alert
boolean validCsrfTokens = validateTokens(cookieToken, requestToken);
if (validCsrfTokens) {
log.fatal(
"User account "
+ userName
+ " Attempting Admin functions! (With Valid CSRF Tokens)");
} else {
log.error(
"User account " + userName + " accessing admin function with bad CSRF Tokens");
}
}
} catch (Exception e) {
log.fatal("Tampered Parameter Detected!!! Could not parameters");
}
} else {
log.debug("Session has no credentials");
}
}
}
return result;
}
/**
* Takes a String submitted to be used to encrypt and makes it the correct length for an
* encryption key
*
* @param userSalt String to be validated
* @return A Valid Encryption Key based on the input
*/
public static String validateEncryptionKey(String userSalt) {
String newKey = new String();
int keySize = userSalt.length();
if (keySize == 16) {
// log.debug("Key Already Valid");
newKey = userSalt;
} else {
if (keySize > 16) {
// log.debug("Key too Long...");
newKey = userSalt.substring(0, 16);
} else // Shorter than 16
{
// log.debug("Key too Short...");
newKey = userSalt;
int howManyTimes = (16 / keySize) - 1;
// log.debug("Repeating String " + howManyTimes + " times");
for (int i = 0; i < howManyTimes; i++) {
newKey += userSalt;
}
keySize = newKey.length();
int toAdd = 16 - keySize;
// log.debug("Adding " + toAdd + " more characters");
newKey = newKey.concat(userSalt.substring(0, toAdd));
}
}
log.debug("Encryption key is '" + newKey + "'");
return newKey;
}
/**
* Function that will check if a valid language is set. if not, returns en (English)
*
* @param ses Session Language Parameter
* @return en by default, or the valid setting found in the submitted lang
*/
public static String validateLanguage(HttpSession ses) {
String result = "en_GB";
String lang = new String();
try {
lang = ses.getAttribute("lang").toString();
} catch (NullPointerException e) {
lang = "";
}
// log.debug("lang submitted: " + lang);
if (lang != null) {
if (lang.matches(".[a-z]{2}-[A-Z]{2}$")) {
result = lang;
}
}
// log.debug("lang set to: " + result);
return result;
}
/**
* Validates objects received through a function request. Also ensures max length is not too high.
*
* @param input Object to validate
* @param maxLength Maximum length of object
* @return Validated String value or empty string value
*/
public static String validateParameter(Object input, int maxLength) {
String result = new String();
if (input == null) {
result = new String();
} else {
result = (String) input;
if (result.length() > maxLength) {
log.debug("Parameter Too Long: " + result.length() + " characters");
log.debug("Parameter Was: " + result);
result = new String();
}
}
return result;
}
/**
* Session is checked for credentials and ensures that they have not been modified and that they
* are valid
*
* @param ses HttpSession from users browser
* @return Boolean value that reflects the validity of the users session
*/
public static boolean validateSession(HttpSession ses) {
boolean result = false;
if (ses == null) {
log.debug("No Session Found");
} else {
if (ses.getAttribute("logout") != null) {
log.debug("Logout Attribute Found: Invalidating session...");
ses.invalidate(); // make servlet engine forget the session
} else {
// log.debug("Active Session Found");
if (ses.getAttribute("userRole") != null) {
try {
// log.debug("Session holder is "+ses.getAttribute("userName").toString());
String role = (String) ses.getAttribute("userRole");
result = (role.compareTo("player") == 0 || role.compareTo("admin") == 0);
if (!result) {
log.fatal("User Role Parameter Tampered. Role = " + role);
} else {
String userName = ses.getAttribute("userName").toString();
// Has the user been suspended? Should they be kicked?
if (UserKicker.shouldKickUser(userName)) {
log.debug(
userName
+ " has been Suspended. Invalidating Session and Reporting Invalid"
+ " Session");
ses.invalidate(); // Killing Session
result = false; // User will not access function they were attempting to call
UserKicker.removeFromKicklist(userName); // Removing from kick list, as they are now
// authenticated, the DB Layer Suspension
// will prevent them from signing in
}
}
} catch (Exception e) {
log.fatal("Tampered Parameter Detected!!! Could not Decrypt stamp");
}
} else {
log.debug("Session has no credentials");
}
}
}
return result;
}
/**
* This method compares the two submitted tokens after ensuring they are not null and not empty.
*
* @param cookieToken CSRF cookie Token
* @param requestToken CSRF request Token
* @return A boolean value stating weather or not the tokens are valid
*/
public static boolean validateTokens(Cookie cookieToken, Object requestToken) {
boolean result = false;
boolean cookieNull = (cookieToken == null);
boolean requestNull = (requestToken == null);
if (!cookieNull && !requestNull) {
String theRequest = (String) requestToken;
String theCookie = cookieToken.getValue();
boolean cookieEmpty = theCookie.isEmpty();
boolean requestEmpty = theRequest.isEmpty();
if (!cookieEmpty && !requestEmpty) {
result = theRequest.compareTo(theCookie) == 0;
} else if (cookieEmpty) {
log.error("Cookie Token Empty");
} else if (requestEmpty) {
log.error("Request Token Empty");
}
if (!result) {
log.error("CSRF Tokens did not match");
}
} else {
if (cookieNull) {
log.error("Cookie Token was Null");
} else if (requestNull) {
log.error("Request Token was Null");
}
}
return result;
}
/**
* Validates file name attributes to defend against path traversal
*
* @param fileName File name to validate
* @return Boolean value reflecting if valid or not
*/
/*
* public static String validateFileName(String fileName) {
* ShepherdLogManager.logEvent(request.getRemoteAddr(),
* request.getHeader("X-Forwarded-For"), "fileName: " + fileName); fileName =
* fileName.replaceAll(" ", "").replaceAll("\\.", "").replaceAll("/",
* "").replaceAll("\\\\", "").replaceAll("\n", "");
* ShepherdLogManager.logEvent(request.getRemoteAddr(),
* request.getHeader("X-Forwarded-For"), "fileName: " + fileName); return
* fileName; }
*/
public static boolean validHostUrl(String hostUrl) {
// TODO - Pull other validation steps into this
boolean result;
result = hostUrl.endsWith("/");
if (!result) {
log.error("URL Doesn't end with a forward slash. Very likely wrong");
}
return result;
}
/**
* Validates that a port number supplied is a valid port number
*
* @param portNum String to validate
* @return Boolean value reflecting if valid or not
*/
public static boolean isValidPortNumber(String portNum) {
try {
Integer validPort = Integer.valueOf(portNum);
if (validPort < 1 || validPort > 65535) {
log.fatal("Value: " + portNum + "is not a valid port number");
return false;
}
} catch (NumberFormatException e) {
log.fatal("Value: " + portNum + "is not a valid port number");
return false;
}
return true;
}
}