CVE-2019-16303 (published 2019-09-14T00:15Z)

A class generated by the Generator in JHipster before 6.3.0 and JHipster Kotlin through 1.1.0 produces code that uses an insecure source of randomness (apache.commons.lang3 RandomStringUtils). This allows an attacker (if able to obtain their own password reset URL) to compute the value for all other password resets for other accounts, thus allowing privilege escalation or account takeover.

Severity: HIGH
CVSS v2 base score: 7.5 (AV:N/AC:L/Au:N/C:P/I:P/A:P)
CVSS v3 base score: 9.8, CRITICAL (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-338
cwe_name: Use of Cryptographically Weak Pseudo-Random Number Generator (PRNG)
description: The product uses a Pseudo-Random Number Generator (PRNG) in a security context, but the PRNG's algorithm is not cryptographically strong.
extended_description: 
            When a non-cryptographic PRNG is used in a cryptographic context, it can expose the cryptography to certain types of attacks.
            Often a pseudo-random number generator (PRNG) is not designed for cryptography. Sometimes a mediocre source of randomness is sufficient or preferable for algorithms that use random numbers. Weak generators generally take less processing power and/or do not use the precious, finite, entropy sources on a system. While such PRNGs might have very useful features, these same features could be used to break the cryptography.
         
         

Fix information:
repo_url: https://github.com/jhipster/generator-jhipster
commit_details: repo_url: https://github.com/jhipster/generator-jhipster
author: Frederik Hahne
author_date: 2019-09-13 15:11:35+02:00
msg: use new SecureRandom which uses non blocking /dev/urandom
repository: repo_url: https://github.com/jhipster/generator-jhipster
repo_name: jhipster/generator-jhipster
description: JHipster is a development platform to quickly generate, develop, & deploy modern web applications & microservice architectures.
repo_language: TypeScript
owner: jhipster
file_changes: filename: PersistentTokenRememberMeServices.java.ejs
change_type: ModificationType.MODIFY
diff: @@ -127,7 +127,7 @@ public class PersistentTokenRememberMeServices extends
                 log.debug("Refreshing persistent login token for user '{}', series '{}'", login, token.getSeries());<% if (databaseType === 'sql' || databaseType === 'mongodb' || databaseType === 'couchbase') { %>
                 token.setTokenDate(LocalDate.now());<%}%><% if (databaseType === 'cassandra') { %>
                 token.setTokenDate(new Date());<%}%>
-                token.setTokenValue(RandomUtil.getInstance().generateTokenData());
+                token.setTokenValue(RandomUtil.generateTokenData());
                 token.setIpAddress(request.getRemoteAddr());
                 token.setUserAgent(request.getHeader("User-Agent"));
                 try {
@@ -156,11 +156,11 @@ public class PersistentTokenRememberMeServices extends
         log.debug("Creating new persistent login for user {}", login);
         PersistentToken token = userRepository.findOneByLogin(login).map(u -> {
             PersistentToken t = new PersistentToken();
-            t.setSeries(RandomUtil.getInstance().generateSeriesData());<% if (databaseType === 'sql' || databaseType === 'mongodb') { %>
+            t.setSeries(RandomUtil.generateSeriesData());<% if (databaseType === 'sql' || databaseType === 'mongodb') { %>
             t.setUser(u);<% } else { %>
             t.setLogin(login);<% } %><% if (databaseType === 'cassandra') { %>
             t.setUserId(u.getId());<% } %>
-            t.setTokenValue(RandomUtil.getInstance().generateTokenData());
+            t.setTokenValue(RandomUtil.generateTokenData());
             t.setTokenDate(<% if (databaseType === 'cassandra') { %>new Date()<% } else { %>LocalDate.now()<% } %>);
             t.setIpAddress(request.getRemoteAddr());
             t.setUserAgent(request.getHeader("User-Agent"));

programming_language: TypeScript
filename: UserService.java.ejs
change_type: ModificationType.MODIFY
diff: @@ -202,7 +202,7 @@ public class UserService {
         return userRepository.findOneByEmailIgnoreCase(mail)
             .filter(<%= asEntity('User') %>::getActivated)
             .<% if (reactive) { %>flatMap<% } else { %>map<% } %>(user -> {
-                user.setResetKey(RandomUtil.getInstance().generateResetKey());
+                user.setResetKey(RandomUtil.generateResetKey());
                 user.setResetDate(Instant.now());
             <%_ if (!reactive) { _%>
                 <%_ if (databaseType === 'mongodb' || databaseType === 'couchbase' || databaseType === 'cassandra') { _%>
@@ -252,7 +252,7 @@ public class UserService {
         // new user is not active
         newUser.setActivated(false);
         // new user gets registration key
-        newUser.setActivationKey(RandomUtil.getInstance().generateActivationKey());
+        newUser.setActivationKey(RandomUtil.generateActivationKey());
         <%_ if (['sql', 'mongodb'].includes(databaseType)) { _%>
         Set<Authority> authorities = new HashSet<>();
         authorityRepository.findById(AuthoritiesConstants.USER).ifPresent(authorities::add);
@@ -310,7 +310,7 @@ public class UserService {
                 // new user is not active
                 newUser.setActivated(false);
                 // new user gets registration key
-                newUser.setActivationKey(RandomUtil.getInstance().generateActivationKey());
+                newUser.setActivationKey(RandomUtil.generateActivationKey());
                 Set<<% if (['sql', 'mongodb'].includes(databaseType)) { %>Authority<% } else { %>String<% } %>> authorities = new HashSet<>();
                 <%_ if (['sql', 'mongodb'].includes(databaseType)) { _%>
                 return authorityRepository.findById(AuthoritiesConstants.USER)
@@ -362,9 +362,9 @@ public class UserService {
         } else {
             user.setLangKey(userDTO.getLangKey());
         }
-        String encryptedPassword = passwordEncoder.encode(RandomUtil.getInstance().generatePassword());
+        String encryptedPassword = passwordEncoder.encode(RandomUtil.generatePassword());
         user.setPassword(encryptedPassword);
-        user.setResetKey(RandomUtil.getInstance().generateResetKey());
+        user.setResetKey(RandomUtil.generateResetKey());
         user.setResetDate(Instant.now());
         user.setActivated(true);
     <%_ if (!reactive) { _%>

programming_language: TypeScript
filename: RandomUtil.java.ejs
change_type: ModificationType.MODIFY
diff: @@ -30,23 +30,12 @@ public final class RandomUtil {
 
     private static final int DEF_COUNT = 20;
 
-    private static final RandomUtil INSTANCE = new RandomUtil();
-
-    public static RandomUtil getInstance() {
-        return INSTANCE;
-    }
-
-    private final SecureRandom secureRandom;
+    private static final SecureRandom secureRandom = new SecureRandom(new SecureRandom().generateSeed(128));
 
     private RandomUtil() {
-        try {
-            secureRandom = SecureRandom.getInstanceStrong();
-        } catch (NoSuchAlgorithmException e) {
-            throw new RuntimeException("Unable to find secure algorithms.", e); //NOSONAR
-        }
     }
 
-    private String generateRandomAlphanumericString() {
+    private static String generateRandomAlphanumericString() {
         return RandomStringUtils.random(DEF_COUNT, 0, 0, true, true, null, secureRandom);
     }
 
@@ -55,7 +44,7 @@ public final class RandomUtil {
      *
      * @return the generated password.
      */
-    public String generatePassword() {
+    public static String generatePassword() {
         return generateRandomAlphanumericString();
     }
 
@@ -64,7 +53,7 @@ public final class RandomUtil {
      *
      * @return the generated activation key.
      */
-    public String generateActivationKey() {
+    public static String generateActivationKey() {
         return generateRandomAlphanumericString();
     }
 
@@ -73,7 +62,7 @@ public final class RandomUtil {
      *
      * @return the generated reset key.
      */
-    public String generateResetKey() {
+    public static String generateResetKey() {
         return generateRandomAlphanumericString();
     }
     <%_ if (authenticationType === 'session' && !reactive) { _%>
@@ -84,7 +73,7 @@ public final class RandomUtil {
      *
      * @return the generated series data.
      */
-    public String generateSeriesData() {
+    public static String generateSeriesData() {
         return generateRandomAlphanumericString();
     }
 
@@ -93,7 +82,7 @@ public final class RandomUtil {
      *
      * @return the generated token data.
      */
-    public String generateTokenData() {
+    public static String generateTokenData() {
         return generateRandomAlphanumericString();
     }
     <%_ } _%>

programming_language: Java
filename: UserServiceIT.java.ejs
change_type: ModificationType.MODIFY
diff: @@ -295,7 +295,7 @@ public class UserServiceIT <% if (databaseType === 'cassandra') { %>extends Abst
     <%_ } _%>
     public void assertThatResetKeyMustNotBeOlderThan24Hours() {
         Instant daysAgo = Instant.now().minus(25, ChronoUnit.HOURS);
-        String resetKey = RandomUtil.getInstance().generateResetKey();
+        String resetKey = RandomUtil.generateResetKey();
         user.setActivated(true);
         user.setResetDate(daysAgo);
         user.setResetKey(resetKey);
@@ -329,7 +329,7 @@ public class UserServiceIT <% if (databaseType === 'cassandra') { %>extends Abst
     public void assertThatUserCanResetPassword() {
         String oldPassword = user.getPassword();
         Instant daysAgo = Instant.now().minus(2, ChronoUnit.HOURS);
-        String resetKey = RandomUtil.getInstance().generateResetKey();
+        String resetKey = RandomUtil.generateResetKey();
         user.setActivated(true);
         user.setResetDate(daysAgo);
         user.setResetKey(resetKey);

programming_language: TypeScript