diff --git a/java/ql/src/Security/CWE/CWE-338/PredictableRandomNumberGenerator.java b/java/ql/src/Security/CWE/CWE-338/PredictableRandomNumberGenerator.java new file mode 100644 index 000000000000..d960045bea96 --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-338/PredictableRandomNumberGenerator.java @@ -0,0 +1,126 @@ +package org.jlleitschuh.bad.random; + +import org.apache.commons.lang.RandomStringUtils; // or org.apache.commons.lang3.RandomStringUtils +import org.apache.commons.text.CharacterPredicates; +import org.apache.commons.text.RandomStringGenerator; + +import java.nio.charset.Charset; +import java.security.SecureRandom; +import java.util.Random; +import java.util.UUID; +import java.util.concurrent.ThreadLocalRandom; + +public class PredictableRandomNumberGenerator { + private static final Random RANDOM = new Random(); + private static final SecureRandom SECURE_RANDOM = new SecureRandom(); + + public static String generatePasswordResetTokenJavaBad() { + byte[] array = new byte[30]; + // BAD! + RANDOM.nextBytes(array); + return new String(array, Charset.defaultCharset()); + } + + public static String generatePasswordResetTokenJavaGood() { + byte[] array = new byte[30]; + // GOOD! + SECURE_RANDOM.nextBytes(array); + return new String(array, Charset.defaultCharset()); + } + + public static String generateSecretBad() { + final char[] CHARS = "abcdefghijklmnopqrstuvwxyz".toCharArray(); + StringBuilder builder = new StringBuilder(30); + for (int i = 0; i < 30; i++) { + // BAD! + char c = CHARS[RANDOM.nextInt(CHARS.length)]; + builder.append(c); + } + return builder.toString(); + } + + public static String generateSecretGood() { + final char[] CHARS = "abcdefghijklmnopqrstuvwxyz".toCharArray(); + StringBuilder builder = new StringBuilder(30); + for (int i = 0; i < 30; i++) { + // GOOD! + char c = CHARS[SECURE_RANDOM.nextInt(CHARS.length)]; + builder.append(c); + } + return builder.toString(); + } + + + public static String generateSessionTokenJavaBad() { + // BAD! + return Long.toHexString(RANDOM.nextLong()); + } + + public static String generateSessionTokenJavaGood() { + // GOOD! + return Long.toHexString(SECURE_RANDOM.nextLong()); + } + + public static String generatePasswordResetTokenApacheCommonsBad() { + // BAD! + return RandomStringUtils.randomAscii(30); + } + + public static String generatePasswordResetTokenApacheCommonsGood() { + // GOOD! + return RandomStringUtils.random( + 30, + 32, + 127, + false, + false, + null, + SECURE_RANDOM + ); + } + + // BAD! + private static RandomStringGenerator STRING_GENERATOR = + new RandomStringGenerator.Builder() + .withinRange('0', 'z') + .filteredBy(CharacterPredicates.LETTERS, CharacterPredicates.DIGITS) + .build(); + + public static String generatePasswordResetTokenApacheTextBad() { + // GOOD! + return STRING_GENERATOR.generate(30); + } + + // GOOD! + private static RandomStringGenerator SECURE_STRING_GENERATOR = + new RandomStringGenerator.Builder() + .usingRandom(SECURE_RANDOM::nextInt) // Notice! Using Secure Random Number Generator + .withinRange('0', 'z') + .filteredBy(CharacterPredicates.LETTERS, CharacterPredicates.DIGITS) + .build(); + + public static String generatePasswordResetTokenApacheTextGood() { + // GOOD! + return SECURE_STRING_GENERATOR.generate(30); + } + + /** + * This is a real example from a vulnerability in the web server library Ratpack. + * See: CVE-2019-11808 + */ + public static UUID generateSessionTokenBad() { + // Thread local values uses a seed of a bitwise OR of System.currentTimeMillis() and System.nanoTime() + // at ThreadLocalRandom classload time. + // IE. If you know the launch time of the application, you can predict the seed. + ThreadLocalRandom random = ThreadLocalRandom.current(); + // Bad! + return new UUID(random.nextLong(), random.nextLong()); + } + + public static UUID generateSessionTokenGood() { + // GOOD! + // Uses SecureRandom internally. + return UUID.randomUUID(); + } + +} diff --git a/java/ql/src/Security/CWE/CWE-338/PredictableRandomNumberGenerator.qhelp b/java/ql/src/Security/CWE/CWE-338/PredictableRandomNumberGenerator.qhelp new file mode 100644 index 000000000000..3c86c855b2c7 --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-338/PredictableRandomNumberGenerator.qhelp @@ -0,0 +1,88 @@ + + + +

