88 */
99
1010import java
11+ import semmle.code.java.dataflow.DataFlow3
1112import semmle.code.java.dataflow.TaintTracking
13+ import semmle.code.java.dataflow.TaintTracking2
14+ import semmle.code.java.dataflow.TaintTracking3
1215import DataFlow:: PathGraph
1316
1417/** The Java class `java.security.MessageDigest`. */
1518class MessageDigest extends RefType {
1619 MessageDigest ( ) { this .hasQualifiedName ( "java.security" , "MessageDigest" ) }
1720}
1821
22+ class MDConstructor extends StaticMethodAccess {
23+ MDConstructor ( ) {
24+ exists ( Method m | m = this .getMethod ( ) |
25+ m .getDeclaringType ( ) instanceof MessageDigest and
26+ m .hasName ( "getInstance" )
27+ )
28+ }
29+ }
30+
1931/** The method `digest()` declared in `java.security.MessageDigest`. */
2032class MDDigestMethod extends Method {
2133 MDDigestMethod ( ) {
@@ -48,24 +60,20 @@ class PasswordVarExpr extends Expr {
4860}
4961
5062/** Taint configuration tracking flow from an expression whose name suggests it holds password data to a method call that generates a hash without a salt. */
51- class HashWithoutSaltConfiguration extends TaintTracking :: Configuration {
52- HashWithoutSaltConfiguration ( ) { this = "HashWithoutSaltConfiguration " }
63+ class PasswordHashConfiguration extends TaintTracking3 :: Configuration {
64+ PasswordHashConfiguration ( ) { this = "PasswordHashConfiguration " }
5365
54- override predicate isSource ( DataFlow :: Node source ) { source .asExpr ( ) instanceof PasswordVarExpr }
66+ override predicate isSource ( DataFlow3 :: Node source ) { source .asExpr ( ) instanceof PasswordVarExpr }
5567
56- override predicate isSink ( DataFlow :: Node sink ) {
68+ override predicate isSink ( DataFlow3 :: Node sink ) {
5769 exists (
58- MethodAccess mua // invoke `md.update(password)` without the call of `md.update(digest)`
70+ MethodAccess ma // invoke `md.update(password)` without the call of `md.update(digest)`
5971 |
60- sink .asExpr ( ) = mua .getArgument ( 0 ) and
61- mua .getMethod ( ) instanceof MDUpdateMethod // md.update(password)
62- )
63- or
64- // invoke `md.digest(password)` without another call of `md.update(salt)`
65- exists ( MethodAccess mda |
66- sink .asExpr ( ) = mda .getArgument ( 0 ) and
67- mda .getMethod ( ) instanceof MDDigestMethod and // md.digest(password)
68- mda .getNumArgument ( ) = 1
72+ sink .asExpr ( ) = ma .getArgument ( 0 ) and
73+ (
74+ ma .getMethod ( ) instanceof MDUpdateMethod or // md.update(password)
75+ ma .getMethod ( ) instanceof MDDigestMethod // md.digest(password)
76+ )
6977 )
7078 }
7179
@@ -76,48 +84,64 @@ class HashWithoutSaltConfiguration extends TaintTracking::Configuration {
7684 * `byte[] messageDigest = md.digest(allBytes);`
7785 * Or the password is concatenated with a salt as a string.
7886 */
79- override predicate isSanitizer ( DataFlow :: Node node ) {
87+ override predicate isSanitizer ( DataFlow3 :: Node node ) {
8088 exists ( MethodAccess ma |
8189 ma .getMethod ( ) .getDeclaringType ( ) .hasQualifiedName ( "java.lang" , "System" ) and
8290 ma .getMethod ( ) .hasName ( "arraycopy" ) and
8391 ma .getArgument ( 0 ) = node .asExpr ( )
8492 ) // System.arraycopy(password.getBytes(), ...)
8593 or
8694 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
95+ }
96+ }
97+
98+ class PasswordDigestConfiguration extends TaintTracking2:: Configuration {
99+ PasswordDigestConfiguration ( ) { this = "PasswordDigestConfiguration" }
100+
101+ override predicate isSource ( DataFlow2:: Node source ) {
102+ exists ( MDConstructor mc | source .asExpr ( ) = mc )
103+ }
104+
105+ override predicate isSink ( DataFlow2:: Node sink ) {
106+ exists ( MethodAccess ma |
91107 (
92- ma .getQualifier ( ) .getType ( ) instanceof Interface
93- or
94- mua .getQualifier ( ) .( VarAccess ) .getVariable ( ) .getAnAccess ( ) = ma .getQualifier ( )
95- or
96- mua .getAnArgument ( ) .( VarAccess ) .getVariable ( ) .getAnAccess ( ) = ma .getQualifier ( )
97- or
98- mua .getQualifier ( ) .( VarAccess ) .getVariable ( ) .getAnAccess ( ) = ma .getAnArgument ( )
99- or
100- mua .getArgument ( 0 ) .( VarAccess ) .getVariable ( ) .getAnAccess ( ) = ma .getAnArgument ( )
108+ ma .getMethod ( ) instanceof MDUpdateMethod or
109+ ma .getMethod ( ) instanceof MDDigestMethod
101110 ) and
102- isMDUpdateCall ( mua .getMethod ( ) )
111+ exists ( PasswordHashConfiguration cc | cc .hasFlowToExpr ( ma .getAnArgument ( ) ) ) and
112+ sink .asExpr ( ) = ma .getQualifier ( )
103113 )
104114 }
105115}
106116
107- /** Holds if a method invokes `md.update(salt)`. */
108- predicate isMDUpdateCall ( Callable caller ) {
109- caller instanceof MDUpdateMethod
110- or
111- exists ( Callable callee |
112- caller .polyCalls ( callee ) and
113- (
114- callee instanceof MDUpdateMethod or
115- isMDUpdateCall ( callee )
117+ class HashWithoutSaltConfiguration extends TaintTracking:: Configuration {
118+ HashWithoutSaltConfiguration ( ) { this = "HashWithoutSaltConfiguration" }
119+
120+ override predicate isSource ( DataFlow:: Node source ) {
121+ exists ( PasswordDigestConfiguration pc | pc .hasFlow ( source , _) )
122+ }
123+
124+ override predicate isSink ( DataFlow:: Node sink ) {
125+ exists ( MethodAccess ma |
126+ ma .getMethod ( ) instanceof MDDigestMethod and // md.digest(password)
127+ sink .asExpr ( ) = ma .getQualifier ( )
128+ )
129+ }
130+
131+ /** Holds if `md.update` or `md.digest` calls integrate something other than the password, perhaps a salt. */
132+ override predicate isSanitizer ( DataFlow:: Node node ) {
133+ exists ( MethodAccess ma |
134+ (
135+ ma .getMethod ( ) instanceof MDUpdateMethod
136+ or
137+ ma .getMethod ( ) instanceof MDDigestMethod and ma .getNumArgument ( ) != 0
138+ ) and
139+ node .asExpr ( ) = ma .getQualifier ( ) and
140+ not exists ( PasswordHashConfiguration cc | cc .hasFlowToExpr ( ma .getAnArgument ( ) ) )
116141 )
117- )
142+ }
118143}
119144
120- from DataFlow:: PathNode source , DataFlow:: PathNode sink , HashWithoutSaltConfiguration c
121- where c .hasFlowPath ( source , sink )
122- select sink .getNode ( ) , source , sink , "$@ is hashed without a salt." , source .getNode ( ) ,
123- "The password"
145+ from DataFlow:: PathNode source , DataFlow:: PathNode sink , HashWithoutSaltConfiguration cc
146+ where cc .hasFlowPath ( source , sink )
147+ select sink , source , sink , "$@ is hashed without a salt." , source , "The password"
0 commit comments