Skip to content

Commit 1784c20

Browse files
committed
Clean up the query
1 parent a03e6fa commit 1784c20

File tree

2 files changed

+26
-41
lines changed

2 files changed

+26
-41
lines changed

java/ql/src/experimental/Security/CWE/CWE-759/HashWithoutSalt.ql

Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class MessageDigest extends RefType {
1717
MessageDigest() { this.hasQualifiedName("java.security", "MessageDigest") }
1818
}
1919

20+
/** The method call `MessageDigest.getInstance(...)` */
2021
class MDConstructor extends StaticMethodAccess {
2122
MDConstructor() {
2223
exists(Method m | m = this.getMethod() |
@@ -57,46 +58,35 @@ class MDHashMethodAccess extends MethodAccess {
5758
string getPasswordRegex() { result = "(?i).*pass(wd|word|code|phrase).*" }
5859

5960
/** Finds variables that hold password information judging by their names. */
60-
class PasswordVarExpr extends Expr {
61+
class PasswordVarExpr extends VarAccess {
6162
PasswordVarExpr() {
62-
this.(VarAccess).getVariable().getName().toLowerCase().regexpMatch(getPasswordRegex()) and
63-
not this.(VarAccess).getVariable().getName().toLowerCase().matches("%hash%") // Exclude variable names such as `passwordHash` since their values were already hashed
63+
exists(string name | name = this.getVariable().getName().toLowerCase() |
64+
name.regexpMatch(getPasswordRegex()) and not name.matches("%hash%") // Exclude variable names such as `passwordHash` since their values were already hashed
65+
)
6466
}
6567
}
6668

6769
/** Holds if `Expr` e is a direct or indirect operand of `ae`. */
68-
predicate hasAddExpr(AddExpr ae, Expr e) { ae.getAnOperand+() = e }
70+
predicate hasAddExprAncestor(AddExpr ae, Expr e) { ae.getAnOperand+() = e }
6971

70-
/** Holds if `MethodAccess` ma has a flow to another `MDHashMethodAccess` call. */
71-
predicate hasAnotherHashCall(MethodAccess ma) {
72-
exists(MDHashMethodAccess ma2, DataFlow::Node node1, DataFlow::Node node2 |
72+
/** Holds if `MDHashMethodAccess ma` is a second `MDHashMethodAccess` call by the same object. */
73+
predicate hasAnotherHashCall(MDHashMethodAccess ma) {
74+
exists(MDHashMethodAccess ma2 |
7375
ma2 != ma and
74-
node1.asExpr() = ma.getAChildExpr() and
75-
node2.asExpr() = ma2.getAChildExpr() and
76-
(
77-
TaintTracking2::localTaint(node1, node2) or
78-
TaintTracking2::localTaint(node2, node1)
79-
)
76+
ma2.getQualifier() = ma.getQualifier().(VarAccess).getVariable().getAnAccess()
8077
)
8178
}
8279

8380
/** Holds if `MethodAccess` ma is a hashing call without a sibling node making another hashing call. */
8481
predicate isSingleHashMethodCall(MDHashMethodAccess ma) { not hasAnotherHashCall(ma) }
8582

8683
/** Holds if `MethodAccess` ma is invoked by `MethodAccess` ma2 either directly or indirectly. */
87-
predicate hasParentCall(MethodAccess ma2, MethodAccess ma) {
88-
ma.getCaller() = ma2.getMethod()
89-
or
90-
exists(MethodAccess ma3 |
91-
ma.getCaller() = ma3.getMethod() and
92-
hasParentCall(ma2, ma3)
93-
)
94-
}
84+
predicate hasParentCall(MethodAccess ma2, MethodAccess ma) { ma.getCaller() = ma2.getMethod() }
9585

9686
/** Holds if `MethodAccess` is a single hashing call that is not invoked by a wrapper method. */
9787
predicate isSink(MethodAccess ma) {
9888
isSingleHashMethodCall(ma) and
99-
not exists(MethodAccess ma2 | hasParentCall(ma2, ma)) // Not invoked by a wrapper method which could invoke MDHashMethod in another call stack to reduce FPs
89+
not hasParentCall(_, ma) // Not invoked by a wrapper method which could invoke MDHashMethod in another call stack. This reduces FPs.
10090
}
10191

10292
/** Sink of hashing calls. */
@@ -131,9 +121,9 @@ class HashWithoutSaltConfiguration extends TaintTracking::Configuration {
131121
ma.getArgument(0) = node.asExpr()
132122
) // System.arraycopy(password.getBytes(), ...)
133123
or
134-
exists(AddExpr e | hasAddExpr(e, node.asExpr())) // password+salt
124+
exists(AddExpr e | hasAddExprAncestor(e, node.asExpr())) // password+salt
135125
or
136-
exists(ConditionalExpr ce | ce = node.asExpr()) // useSalt?password+":"+salt:password
126+
exists(ConditionalExpr ce | ce.getAChildExpr() = node.asExpr()) // useSalt?password+":"+salt:password
137127
or
138128
exists(MethodAccess ma |
139129
ma.getMethod().getDeclaringType().hasQualifiedName("java.lang", "StringBuilder") and
@@ -143,7 +133,7 @@ class HashWithoutSaltConfiguration extends TaintTracking::Configuration {
143133
or
144134
exists(MethodAccess ma |
145135
ma.getQualifier().(VarAccess).getVariable().getType() instanceof Interface and
146-
ma.getAnArgument() = node.asExpr() // Method access of interface type variables requires runtime determination
136+
ma.getAnArgument() = node.asExpr() // Method access of interface type variables requires runtime determination thus not handled
147137
)
148138
}
149139
}

java/ql/test/experimental/query-tests/security/CWE-759/HashWithoutSalt.java

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,16 @@ public String getSHA256Hash3(String password, byte[] salt) throws NoSuchAlgorith
5454

5555
// GOOD - Hash with a given salt stored somewhere else.
5656
public String getSHA256Hash(String password, String salt) throws NoSuchAlgorithmException {
57-
return hash(password+":"+salt);
57+
MessageDigest alg = MessageDigest.getInstance("SHA-256");
58+
String payload = password+":"+salt;
59+
return Base64.getEncoder().encodeToString(alg.digest(payload.getBytes(java.nio.charset.StandardCharsets.UTF_8)));
5860
}
5961

6062
// GOOD - Hash with a given salt stored somewhere else.
6163
public String getSHA256Hash2(String password, String salt, boolean useSalt) throws NoSuchAlgorithmException {
62-
return hash(useSalt?password+":"+salt:password);
64+
MessageDigest alg = MessageDigest.getInstance("SHA-256");
65+
String payload = useSalt?password+":"+salt:password;
66+
return Base64.getEncoder().encodeToString(alg.digest(payload.getBytes(java.nio.charset.StandardCharsets.UTF_8)));
6367
}
6468

6569
// GOOD - Hash with a salt for a variable named passwordHash, whose value is a hash used as an input for a hashing function.
@@ -73,10 +77,6 @@ public void update(SHA256 sha256, byte[] foo, int start, int len) throws NoSuchA
7377
sha256.update(foo, start, len);
7478
}
7579

76-
public void update2(SHA256 sha256, byte[] foo, int start, int len) throws NoSuchAlgorithmException {
77-
sha256.update(foo, start, len);
78-
}
79-
8080
// BAD - Invoking a wrapper implementation without a salt is not detected.
8181
public String getSHA256Hash4(String password) throws NoSuchAlgorithmException, ClassNotFoundException, IllegalAccessException, InstantiationException {
8282
SHA256 sha256 = new SHA256();
@@ -98,15 +98,15 @@ public String getSHA256Hash5(String password) throws NoSuchAlgorithmException {
9898
}
9999

100100
// BAD - Invoking a wrapper implementation without a salt is not detected.
101-
public String getSHA256Hash6(String password) throws NoSuchAlgorithmException {
102-
SHA256 sha256 = new SHA256();
101+
public String getSHA512Hash6(String password) throws NoSuchAlgorithmException {
102+
SHA512 sha512 = new SHA512();
103103
byte[] passBytes = password.getBytes();
104-
sha256.update(passBytes, 0, passBytes.length);
105-
return Base64.getEncoder().encodeToString(sha256.digest());
104+
sha512.update(passBytes, 0, passBytes.length);
105+
return Base64.getEncoder().encodeToString(sha512.digest());
106106
}
107107

108108
// BAD - Invoke a wrapper implementation with a salt, which is not detected with an interface type variable.
109-
public String getSHA256Hash7(byte[] passphrase) throws NoSuchAlgorithmException, ClassNotFoundException, IllegalAccessException, InstantiationException {
109+
public String getSHA512Hash7(byte[] passphrase) throws NoSuchAlgorithmException, ClassNotFoundException, IllegalAccessException, InstantiationException {
110110
Class c = Class.forName("SHA512");
111111
HASH sha512 = (HASH) (c.newInstance());
112112
byte[] tmp = new byte[4];
@@ -120,11 +120,6 @@ public String getSHA256Hash7(byte[] passphrase) throws NoSuchAlgorithmException,
120120
return Base64.getEncoder().encodeToString(key);
121121
}
122122

123-
private String hash(String payload) throws NoSuchAlgorithmException {
124-
MessageDigest alg = MessageDigest.getInstance("SHA-256");
125-
return Base64.getEncoder().encodeToString(alg.digest(payload.getBytes(java.nio.charset.StandardCharsets.UTF_8)));
126-
}
127-
128123
public static byte[] getSalt() throws NoSuchAlgorithmException {
129124
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
130125
byte[] salt = new byte[16];

0 commit comments

Comments
 (0)