-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathInsecureBasicAuth.ql
More file actions
238 lines (217 loc) · 7.55 KB
/
InsecureBasicAuth.ql
File metadata and controls
238 lines (217 loc) · 7.55 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
/**
* @name Insecure basic authentication
* @description Basic authentication only obfuscates username/password in
* Base64 encoding, which can be easily recognized and reversed.
* Transmission of sensitive information not over HTTPS is
* vulnerable to packet sniffing.
* @kind path-problem
* @problem.severity warning
* @precision medium
* @id java/insecure-basic-auth
* @tags security
* external/cwe/cwe-522
* external/cwe/cwe-319
*/
import java
import semmle.code.java.frameworks.Networking
import semmle.code.java.frameworks.ApacheHttp
import semmle.code.java.dataflow.TaintTracking
import DataFlow::PathGraph
/**
* Class of Java URL constructor.
*/
class URLConstructor extends ClassInstanceExpr {
URLConstructor() { this.getConstructor().getDeclaringType() instanceof TypeUrl }
predicate hasHttpStringArg() {
this.getConstructor().getParameter(0).getType() instanceof TypeString and
(
// URLs constructed with any of the three string constructors below:
// `URL(String protocol, String host, int port, String file)`,
// `URL(String protocol, String host, int port, String file, URLStreamHandler handler)`,
// `URL(String protocol, String host, String file)`
this.getConstructor().getNumberOfParameters() > 1 and
concatHttpString(getArgument(0), this.getArgument(1)) // First argument contains the protocol part and the second argument contains the host part.
or
// URLs constructed with the string constructor `URL(String spec)`
this.getConstructor().getNumberOfParameters() = 1 and
this.getArgument(0) instanceof HttpString // First argument contains the whole spec.
)
}
}
/**
* Class of Java URI constructor.
*/
class URIConstructor extends ClassInstanceExpr {
URIConstructor() { this.getConstructor().getDeclaringType() instanceof TypeUri }
predicate hasHttpStringArg() {
(
this.getNumArgument() = 1 and
this.getArgument(0) instanceof HttpString // `URI(String str)`
or
this.getNumArgument() = 4 and
concatHttpString(this.getArgument(0), this.getArgument(1)) // `URI(String scheme, String host, String path, String fragment)`
or
this.getNumArgument() = 5 and
concatHttpString(this.getArgument(0), this.getArgument(1)) // `URI(String scheme, String authority, String path, String query, String fragment)` without user-info in authority
or
this.getNumArgument() = 7 and
concatHttpString(this.getArgument(0), this.getArgument(2)) // `URI(String scheme, String userInfo, String host, int port, String path, String query, String fragment)`
)
}
}
/**
* String of HTTP URLs not in private domains.
*/
class HttpStringLiteral extends StringLiteral {
HttpStringLiteral() {
// Match URLs with the HTTP protocol and without private IP addresses to reduce false positives.
exists(string s | this.getRepresentedString() = s |
s.regexpMatch("(?i)http://[\\[a-zA-Z0-9].*") and
not s.substring(7, s.length()) instanceof PrivateHostName
)
}
}
/**
* Checks both parts of protocol and host.
*/
predicate concatHttpString(Expr protocol, Expr host) {
(
protocol.(CompileTimeConstantExpr).getStringValue().regexpMatch("(?i)http(://)?") or
protocol
.(VarAccess)
.getVariable()
.getAnAssignedValue()
.(CompileTimeConstantExpr)
.getStringValue()
.regexpMatch("(?i)http(://)?")
) and
not exists(string hostString |
hostString = host.(CompileTimeConstantExpr).getStringValue() or
hostString =
host.(VarAccess).getVariable().getAnAssignedValue().(CompileTimeConstantExpr).getStringValue()
|
hostString.length() = 0 or // Empty host is loopback address
hostString instanceof PrivateHostName
)
}
/** Gets the leftmost operand in a concatenated string */
Expr getLeftmostConcatOperand(Expr expr) {
if expr instanceof AddExpr
then result = getLeftmostConcatOperand(expr.(AddExpr).getLeftOperand())
else result = expr
}
/**
* String concatenated with `HttpStringLiteral`.
*/
class HttpString extends Expr {
HttpString() {
this instanceof HttpStringLiteral
or
concatHttpString(this.(AddExpr).getLeftOperand(),
getLeftmostConcatOperand(this.(AddExpr).getRightOperand()))
}
}
/**
* String pattern of basic authentication.
*/
class BasicAuthString extends StringLiteral {
BasicAuthString() { exists(string s | this.getRepresentedString() = s | s.matches("Basic %")) }
}
/**
* String concatenated with `BasicAuthString`.
*/
predicate builtFromBasicAuthStringConcat(Expr expr) {
expr instanceof BasicAuthString
or
builtFromBasicAuthStringConcat(expr.(AddExpr).getLeftOperand())
or
exists(Expr other | builtFromBasicAuthStringConcat(other) |
exists(Variable var | var.getAnAssignedValue() = other and var.getAnAccess() = expr)
)
}
/** The `openConnection` method of Java URL. Not to include `openStream` since it won't be used in this query. */
class HttpURLOpenMethod extends Method {
HttpURLOpenMethod() {
this.getDeclaringType() instanceof TypeUrl and
this.getName() = "openConnection"
}
}
/** Constructor of `ApacheHttpRequest` */
predicate apacheHttpRequest(DataFlow::Node node1, DataFlow::Node node2) {
exists(ConstructorCall cc |
cc.getConstructedType() instanceof ApacheHttpRequest and
node2.asExpr() = cc and
cc.getAnArgument() = node1.asExpr()
)
}
/** `URI` methods */
predicate createURI(DataFlow::Node node1, DataFlow::Node node2) {
exists(
URIConstructor cc // new URI
|
node2.asExpr() = cc and
cc.getArgument(0) = node1.asExpr()
)
or
exists(
StaticMethodAccess ma // URI.create
|
ma.getMethod().getDeclaringType() instanceof TypeUri and
ma.getMethod().hasName("create") and
node1.asExpr() = ma.getArgument(0) and
node2.asExpr() = ma
)
}
/** Constructors of `URL` */
predicate createURL(DataFlow::Node node1, DataFlow::Node node2) {
exists(URLConstructor cc |
node2.asExpr() = cc and
cc.getArgument(0) = node1.asExpr()
)
}
/** Method call of `HttpURLOpenMethod` */
predicate urlOpen(DataFlow::Node node1, DataFlow::Node node2) {
exists(MethodAccess ma |
ma.getMethod() instanceof HttpURLOpenMethod and
node1.asExpr() = ma.getQualifier() and
ma = node2.asExpr()
)
}
class BasicAuthFlowConfig extends TaintTracking::Configuration {
BasicAuthFlowConfig() { this = "InsecureBasicAuth::BasicAuthFlowConfig" }
override predicate isSource(DataFlow::Node src) {
src.asExpr() instanceof HttpString
or
exists(URLConstructor uc |
uc.hasHttpStringArg() and
src.asExpr() = uc.getArgument(0)
)
or
exists(URIConstructor uc |
uc.hasHttpStringArg() and
src.asExpr() = uc.getArgument(0)
)
}
override predicate isSink(DataFlow::Node sink) {
exists(MethodAccess ma |
sink.asExpr() = ma.getQualifier() and
(
ma.getMethod().hasName("addHeader") or
ma.getMethod().hasName("setHeader") or
ma.getMethod().hasName("setRequestProperty")
) and
ma.getArgument(0).(CompileTimeConstantExpr).getStringValue() = "Authorization" and
builtFromBasicAuthStringConcat(ma.getArgument(1))
)
}
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
apacheHttpRequest(node1, node2) or
createURI(node1, node2) or
createURL(node1, node2) or
urlOpen(node1, node2)
}
}
from DataFlow::PathNode source, DataFlow::PathNode sink, BasicAuthFlowConfig config
where config.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "Insecure basic authentication from $@.", source.getNode(),
"HTTP url"