· 6 years ago · Jan 28, 2020, 02:16 PM
1package lu.excellium.trainingkit.util;
2
3import com.auth0.jwt.JWT;
4import com.auth0.jwt.JWTVerifier;
5import com.auth0.jwt.algorithms.Algorithm;
6import com.auth0.jwt.exceptions.JWTVerificationException;
7import com.auth0.jwt.interfaces.DecodedJWT;
8import net.sf.ehcache.Cache;
9import net.sf.ehcache.CacheManager;
10import net.sf.ehcache.Element;
11import org.slf4j.Logger;
12import org.slf4j.LoggerFactory;
13
14import javax.servlet.http.HttpServletResponseWrapper;
15import javax.xml.bind.DatatypeConverter;
16import java.security.MessageDigest;
17import java.security.NoSuchAlgorithmException;
18import java.security.SecureRandom;
19import java.util.Calendar;
20
21/**
22 * Class in charge of all operations related to the security of the application.
23 * Must be implemented by the student in order to handle the different identified abuse cases.
24 */
25public abstract class SecurityUtils {
26
27 /**
28 * Logger for the security event tracing
29 */
30 private static final Logger TRACE = LoggerFactory.getLogger(SecurityUtils.class);
31
32 private static final SecureRandom RND = new SecureRandom();
33
34 /**
35 * Cache used to manage IP brute force protection
36 */
37 public static final Cache BANNED_IP;
38
39 /**
40 * Cache used to manage access token flagged as no more usable due to logout
41 */
42 public static final Cache BANNED_ACCESS_TOKEN;
43
44 static {
45 CacheManager singletonManager = CacheManager.create();
46 Cache memoryOnlyCacheForIP = new Cache("IPCache", 1000, true, false, 600, 600);
47 singletonManager.addCache(memoryOnlyCacheForIP);
48 BANNED_IP = singletonManager.getCache("IPCache");
49
50 Cache memoryOnlyCacheForAT = new Cache("ATCache", 1000, true, false, 7200, 7200);
51 singletonManager.addCache(memoryOnlyCacheForAT);
52 BANNED_ACCESS_TOKEN = singletonManager.getCache("ATCache");
53 }
54
55 /****************[SECTION FOR ABUSE CASE 1 HANDLING]**************/
56
57 /**
58 * Apply input validation on the user login
59 *
60 * @param login User login
61 * @throws SecurityException Exception raised if the validation failed
62 */
63 public static void inputValidationForUserCredential(String login) throws SecurityException {
64 //REGEX: [a-zA-Z]{1,20}
65 //See https://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html
66 //TODO: Implement the method
67 }
68
69 /**
70 * Apply input validation on a tweet body
71 *
72 * @param tweetBody Content of the tweet
73 * @throws SecurityException Exception raised if the validation failed
74 */
75 public static void inputValidationForTweetBody(String tweetBody) throws SecurityException {
76 //REGEX: [a-zA-Z0-9\s]{1,140}
77 //See https://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html
78 //TODO: Implement the method
79 }
80
81 /**
82 * Apply input validation on a tweet ID
83 *
84 * @param tweetId Identifier of the tweet
85 * @throws SecurityException Exception raised if the validation failed
86 */
87 public static void inputValidationForTweetId(String tweetId) throws SecurityException {
88 //REGEX: [0-9a-z]{32}
89 //See https://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html
90 //TODO: Implement the method
91 }
92
93
94 /****************[SECTION FOR ABUSE CASE 2 HANDLING]**************/
95
96 /**
97 * Generate the "hashed" version of the password (in fact is a derivated key used as a hash)
98 *
99 * @param pwd User password provided
100 * @param pwdSalt Salt associated with the password
101 * @return The password hashed using the PBKDF2 algorithm and encoded in HEX
102 * @throws SecurityException Exception raised if the generation failed
103 */
104 public static String hashPassword(String pwd, byte[] pwdSalt) throws SecurityException {
105 //TIP:
106 // Use PBKDF2 algorithm with:
107 //- Algorithm “PBKDF2WithHmacSHA512”
108 //- 1000000 iterations
109 //- Provided pepper in the method (32 bytes)
110 //- Unique random salt by password provided (32 bytes)
111 //- 256 bits output key size
112 //- The computing must take at least 2 seconds to process
113 //- Salt[64] used for password hashing = Pepper[32] + PwdSalt[32]
114 // - The computing must take at least 2 seconds to process
115 //See https://wiki.owasp.org/index.php/Hashing_Java for code
116 //TODO: Implement the method
117 byte[] pepper = System.getProperty("pbkdf2.pepper").getBytes();
118
119 //Hex encoding
120 //String hashToReturn = DatatypeConverter.printHexBinary(hashComputed.getBytes());
121 return pwd;
122 }
123
124 /****************[SECTION FOR ABUSE CASE 3 HANDLING]**************/
125
126 /**
127 * Generate a JWT token that will be used as access token for the user
128 *
129 * @param login User login
130 * @param role Access role
131 * @param userAgent User agent used by the user to access to the application
132 * @param userIPAddress User IP address
133 * @return the access token as JWT token
134 * @throws SecurityException Exception raised if the generation failed
135 */
136 public static String generateAccessToken(String login, String role, String userAgent, String userIPAddress) throws SecurityException {
137 String accessToken;
138
139 //Generate the fingerprint using the user agent, a salt and the user ip address
140 String value = userAgent + System.getProperty("salt") + userIPAddress;
141 String fingerprint = "";
142 try {
143 fingerprint = DatatypeConverter.printHexBinary(MessageDigest.getInstance("sha-256").digest(value.getBytes()));
144 } catch (NoSuchAlgorithmException nse) {
145 throw new SecurityException(nse);
146 }
147
148 //TODO: Add the fingerprint claim to the JWT token
149 //See https://jwt.io/#debugger-io to see the content of a token
150
151 //Build the access token
152 Algorithm algorithm = Algorithm.HMAC256(System.getProperty("jwt.secret").getBytes());
153 Calendar c = Calendar.getInstance();
154 c.add(Calendar.MINUTE, 60);
155 accessToken = JWT.create().withIssuer("CODE2").withSubject(login).withExpiresAt(c.getTime()).withClaim("role", role.trim()).sign(algorithm);
156
157
158 return accessToken;
159 }
160
161 /****************[SECTION FOR ABUSE CASE 4 HANDLING]**************/
162
163 /**
164 * Validate a received JWT token, the user context and check that the user role is equals to one expected
165 *
166 * @param accessToken Access token to validate
167 * @param expectedRole Expected user role that the token must contains
168 * @return The user login if the token is valid
169 * @throws SecurityException Exception raised if the verification failed (invalid token / role not equals / user context fingerprint do not match)
170 */
171 public static String validateAccessToken(String accessToken, String expectedRole, String userAgent, String userIPAddress) throws SecurityException {
172
173 //Generate the fingerprint using the user agent, a salt and the user ip address
174 String value = userAgent + System.getProperty("salt") + userIPAddress;
175 String fingerprint = "";
176 try {
177 fingerprint = DatatypeConverter.printHexBinary(MessageDigest.getInstance("sha-256").digest(value.getBytes()));
178 } catch (NoSuchAlgorithmException nse) {
179 throw new SecurityException(nse);
180 }
181
182 //TODO: Add the role and the fingerprint to the claim to verify in the JWT token verifier
183 //See https://jwt.io/#debugger-io to see the content of a token
184
185 //Validate the token
186 Algorithm algorithm = Algorithm.HMAC256(System.getProperty("jwt.secret").getBytes());
187 JWTVerifier verifier = JWT.require(algorithm).withIssuer("CODE2").build();
188 DecodedJWT token;
189 try {
190 token = verifier.verify(accessToken);
191 } catch (JWTVerificationException ve) {
192 throw new SecurityException(ve);
193 }
194
195 return token.getSubject();
196 }
197
198 /****************[SECTION FOR ABUSE CASE 5 HANDLING]**************/
199
200 /**
201 * Increment the login failed attempt counter for the user and flag the source IP has must be blocked or not.
202 *
203 * @param userIPAddress User source IP address
204 * @param login User login
205 * @throws SecurityException Exception raised if the verification failed
206 */
207 public static void checkAntiBruteForceAccess(String userIPAddress, String login) throws SecurityException {
208 String keyInCacheToAddToEnableIPBanning = (userIPAddress + "_BANNED").toUpperCase();
209 String keyInCacheToStoreTheCounter = (login + "_" + userIPAddress).toUpperCase();
210 Element ipBanFlagToAddInCacheToEnableIPBanning = new Element(keyInCacheToAddToEnableIPBanning, keyInCacheToAddToEnableIPBanning);
211 //TODO: implements the method using the BANNED_IP cache reference item to store the counter and enable or not the IP ban flag
212 }
213
214
215 /****************[SECTION FOR ABUSE CASE 6 HANDLING]**************/
216
217 /**
218 * Send a trace to logging system about a security event (do not forget to escape dangerous content)
219 *
220 * @param information Informations about the event
221 * @throws SecurityException Exception raised if the tracing failed
222 */
223 public static void logSecurityEvent(String... information) throws SecurityException {
224 //TODO: Implement the method using logback logger TRACE instance to send trace
225 System.out.println(information[0]);
226 }
227
228 /**
229 * Apply output escaping on a value
230 *
231 * @param unsafeInput Unsafe input value to escape
232 * @return The input value escaped
233 * @throws SecurityException Exception raised if the escaping failed
234 */
235 public static String escapeContent(String unsafeInput) throws SecurityException {
236 //TIP: Use OWASP Encoder API here (already available in classpath)...
237 //See https://wiki.owasp.org/index.php/OWASP_Java_Encoder_Project#tab=Use_the_Java_Encoder_Project
238 //TODO: Implement the method
239 return unsafeInput;
240 }
241
242 /****************[SECTION FOR ABUSE CASE 7 HANDLING]**************/
243 /**
244 * Define the caching policy of all HTTP responses in order to prevent that any devices store information in cache using the headers “Cache-Control” + “Pragma” + “Expires”
245 * @param httpResponse Ref on HTTP response object
246 * @throws SecurityException Exception raised if the caching policy cannot be set
247 */
248 public static void defineCachingPolicy(HttpServletResponseWrapper httpResponse) throws SecurityException {
249 //See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
250 //See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Pragma
251 //See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expires
252 //TODO: Add the headers with the correct value
253 }
254
255
256 /****************[SECTION FOR ABUSE CASE 8 HANDLING]**************/
257 /**
258 * Define the browser behavior policy using a Content-Security-Policy HTTP response header that:
259 * - Allow only the loading of information from the current domain or https//code.jquery.com or https://maxcdn.bootstrapcdn.com
260 * - Allow inline script for JavaScript
261 * - Allow inline style for CSS
262 * @param httpResponse Ref on HTTP response object
263 * @throws SecurityException Exception raised if the behavior policy cannot be set
264 */
265 public static void defineBrowserBehaviorPolicy(HttpServletResponseWrapper httpResponse) throws SecurityException {
266 //See https://content-security-policy.com/
267 //TODO: Add the header with the correct policy
268 }
269
270}