33
33
*/
34
34
package fr .paris .lutece .util .http ;
35
35
36
+ import fr .paris .lutece .portal .service .util .AppPathService ;
36
37
import fr .paris .lutece .portal .web .LocalVariables ;
37
38
import fr .paris .lutece .util .string .StringUtil ;
38
39
43
44
import java .util .Enumeration ;
44
45
45
46
import javax .servlet .http .HttpServletRequest ;
47
+ import org .springframework .util .AntPathMatcher ;
46
48
47
49
/**
48
50
* Security utils
@@ -54,13 +56,16 @@ public final class SecurityUtil
54
56
private static final String CONSTANT_HTTP_HEADER_X_FORWARDED_FOR = "X-Forwarded-For" ;
55
57
private static final String PATTERN_IP_ADDRESS = "^([0-9]{1,3}\\ .){3}[0-9]{1,3}$" ;
56
58
private static final String CONSTANT_COMMA = "," ;
59
+ private static final String CONSTANT_PATH_SEPARATOR = "." ;
57
60
private static final String [ ] XXE_TERMS = {
58
61
"!DOCTYPE" , "!ELEMENT" , "!ENTITY"
59
62
};
60
63
private static final String [ ] PATH_MANIPULATION = {
61
64
".." , "/" , "\\ "
62
65
};
63
66
67
+ public static final String PROPERTY_ANTPATHMATCHER_PATTERNS = "lutece.security.antpathmatcher.patterns" ;
68
+
64
69
// private static final String PATTERN_CLEAN_PARAMETER = "^[\\w/]+$+";
65
70
66
71
/**
@@ -266,6 +271,75 @@ 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
+ return isRedirectUrlSafe ( strUrl , request , null );
286
+ }
287
+
288
+
289
+ /**
290
+ * Validate a forward URL to avoid open redirect
291
+ * the url should :
292
+ * - not be blank (null or empty string or spaces)
293
+ * - not start with "http://" or "https://" or "//" OR match the base URL or any URL in the pattern list
294
+ *
295
+ * example with a base url "https://lutece.fr/ :
296
+ * - valid : myapp/jsp/site/Portal.jsp , Another.jsp , https://lutece.fr/myapp/jsp/site/Portal.jsp
297
+ * - invalid : http://anothersite.com , https://anothersite.com , //anothersite.com , file://my.txt , ...
298
+ *
299
+ *
300
+ * @param strUrl the Url to validate
301
+ * @param request the current request (containing the baseUrl)
302
+ * @param strAntPathMatcherPatterns a comma separated list of AntPathMatcher patterns, as "http://**.lutece.com,https://**.lutece.com"
303
+ * @return true if valid
304
+ */
305
+ public static boolean isRedirectUrlSafe ( String strUrl , HttpServletRequest request , String strAntPathMatcherPatterns )
306
+ {
307
+
308
+ if ( StringUtils .isBlank ( strUrl ) ) return false ;
309
+ AntPathMatcher pathMatcher = new AntPathMatcher ();
310
+ pathMatcher .setPathSeparator ( CONSTANT_PATH_SEPARATOR );
311
+
312
+ if ( !strUrl .startsWith ( "//" ) && !strUrl .startsWith ("http://" ) && !strUrl .startsWith ("https://" ) && !strUrl .startsWith ("javascript:" ) )
313
+ {
314
+ // relative path
315
+ return true ;
316
+ }
317
+ else
318
+ {
319
+ // check base urls
320
+ if ( !pathMatcher .matchStart ( strUrl , AppPathService .getBaseUrl ( request ) ) )
321
+ return true ;
322
+
323
+ if ( strAntPathMatcherPatterns != null )
324
+ {
325
+ String [] strAntPathMatcherPatternsTab = strAntPathMatcherPatterns .split ( CONSTANT_COMMA ) ;
326
+ for ( String pattern : strAntPathMatcherPatternsTab )
327
+ {
328
+ if ( pattern != null && pathMatcher .match ( pattern , strUrl ) )
329
+ return true ;
330
+ }
331
+ }
332
+
333
+
334
+ // the Url does not match the allowed patterns
335
+ Logger logger = Logger .getLogger ( LOGGER_NAME );
336
+ logger .warn ( "SECURITY WARNING : OPEN_REDIRECT DETECTED : " + dumpRequest ( request ) );
337
+
338
+ return false ;
339
+ }
340
+
341
+ }
342
+
269
343
270
344
/**
271
345
* Identify user data saved in log files to prevent Log Forging attacks
0 commit comments