Skip to content

Commit 048167d

Browse files
committed
Revamp the query to reduce FPs introduced by wrapper calls
1 parent 3af8773 commit 048167d

File tree

3 files changed

+72
-19
lines changed

3 files changed

+72
-19
lines changed

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

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -55,29 +55,17 @@ class HashWithoutSaltConfiguration extends TaintTracking::Configuration {
5555

5656
override predicate isSink(DataFlow::Node sink) {
5757
exists(
58-
MethodAccess mua, MethodAccess mda // invoke `md.digest()` with only one call of `md.update(password)`, that is, without the call of `md.update(digest)`
58+
MethodAccess mua // invoke `md.update(password)` without the call of `md.update(digest)`
5959
|
6060
sink.asExpr() = mua.getArgument(0) and
61-
mua.getMethod() instanceof MDUpdateMethod and // md.update(password)
62-
mda.getMethod() instanceof MDDigestMethod and
63-
mda.getNumArgument() = 0 and // md.digest()
64-
mda.getQualifier() = mua.getQualifier().(VarAccess).getVariable().getAnAccess() and
65-
not exists(MethodAccess mua2 |
66-
mua2.getMethod() instanceof MDUpdateMethod and // md.update(salt)
67-
mua2.getQualifier() = mua.getQualifier().(VarAccess).getVariable().getAnAccess() and
68-
mua2 != mua
69-
)
61+
mua.getMethod() instanceof MDUpdateMethod // md.update(password)
7062
)
7163
or
7264
// invoke `md.digest(password)` without another call of `md.update(salt)`
7365
exists(MethodAccess mda |
7466
sink.asExpr() = mda.getArgument(0) and
7567
mda.getMethod() instanceof MDDigestMethod and // md.digest(password)
76-
mda.getNumArgument() = 1 and
77-
not exists(MethodAccess mua |
78-
mua.getMethod() instanceof MDUpdateMethod and // md.update(salt)
79-
mua.getQualifier() = mda.getQualifier().(VarAccess).getVariable().getAnAccess()
80-
)
68+
mda.getNumArgument() = 1
8169
)
8270
}
8371

@@ -96,9 +84,37 @@ class HashWithoutSaltConfiguration extends TaintTracking::Configuration {
9684
) // System.arraycopy(password.getBytes(), ...)
9785
or
9886
exists(AddExpr e | node.asExpr() = e.getAnOperand()) // password+salt
87+
or
88+
exists(MethodAccess mua, MethodAccess ma |
89+
ma.getArgument(0) = node.asExpr() and // Detect wrapper methods that invoke `md.update(salt)`
90+
ma != mua and
91+
(
92+
mua.getQualifier().(VarAccess).getVariable().getAnAccess() = ma.getQualifier()
93+
or
94+
mua.getAnArgument().(VarAccess).getVariable().getAnAccess() = ma.getQualifier()
95+
or
96+
mua.getQualifier().(VarAccess).getVariable().getAnAccess() = ma.getAnArgument()
97+
or
98+
mua.getAnArgument().(VarAccess).getVariable().getAnAccess() = ma.getAnArgument()
99+
) and
100+
isMDUpdateCall(mua.getMethod())
101+
)
99102
}
100103
}
101104

105+
/** Holds if a method invokes `md.update(salt)`. */
106+
predicate isMDUpdateCall(Callable caller) {
107+
caller instanceof MDUpdateMethod
108+
or
109+
exists(Callable callee |
110+
caller.polyCalls(callee) and
111+
(
112+
callee instanceof MDUpdateMethod or
113+
isMDUpdateCall(callee)
114+
)
115+
)
116+
}
117+
102118
from DataFlow::PathNode source, DataFlow::PathNode sink, HashWithoutSaltConfiguration c
103119
where c.hasFlowPath(source, sink)
104120
select sink.getNode(), source, sink, "$@ is hashed without a salt.", source.getNode(),

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

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,43 @@ public String getSHA256Hash3(String passwordHash) throws NoSuchAlgorithmExceptio
6464
return Base64.getEncoder().encodeToString(messageDigest);
6565
}
6666

67-
private String hash(String payload) {
67+
public void update(SHA256 sha256, byte[] foo, int start, int len) throws NoSuchAlgorithmException {
68+
sha256.update(foo, start, len);
69+
}
70+
71+
public void update2(SHA256 sha256, byte[] foo, int start, int len) throws NoSuchAlgorithmException {
72+
sha256.update(foo, start, len);
73+
}
74+
75+
// GOOD - Invoke a wrapper implementation with a salt.
76+
public String getSHA256Hash4(String password) throws NoSuchAlgorithmException {
77+
SHA256 sha256 = new SHA256();
78+
byte[] salt = getSalt();
79+
byte[] passBytes = password.getBytes();
80+
sha256.update(passBytes, 0, passBytes.length);
81+
sha256.update(salt, 0, salt.length);
82+
return Base64.getEncoder().encodeToString(sha256.digest());
83+
}
84+
85+
// GOOD - Invoke a wrapper implementation with a salt.
86+
public String getSHA256Hash5(String password) throws NoSuchAlgorithmException {
87+
SHA256 sha256 = new SHA256();
88+
byte[] salt = getSalt();
89+
byte[] passBytes = password.getBytes();
90+
sha256.update(passBytes, 0, passBytes.length);
91+
update(sha256, salt, 0, salt.length);
92+
return Base64.getEncoder().encodeToString(sha256.digest());
93+
}
94+
95+
// BAD - Invoke a wrapper implementation without a salt.
96+
public String getSHA256Hash6(String password) throws NoSuchAlgorithmException {
97+
SHA256 sha256 = new SHA256();
98+
byte[] passBytes = password.getBytes();
99+
sha256.update(passBytes, 0, passBytes.length);
100+
return Base64.getEncoder().encodeToString(sha256.digest());
101+
}
102+
103+
private String hash(String payload) throws NoSuchAlgorithmException {
68104
MessageDigest alg = MessageDigest.getInstance("SHA-256");
69105
return Base64.getEncoder().encodeToString(alg.digest(payload.getBytes(java.nio.charset.StandardCharsets.UTF_8)));
70106
}
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
11
import java.security.MessageDigest;
2+
import java.security.NoSuchAlgorithmException;
23

34
public class SHA256 {
45
MessageDigest md;
56
public int getBlockSize() {return 32;}
6-
public void init() throws Exception {
7+
public void init() throws NoSuchAlgorithmException {
78
try { md = MessageDigest.getInstance("SHA-256"); }
89
catch (Exception e){
910
System.err.println(e);
1011
}
1112
}
1213

13-
public void update(byte[] foo, int start, int len) throws Exception {
14+
public void update(byte[] foo, int start, int len) throws NoSuchAlgorithmException {
1415
md.update(foo, start, len);
1516
}
1617

17-
public byte[] digest() throws Exception {
18+
public byte[] digest() throws NoSuchAlgorithmException {
1819
return md.digest();
1920
}
2021
}

0 commit comments

Comments
 (0)