33
33
*/
34
34
package fr .paris .lutece .util .http ;
35
35
36
+ import fr .paris .lutece .portal .service .util .AppPathService ;
37
+ import fr .paris .lutece .portal .service .util .AppPropertiesService ;
36
38
import fr .paris .lutece .portal .web .LocalVariables ;
37
39
import fr .paris .lutece .util .string .StringUtil ;
38
40
43
45
import java .util .Enumeration ;
44
46
45
47
import javax .servlet .http .HttpServletRequest ;
48
+ import org .springframework .util .AntPathMatcher ;
46
49
47
50
/**
48
51
* Security utils
@@ -61,6 +64,8 @@ public final class SecurityUtil
61
64
".." , "/" , "\\ "
62
65
};
63
66
67
+ public static final String PROPERTY_REDIRECT_URL_SAFE_PATTERNS = "lutece.security.redirectUrlSafePatterns" ;
68
+
64
69
// private static final String PATTERN_CLEAN_PARAMETER = "^[\\w/]+$+";
65
70
66
71
/**
@@ -266,6 +271,79 @@ public static String getRealIp( HttpServletRequest request )
266
271
267
272
return strIPAddress ;
268
273
}
274
+
275
+ /**
276
+ * Validate a forward URL to avoid open redirect
277
+ * [see isRedirectUrlSafe(String, HttpServletRequest, String)]
278
+ *
279
+ * @param strUrl
280
+ * @param request
281
+ * @return true if valid
282
+ */
283
+ public static boolean isRedirectUrlSafe ( String strUrl , HttpServletRequest request )
284
+ {
285
+ String strAntPathMatcherPatternsValues = AppPropertiesService .getProperty ( SecurityUtil .PROPERTY_REDIRECT_URL_SAFE_PATTERNS );
286
+
287
+ return isRedirectUrlSafe ( strUrl , request , strAntPathMatcherPatternsValues );
288
+ }
289
+
290
+
291
+ /**
292
+ * Validate a redirect URL to avoid open redirect.
293
+ * (Use this function only if the use of url redirect keys is not possible)
294
+ *
295
+ * the url should :
296
+ * - not be blank (null or empty string or spaces)
297
+ * - not start with "http://" or "https://" or "//" OR match the base URL or any URL in the pattern list
298
+ *
299
+ * example with a base url "https://lutece.fr/ :
300
+ * - valid : myapp/jsp/site/Portal.jsp , Another.jsp , https://lutece.fr/myapp/jsp/site/Portal.jsp
301
+ * - invalid : http://anothersite.com , https://anothersite.com , //anothersite.com , file://my.txt , ...
302
+ *
303
+ *
304
+ * @param strUrl the Url to validate
305
+ * @param request the current request (containing the baseUrl)
306
+ * @param strAntPathMatcherPatterns a comma separated list of AntPathMatcher patterns, as "http://**.lutece.com,https://**.lutece.com"
307
+ * @return true if valid
308
+ */
309
+ public static boolean isRedirectUrlSafe ( String strUrl , HttpServletRequest request , String strAntPathMatcherPatterns )
310
+ {
311
+
312
+ if ( StringUtils .isBlank ( strUrl ) ) return true ; // this is not a valid redirect Url, but it is not unsafe
313
+
314
+ // filter schemes
315
+ if ( !strUrl .startsWith ( "//" )
316
+ && !strUrl .startsWith ("http:" )
317
+ && !strUrl .startsWith ("https:" )
318
+ && !strUrl .contains ( "://" )
319
+ && !strUrl .startsWith ("javascript:" ) )
320
+ return true ; // should be a relative path
321
+
322
+ // compare with current baseUrl
323
+ if ( strUrl .startsWith ( AppPathService .getBaseUrl ( request ) ) )
324
+ return true ;
325
+
326
+ // compare with allowed url patterns
327
+ if ( !StringUtils .isBlank ( strAntPathMatcherPatterns ) )
328
+ {
329
+ AntPathMatcher pathMatcher = new AntPathMatcher ();
330
+
331
+ String [] strAntPathMatcherPatternsTab = strAntPathMatcherPatterns .split ( CONSTANT_COMMA ) ;
332
+ for ( String pattern : strAntPathMatcherPatternsTab )
333
+ {
334
+ if ( pattern != null && pathMatcher .match ( pattern , strUrl ) )
335
+ return true ;
336
+ }
337
+ }
338
+
339
+
340
+ // the Url does not match the allowed patterns
341
+ Logger logger = Logger .getLogger ( LOGGER_NAME );
342
+ logger .warn ( "SECURITY WARNING : OPEN_REDIRECT DETECTED : " + dumpRequest ( request ) );
343
+
344
+ return false ;
345
+
346
+ }
269
347
270
348
/**
271
349
* Identify user data saved in log files to prevent Log Forging attacks
0 commit comments