Using a Psudeorandom Number Generator (PRNG) +instead of a Cryptographically Secure Psudeorandom Number Generator (CSPRNG) +in a context requiring unpredictability, it may be possible for an attacker to +guess the next value that will be generated, and use this guess to impersonate +another user or access sensitive information. +

+ +

Example 1:

+ +

Assuming that the code uses an insecure source of randomess to generate a password reset URL.

+ +

An attacker attacker (if able to obtain their own password reset URL) can compute the value for all other password resets for other accounts, +thus allowing privilege escalation or account takeover.

+ +

Example 2:

+ +

Assuming that the code uses a java.util.concurrent.ThreadLocalRandom +as the source of randomess to generate a session token.

+ +

An attacker (if able to determine the classload time of ThreadLocalRandom (most likely at JVM launch)) +can determine the seed of the ThreadLocalRandom used by the JVM. +This would allow them to compute any secrets generated by ThreadLocalRandom.

+ +

Criticality

+ +

Depending upon the criticality of random value being used, this vulnerability can have up to a + + CVSS v3.1 base score of 9.8/10 +.

+ +
+ + +

When generating random secret data, always use java.security.SecureRandom to supply +your source of randomness.

+ +
+ + +

In the examples below, various ways of generating a random secure and insecure string of characters are demonstrated +including common uses of Apache Commons.

