|
58 | 58 | import java.nio.file.Files;
|
59 | 59 | import java.security.MessageDigest;
|
60 | 60 | import java.time.Duration;
|
61 |
| -import java.util.List; |
62 | 61 | import java.util.*;
|
| 62 | +import java.util.List; |
| 63 | +import java.util.concurrent.*; |
63 | 64 | import java.util.concurrent.atomic.AtomicInteger;
|
64 | 65 | import java.util.regex.Pattern;
|
65 | 66 | import java.util.zip.ZipEntry;
|
@@ -1285,7 +1286,44 @@ public static boolean applyJWTExtraValidation(DecodedJWT token, TokenType expect
|
1285 | 1286 | //In case of error then assume that the check failed
|
1286 | 1287 | isValid = false;
|
1287 | 1288 | }
|
1288 |
| - |
1289 | 1289 | return isValid;
|
1290 | 1290 | }
|
| 1291 | + |
| 1292 | + /** |
| 1293 | + * Apply a validations on a regular expression to ensure that is not prone to the ReDOS attack. |
| 1294 | + * <br>If your technology is supported by <a href="https://github.com/doyensec/regexploit">regexploit</a> then <b>use it instead of this method!</b> |
| 1295 | + * <br>Indeed, the <a href="https://www.doyensec.com/">doyensec</a> team has made an intensive and amazing work on this topic. |
| 1296 | + * |
| 1297 | + * @param regex String expected to be a valid regular expression. |
| 1298 | + * @param data Test data on which the regular expression is executed for the test. |
| 1299 | + * @param maximumRunningTimeInSeconds Optional parameter to specify a number of seconds above which a regex execution time is considered as not safe (default to 4 seconds when not specified). |
| 1300 | + * @return True only if the string pass all validations. |
| 1301 | + * @see "https://github.blog/security/how-to-fix-a-redos/" |
| 1302 | + * @see "https://learn.snyk.io/lesson/redos/?ecosystem=javascript" |
| 1303 | + * @see "https://rules.sonarsource.com/java/RSPEC-2631/" |
| 1304 | + * @see "https://github.com/doyensec/regexploit" |
| 1305 | + * @see "https://wiki.owasp.org/images/2/23/OWASP_IL_2009_ReDoS.pdf" |
| 1306 | + * @see "https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS" |
| 1307 | + */ |
| 1308 | + public static boolean isRegexSafe(String regex, String data, Optional<Integer> maximumRunningTimeInSeconds) { |
| 1309 | + boolean isSafe = false; |
| 1310 | + final String testData = (data != null) ? data : ""; |
| 1311 | + int executionTimeout = maximumRunningTimeInSeconds.orElse(4); |
| 1312 | + ExecutorService executor = Executors.newSingleThreadExecutor(); |
| 1313 | + try { |
| 1314 | + Callable<Boolean> task = () -> { |
| 1315 | + Pattern pattern = Pattern.compile(regex); |
| 1316 | + return pattern.matcher(testData).matches(); |
| 1317 | + }; |
| 1318 | + List<Future<Boolean>> tasks = executor.invokeAll(List.of(task), executionTimeout, TimeUnit.SECONDS); |
| 1319 | + if (!tasks.getFirst().isCancelled()) { |
| 1320 | + isSafe = true; |
| 1321 | + } |
| 1322 | + } catch (Exception e) { |
| 1323 | + isSafe = false; |
| 1324 | + } finally { |
| 1325 | + executor.shutdownNow(); |
| 1326 | + } |
| 1327 | + return isSafe; |
| 1328 | + } |
1291 | 1329 | }
|
0 commit comments