+ + + +
+ + +
  • + + Cloudflare Blog: Why secure systems require random numbers + +
  • +
  • + + How I Hacked Hacker News (with arc security advisory) + +
  • +
  • + Research (Hacking Apache Commons RandomStringUtils): + + The Java Soothsayer: A practical application for insecure randomness. (Includes free 0day) + +
  • + +At the time of writing almost + + 15k repositories + +are vulnerable to this due to a vulnerability discovered in the project-bootstraping code generator + + JHipster Generator + +( + + CVE-2019-16303 + +). + + + +
    +
    diff --git a/java/ql/src/Security/CWE/CWE-338/PredictableRandomNumberGenerator.ql b/java/ql/src/Security/CWE/CWE-338/PredictableRandomNumberGenerator.ql new file mode 100644 index 000000000000..97adfd349008 --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-338/PredictableRandomNumberGenerator.ql @@ -0,0 +1,98 @@ +/** + * @name Predictable Random Number Generation in Sensitive Location + * @description The use of predictable random number generation in a security sensitive location can + * can leave an application vulnerable to account takeover as future random values can + * be preditcted or computed by knowing any previous value. + * @kind path-problem + * @precision medium + * @problem.severity error + * @id java/predictable-random-number-generation + * @tags security + * external/cwe/cwe-338 + * external/cwe/cwe-330 + */ + +import java +import types +import utilities +import PredictableRandomNumberGeneratorSource +import semmle.code.java.dataflow.FlowSources +import DataFlow::PathGraph + +abstract class SecureContextSink extends DataFlow::Node { } + +private class SensitiveVariable extends Variable { + SensitiveVariable() { isSensitiveName(this.getName().toLowerCase()) } +} + +private class SensitiveMethod extends Method { + SensitiveMethod() { isSensitiveName(this.getName().toLowerCase()) } +} + +/** + * Assignment to a variable with a name that indicates the data is potentially sensitive. + */ +private class SensitiveVariableSink extends SecureContextSink { + SensitiveVariableSink() { + exists(VariableAssign va | + va.getDestVar() instanceof SensitiveVariable and va.getSource() = this.asExpr() + ) + } +} + +/** + * Insecure RNG leaks into the context of a method with potentially senisitve behaviour. + */ +private class SensitiveMethodSink extends SecureContextSink { + SensitiveMethodSink() { this.getEnclosingCallable() instanceof SensitiveMethod } +} + +class PredictableRandomConfig extends TaintTracking::Configuration { + PredictableRandomConfig() { this = "PredictableRandomConfig" } + + override predicate isSource(DataFlow::Node source) { + source instanceof PredictableRandomFlowSource + } + + override predicate isSink(DataFlow::Node sink) { sink instanceof SecureContextSink } + + private predicate testSanitizer(DataFlow::Node node) { + // TODO: Don't merge with this + // Helps get test logic out of the results + node.asExpr().getFile().getAbsolutePath().toLowerCase().matches("%test%") + } + + override predicate isSanitizerOut(DataFlow::Node node) { testSanitizer(node) } + + override predicate isSanitizerIn(DataFlow::Node node) { testSanitizer(node) } + + /** + * A char array that holds pre-aproved characters but the elements from that + * char array are selected with a random number generator that is insecure. + * + * Example: + * ``` + * Random RANDOM = new Random() + * char[] CHARS = "abcdefghijklmnopqrstuvwxyz".toCharArray(); + * // Insecurely chosen + * char c = CHARS[RANDOM.nextInt(CHARS.length)]; + * ``` + */ + private predicate isTaintedCharArrayAccess(Expr expSource, Expr expDest) { + exists(ArrayAccess aa, Array a, CharacterType charType | + a.getComponentType() = charType and + aa.getArray().(VarAccess).getVariable().getType() = a and + aa.getIndexExpr() = expSource and + aa = expDest + ) + } + + override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { + isTaintedUuidCreation(node1.asExpr(), node2.asExpr()) or + isTaintedCharArrayAccess(node1.asExpr(), node2.asExpr()) + } +} + +from DataFlow::PathNode source, DataFlow::PathNode sink, PredictableRandomConfig conf +where conf.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "Potentially predictably generated random value" diff --git a/java/ql/src/Security/CWE/CWE-338/PredictableRandomNumberGeneratorSource.qll b/java/ql/src/Security/CWE/CWE-338/PredictableRandomNumberGeneratorSource.qll new file mode 100644 index 000000000000..c7c5b88144da --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-338/PredictableRandomNumberGeneratorSource.qll @@ -0,0 +1,153 @@ +import java +import types +import StrictPredictableRandomTaintTracker +import semmle.code.java.dataflow.FlowSources +import DataFlow::PathGraph +import semmle.code.java.dataflow.FlowSources +import semmle.code.java.dataflow.TypeFlow +import semmle.code.java.dataflow.TaintTracking2 + +abstract class PredictableRandomFlowSource extends DataFlow::Node { } + +abstract class PredictableRandomMethodAccess extends MethodAccess { } + +private class PredictableApacheRandomStringUtilsMethod extends Method { + PredictableApacheRandomStringUtilsMethod() { + this.getDeclaringType() instanceof ApacheRandomStringUtilsType + } +} + +private class PredictableApacheRandomStringUtilsMethodAccess extends PredictableRandomMethodAccess { + PredictableApacheRandomStringUtilsMethodAccess() { + this.getMethod() instanceof PredictableApacheRandomStringUtilsMethod and + // The one valid use of this type that uses SecureRandom as a source of data. + not ( + this.getMethod().getName() = "random" and + not( + // this.getArgument(6).getProperExpr().getType() instanceof TypeSecureRandom or + isPredictableRandomRuntimeExp(this.getArgument(6).getProperExpr()) + ) + ) + } +} + +private predicate isPredictableRandomRuntimeExp(VarAccess varAccess) { + exists(StrictPredictableToAnyRandomConfig config | + config.hasFlowTo(DataFlow::exprNode(varAccess)) + ) +} + +/** + * Determines if this VarAccess can be safely assumed to be 'Secure' + * at runtime due to the implied type of the variable due to assignments. + */ +private predicate isSecureRuntimeExpr(VarAccess varAccess) { + /* + * TODO: Perhaps also check for multiple constructors that assign different things. + * This currently verifies that there is at least one final assignment that is 'Secure'. + * This does not verify that this is the _exclusive_ assignment to this variable. + * + * In reality, we are trying to guess at what the type will be at runtime, which is a turning + * complete problem, so we can only use this to eliminate the common cases. + */ + + exists(TypeSecureRandom typeSecureRandom | exprTypeFlow(varAccess, typeSecureRandom, false)) +} + +class PredictableJavaStdlibRandomMethodAcccess extends PredictableRandomMethodAccess { + PredictableJavaStdlibRandomMethodAcccess() { + this.getReceiverType() instanceof JavaStdlibInsecureRandomType and + not isSecureRuntimeExpr(this.getQualifier()) + } +} + +/** + * A byte array tainted by insecure RNG. + * + * Examples: + * + * ``` + * byte[] array = new byte[]; + * new Random().nextBytes(array); + * ``` + */ +private class TaintedByteArrayWrite extends VarAccess { + TaintedByteArrayWrite() { + exists(PredictableJavaStdlibRandomMethodAcccess insecureMethodAccess, MethodAccess ma | + ma.getMethod().hasName("nextBytes") and + ma = insecureMethodAccess and + ma.getArgument(0) = this + ) + } +} + +class SecureRandomMethodAccess extends MethodAccess { + SecureRandomMethodAccess() { this.getReceiverType() instanceof TypeSecureRandom } +} + +/** + * Intermediate tracking step to find instances of RandomStringGenerator that are "safely" created. + * A "safely" created RandomStringGenerator is defined as having used a SecureRandom method in the + * RandomStringGenerator.Builder::usingRandom lambda. + */ +class SafeRandomStringGeneratorFlowConfig extends TaintTracking2::Configuration { + SafeRandomStringGeneratorFlowConfig() { this = "RNG:SafeRandomStringGeneratorFlowConfig" } + + private predicate isUsingSafeUsingRandomSupplier(MethodAccess source) { + exists( + ApacheRandomStringGeneratorBuilderUsingRandomMethod usingRandomMethod, + FunctionalExpr functionalExpr, SecureRandomMethodAccess secureRandomMethodAccess + | + usingRandomMethod = source.getMethod() and + source.getArgument(0).getProperExpr() = functionalExpr and + // Some SecureRandom method is used in the scope of the functional expression + functionalExpr.asMethod() = secureRandomMethodAccess.getEnclosingCallable() + ) + } + + override predicate isSource(DataFlow::Node src) { isUsingSafeUsingRandomSupplier(src.asExpr()) } + + /** + * A sink is any RandomStringGenerator::generate method. + */ + override predicate isSink(DataFlow::Node sink) { + exists(ApacheRandomStringGeneratorType generatorType | + sink.asExpr().(MethodAccess).getMethod() = generatorType.getGenerateMethod() + ) + } + + private predicate isBuilderStep(MethodAccess previous, MethodAccess next) { + exists(ApacheRandomStringGeneratorBuilderType builderType | + builderType.getAMethod() = previous.getMethod() and + previous = next.getQualifier() + ) + } + + private predicate isGeneratorCallStep(Expr previous, MethodAccess next) { + exists(ApacheRandomStringGeneratorType generatorType | + generatorType.getGenerateMethod() = next.getMethod() and + previous = next.getQualifier() + ) + } + + override predicate isAdditionalTaintStep(DataFlow::Node previous, DataFlow::Node next) { + isBuilderStep(previous.asExpr(), next.asExpr()) or + isGeneratorCallStep(previous.asExpr(), next.asExpr()) + } +} + +private class TaintedRandomStringGeneratorMethodAccess extends PredictableRandomMethodAccess { + TaintedRandomStringGeneratorMethodAccess() { + this.getMethod().getDeclaringType() instanceof ApacheRandomStringGeneratorType and + not exists(SafeRandomStringGeneratorFlowConfig safeFlowSource | + safeFlowSource.hasFlowTo(DataFlow::exprNode(this)) + ) + } +} + +private class PredictableRandomTaintedMethodAccessSource extends PredictableRandomFlowSource { + PredictableRandomTaintedMethodAccessSource() { + this.asExpr() instanceof PredictableRandomMethodAccess // or + // this.asExpr() instanceof TaintedByteArrayWrite + } +} diff --git a/java/ql/src/Security/CWE/CWE-338/PureInsecureRandomFlowDetector.ql b/java/ql/src/Security/CWE/CWE-338/PureInsecureRandomFlowDetector.ql new file mode 100644 index 000000000000..c03355e755b1 --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-338/PureInsecureRandomFlowDetector.ql @@ -0,0 +1,59 @@ +/** + * @name Predictable Random Number Generation in Sensitive Location + * @description The use of predictable random number generation in a security sensitive location can + * can leave an application vulnerable to account takeover as future random values can + * be preditcted or computed by knowing any previous value. + * @kind path-problem + * @precision medium + * @problem.severity error + * @id java/predictable-random-number-generation + * @tags security + * external/cwe/cwe-338 + * external/cwe/cwe-330 + */ + +import java +import types +import utilities +import StrictPredictableRandomTaintTracker +import PredictableRandomNumberGeneratorSource +import semmle.code.java.dataflow.FlowSources +import DataFlow::PathGraph + +abstract class SecureContextSink extends DataFlow::Node { } + +private class SensitiveVariable extends Variable { + SensitiveVariable() { isSensitiveName(this.getName().toLowerCase()) } +} + +private class SensitiveMethod extends Method { + SensitiveMethod() { isSensitiveName(this.getName().toLowerCase()) } +} + +/** + * Assignment to a variable with a name that indicates the data is potentially sensitive. + */ +private class SensitiveVariableSink extends SecureContextSink { + SensitiveVariableSink() { + exists(VariableAssign va | + va.getDestVar() instanceof SensitiveVariable and va.getSource() = this.asExpr() + ) + } +} + +/** + * Insecure RNG leaks into the context of a method with potentially senisitve behaviour. + */ +private class SensitiveMethodSink extends SecureContextSink { + SensitiveMethodSink() { this.getEnclosingCallable() instanceof SensitiveMethod } +} + +class PredictableRandomConfig extends StrictPredictableRandomConfig { + PredictableRandomConfig() { this = "PredictableRandomConfig" } + + override predicate isSink(DataFlow::Node sink) { sink instanceof SecureContextSink } +} + +from DataFlow2::PathNode source, DataFlow2::PathNode sink, PredictableRandomConfig conf +where conf.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "Potentially predictably generated random value" diff --git a/java/ql/src/Security/CWE/CWE-338/StrictPredictableRandomTaintTracker.qll b/java/ql/src/Security/CWE/CWE-338/StrictPredictableRandomTaintTracker.qll new file mode 100644 index 000000000000..a18b155a932d --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-338/StrictPredictableRandomTaintTracker.qll @@ -0,0 +1,67 @@ +import java +import types +import utilities +import PredictableRandomNumberGeneratorSource +import semmle.code.java.dataflow.FlowSources +import DataFlow::PathGraph + +abstract class StrictPredictableRandomConfig extends TaintTracking2::Configuration { + StrictPredictableRandomConfig() { this = "StrictPredictableRandomConfig" } + + override predicate isSource(DataFlow::Node source) { + source.asExpr() instanceof JavaStdlibInsecureRandomTypeKnownCreation + } + + private predicate testSanitizer(DataFlow::Node node) { + // TODO: Don't merge with this + // Helps get test logic out of the results + node.asExpr().getFile().getAbsolutePath().toLowerCase().matches("%test%") + } + + override predicate isSanitizerOut(DataFlow::Node node) { testSanitizer(node) } + + override predicate isSanitizerIn(DataFlow::Node node) { testSanitizer(node) } + + private predicate isTaintedRandomMethodCall(Expr expSource, Expr expDest) { + exists(PredictableJavaStdlibRandomMethodAcccess ma | + ma.getMethod().hasName("nextBytes") and + ma.getArgument(0) = expDest and + ma.getQualifier() = expSource + or + ma.getQualifier() = expSource and ma = expDest + ) + } + + /** + * A char array that holds pre-aproved characters but the elements from that + * char array are selected with a random number generator that is insecure. + * + * Example: + * ``` + * Random RANDOM = new Random() + * char[] CHARS = "abcdefghijklmnopqrstuvwxyz".toCharArray(); + * // Insecurely chosen + * char c = CHARS[RANDOM.nextInt(CHARS.length)]; + * ``` + */ + private predicate isTaintedCharArrayAccess(Expr expSource, Expr expDest) { + exists(ArrayAccess aa, Array a, CharacterType charType | + a.getComponentType() = charType and + aa.getArray().(VarAccess).getVariable().getType() = a and + aa.getIndexExpr() = expSource and + aa = expDest + ) + } + + override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { + isTaintedUuidCreation(node1.asExpr(), node2.asExpr()) or + isTaintedCharArrayAccess(node1.asExpr(), node2.asExpr()) or + isTaintedRandomMethodCall(node1.asExpr(), node2.asExpr()) + } +} + +class StrictPredictableToAnyRandomConfig extends StrictPredictableRandomConfig { + StrictPredictableToAnyRandomConfig() { this = "StrictPredictableToAnyRandomConfig" } + + override predicate isSink(DataFlow::Node source) { any() } +} diff --git a/java/ql/src/Security/CWE/CWE-338/types.qll b/java/ql/src/Security/CWE/CWE-338/types.qll new file mode 100644 index 000000000000..552f3f074575 --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-338/types.qll @@ -0,0 +1,81 @@ +import java + +class UUIDCreationExp extends ClassInstanceExpr { + UUIDCreationExp() { this.getConstructedType() instanceof TypeUUID } +} + +class ApacheRandomStringUtilsType extends RefType { + ApacheRandomStringUtilsType() { + this.hasQualifiedName("org.apache.commons.lang", "RandomStringUtils") or + this.hasQualifiedName("org.apache.commons.lang3", "RandomStringUtils") + } +} + +class ApacheRandomStringGeneratorType extends RefType { + ApacheRandomStringGeneratorType() { + this.hasQualifiedName("org.apache.commons.text", "RandomStringGenerator") + } + + Method getGenerateMethod() { + exists(Method m | + m.getDeclaringType() = this and + m.hasName("generate") and + m = result + ) + } +} + +class ApacheRandomStringGeneratorBuilderType extends RefType { + ApacheRandomStringGeneratorBuilderType() { + this.hasQualifiedName("org.apache.commons.text", "RandomStringGenerator$Builder") + } +} + +class ApacheRandomStringGeneratorBuilderUsingRandomMethod extends Method { + ApacheRandomStringGeneratorBuilderUsingRandomMethod() { + this.getDeclaringType() instanceof ApacheRandomStringGeneratorBuilderType and + this.hasName("usingRandom") + } +} + +class ApacheTextRandomProviderType extends RefType { + ApacheTextRandomProviderType() { + this.hasQualifiedName("org.apache.commons.text", "TextRandomProvider") + } +} + +abstract class JavaStdlibInsecureRandomType extends RefType {} + +class ThreadLocalRandomType extends JavaStdlibInsecureRandomType { + ThreadLocalRandomType() { + this.hasQualifiedName("java.util.concurrent", "ThreadLocalRandom") + } +} + +class RandomType extends JavaStdlibInsecureRandomType { + RandomType() { + this.hasQualifiedName("java.util", "Random") + } +} + +class SplittableRandomType extends JavaStdlibInsecureRandomType { + SplittableRandomType() { + this.hasQualifiedName("java.util", "SplittableRandom") + } +} + +abstract class JavaStdlibInsecureRandomTypeKnownCreation extends Expr {} + +class ThreadLocalRandomInstanceMethodAccess extends MethodAccess, JavaStdlibInsecureRandomTypeKnownCreation { + ThreadLocalRandomInstanceMethodAccess() { + this.getMethod().hasName("current") and + this.getMethod().isStatic() and + this.getMethod().getDeclaringType() instanceof ThreadLocalRandomType + } +} + +class RandomCreationExp extends ClassInstanceExpr, JavaStdlibInsecureRandomTypeKnownCreation { + RandomCreationExp() { + this.getConstructedType() instanceof JavaStdlibInsecureRandomType + } +} diff --git a/java/ql/src/Security/CWE/CWE-338/utilities.qll b/java/ql/src/Security/CWE/CWE-338/utilities.qll new file mode 100644 index 000000000000..ea6bb4939274 --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-338/utilities.qll @@ -0,0 +1,31 @@ +import java +import types + +/** + * A 'Sensitive Name' is something that indicates that the name has some sort of security + * implication associated with it's usage. + */ +bindingset[name] +predicate isSensitiveName(string name) { + name.matches("%pass%") // also matches password + or + name.matches("%tok%") // also matches token + or + name.matches("%secret%") + or + name.matches("%reset%key%") and not name.matches("%value%") // Reduce false positive from 'keyValue' type of methods from maps + or + name.matches("%cred%") // also matches credentials + or + name.matches("%auth%") // also matches authentication + or + name.matches("%sess%id%") // also matches sessionid +} + + +/** + * A UUID created with insecure RNG will itself be tainted. + */ +predicate isTaintedUuidCreation(Expr expSource, Expr expDest) { + exists(UUIDCreationExp c | c.getAnArgument() = expSource and c = expDest) +} diff --git a/java/ql/src/semmle/code/java/JDK.qll b/java/ql/src/semmle/code/java/JDK.qll index d9a1a15e5d3d..bea5e1a282f6 100644 --- a/java/ql/src/semmle/code/java/JDK.qll +++ b/java/ql/src/semmle/code/java/JDK.qll @@ -170,6 +170,18 @@ class TypeFile extends Class { TypeFile() { this.hasQualifiedName("java.io", "File") } } +// --- Java Util --- +/** The type `java.util.UUID` */ +class TypeUUID extends Class { + TypeUUID() { this.hasQualifiedName("java.util", "UUID") } +} + +// --- Java Security -- +/** The type `java.security.SecureRandom` */ +class TypeSecureRandom extends Class { + TypeSecureRandom() { this.hasQualifiedName("java.security", "SecureRandom") } +} + // --- Standard methods --- /** * Any of the methods named `command` on class `java.lang.ProcessBuilder`. diff --git a/java/ql/src/semmle/code/java/security/Random.qll b/java/ql/src/semmle/code/java/security/Random.qll index 095b619c9c66..9bcc679e77dd 100644 --- a/java/ql/src/semmle/code/java/security/Random.qll +++ b/java/ql/src/semmle/code/java/security/Random.qll @@ -2,9 +2,7 @@ import java import semmle.code.java.dataflow.DefUse import semmle.code.java.dataflow.DataFlow -class SecureRandomNumberGenerator extends RefType { - SecureRandomNumberGenerator() { this.hasQualifiedName("java.security", "SecureRandom") } -} +class SecureRandomNumberGenerator extends TypeSecureRandom { } class GetRandomData extends MethodAccess { GetRandomData() {