-
Notifications
You must be signed in to change notification settings - Fork 0
/
webAuthWithFix.als
293 lines (251 loc) · 8.42 KB
/
webAuthWithFix.als
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
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
open declarations
/* Model the WebAuth Protocol and the interaction of the following:
WebKDC: the WebAuth Login Server
WAS : a WebAuth-enabled Web Server
UA : the user agent, i.e. browser
*/
sig WAToken extends Token {}
// requester credentials
sig WAServiceToken extends WAToken { }
sig WAKrb5Token extends WAToken {}
// subject credentials
// The proxy token allow an application server to
// retrieve sensitive information (e.g. a user's
// mails or LDAP data) on behalf of the user.
sig WAProxyToken extends WAToken{}
sig WALoginToken extends WAToken{}
sig WACredToken extends WAToken{}
// request token
sig WARequestToken extends Token {
nonce:WANonce // add a WANonce to fix login CSRF in WebAuth
}
sig WARequest extends Token {
msgId: lone Int
}
sig WAResponse extends Token {
causedByMsgId: lone Int
}
sig WAGetTokenReq extends WARequest {
reqCred:WAToken,
subjCred:lone WAToken,
reqToken:lone WARequestToken,
tokens:set WAToken
}
sig WASessionKey extends Secret {}
sig WAGetTokenResp extends WAResponse {
respTokens:set WAToken,
sessionKey:WASessionKey
}
sig WARequestTokenReq extends WARequest {
reqCred:WAServiceToken,
proxyTokens:set WAProxyToken,
loginTokens:WALoginToken,
reqToken:WARequestToken,
remoteUser:WebPrincipal
// optional IP addresses go here
}
sig WAErrorToken extends WAToken {}
sig WAAppToken extends WAToken {} // opaque application token
sig WARequestTokenResp extends WAResponse {
error:WAErrorToken,
proxyToken:lone WAProxyToken, // for login request
returnURL:URL,
requesterSubject:Principal, // identity of requester
subject:Principal, // identity in supplied credentials
requestedToken:lone WAIdToken+WAProxyToken,
loginCanceledToken:WAErrorToken,
appState:WAAppToken
}
sig WAProxyData extends WAToken {} // may contain encrypted Krb data
// This command converts an existing Kerberos token
// into a webkdc-proxy token, for improvied single sign-on.
sig WAProxyTokenReq extends WARequest {
subjCred:WAToken,
proxyData:WAProxyData
}
sig WAProxyTokenResp extends WAResponse {
proxyToken:WAProxyToken,
subject:WebPrincipal // identity from subject credential in the request
}
// This command extracts info from a proxy token
sig WAProxyTokenInfoReq extends WARequest {
proxyToken:WAProxyToken
}
sig WAProxyTokenInfoResp extends WAResponse {
subject:WebPrincipal, // identity from proxy token
// proxy type
creation:Time,
expiration:Time
}
sig WANonce extends Token {}
// HMAC used in WebAuth. We may move them to a generic crypto file after we accumulate enough of them
sig WAHmac extends Token {}
sig WAAttribute {name:Token, Value:Token}
sig WAEncryptedToken extends WAToken {
// 4-byte UTC time to tell the server which key was used to encrypt this token
private keyHint:Token,
private nonce:WANonce,
private hmac:WAHmac,
// private attrs:set WAAttribute,
private padding:Token
}
sig WASCookie extends SecureCookie {}{
value in WANonce
}
fact WASCookieConstraints {
all disj c1, c2:WASCookie | c1.value != c2.value
}
sig WAIdToken extends WAEncryptedToken{
private username: WebPrincipal,
private wasNonce: WANonce,
// we don't need Kerberos subject authn data for now
private creationTime: Time,
private expirationTime: Time
}{
// Token should normally expire within 5 minutes based on WebAuth spec.
// Simulate using 5 ticks
expirationTime in creationTime.next.next.next.next.^next
}
// pred WAPossessWASCookie[httpClient:HTTPClient, token:WAIdToken, usageEvent:Event]{}
pred WARemoteScriptingIsPossible {
1 = 1 // true unless user disables script support
}
sig WASAuthPath extends Path {}
sig WAS extends HTTPServer {}
sig WAWebKDC extends HTTPServer {}
/* On initial access to WAS, WAS creates a host cookie containing a nonce,
and redirects the user's browser to the WebKDC with the nonce imbedded
in the query string
*/
pred WARedirectedFromWAS[t2:HTTPTransaction] {
some t1:t2.^cause, c:WASCookie|{
t1.req.to in WAS and
t1.resp.statusCode in RedirectionStatus and
WAWebKDC in (location & t1.resp.headers ).targetOrigin.dnslabel.resolvesTo and
c in (SetCookieHeader & t1.resp.headers).thecookie and
c.value in (t2.req.queryString.value & WARequestToken).nonce
}
}
/* On successful user authentication, WebKDC sends the Id Token to WAS.
The ID token contains the nonce originally created by WAS.
*/
pred WAPossessTokenViaLogin[httpClient:HTTPClient, token:WAIdToken, usageEvent:Event]{
some t1:HTTPTransaction|{
happensBeforeOrdering[t1.req, usageEvent] and
WARedirectedFromWAS[t1] and
t1.req.path = LOGIN and
t1.req.to in WAWebKDC and
t1.req.from in httpClient and
t1.resp.statusCode in RedirectionStatus and
WAContainsIdToken[t1.resp, token] and
token.creationTime = t1.resp.post and
token.username in httpClient.owner and
// IDToken includes the nonce created by WAS
token.wasNonce in (t1.req.queryString.value & WARequestToken).nonce
}
}
pred WAContainsIdToken [resp:HTTPResponse, token:WAIdToken] {
one (LocationHeader & resp.headers) and
some locHdr: LocationHeader |
locHdr in resp.headers and
locHdr.targetPath in WASAuthPath and
token in locHdr.params.value
}
/* The WAS accepts an ID Token in the query string only if
it is corroborated with a host cookie set by the WAS
*/
pred WAContainsIdToken [req:HTTPRequest, token:WAIdToken] {
token in req.queryString.value
some c:WASCookie| {
c in (CookieHeader & req.headers).thecookie
c.value = token.wasNonce
}
}
pred WAPossessTokenViaRemoteScripting [httpClient1:HTTPClient, token:WAIdToken, usageEvent:Event]{
// Through remote scripting, some user2(say, Mallory) can pass information to user1(say, Alice)'s browser
some t1:HTTPTransaction, httpClient2:HTTPClient|let user2=httpClient2.owner|
{
// Mallory possesses the token
WAPossessTokenViaLogin[httpClient2, token, usageEvent] and
// user1 (Alice) has a connection to user2(Mallory)'s web server
happensBefore[t1.req, usageEvent] and
t1.req.from in httpClient1 and
t1.req.to in (user2.servers & HTTPServer)
} and {
WARemoteScriptingIsPossible
}
}
fact WABeforeUsingTokenYouNeedToPossessIt{
all httpClient:HTTPClient, req:HTTPRequest, token:WAIdToken |
{
req.from in httpClient and
WAContainsIdToken [req, token]
} implies
WAPossessToken[httpClient, token, req]
}
pred WAPossessToken[httpClient:HTTPClient, token:WAIdToken, usageEvent:Event]{
WAPossessTokenViaLogin [httpClient, token, usageEvent] or
WAPossessTokenViaRemoteScripting [httpClient, token, usageEvent]
}
// Constrain the web model in this module for better illustration.
fact WAHTTPFacts{
all req:HTTPRequest | {req.from in HTTPClient and req.to in HTTPServer}
all user:WebPrincipal | user.servers in HTTPServer
all t1:HTTPTransaction | some (t1.resp) implies (t1.req.from = t1.resp.to ) and (t1.req.to = t1.resp.from)
Mallory.servers in (HTTPServer - (WAS+WAWebKDC))
}
pred WALoginCSRF [t1:HTTPTransaction]{
some token:WAIdToken|{
t1.req.from in Alice.httpClients and
t1.req.path in WASAuthPath and
t1.req.to in WAS and
WAContainsIdToken [t1.req, token] and
token.username in Mallory
}
}
fun WAFindAttacks: HTTPTransaction{
{
t1:HTTPTransaction |
WALoginCSRF[t1]
}
}
// run WAFindAttacks for 8 but exactly 0 ACTIVEATTACKER, exactly 0 PASSIVEATTACKER
check WANoLoginCSRF {
no WAFindAttacks
}
for 8 but exactly 0 ACTIVEATTACKER, exactly 0 PASSIVEATTACKER
/*
Executing "Check WANoLoginCSRF for 8 but exactly 0 ACTIVEATTACKER, exactly 0 PASSIVEATTACKER"
Run 1:
======
Generating facts...
Simplifying the bounds...
Solver=sat4j Bitwidth=4 MaxSeq=7 SkolemDepth=1 Symmetry=20
Generating CNF...
Generating the solution...
Begin solveAll()
209396 vars. 6897 primary vars. 412890 clauses. 6058505ms.
Solving...
End solveAll()
No counterexample found. Assertion may be valid. 1612370ms.
Run 2:
======
Solver=sat4j Bitwidth=4 MaxSeq=7 SkolemDepth=1 Symmetry=20
Generating CNF...
Generating the solution...
Begin solveAll()
209396 vars. 6897 primary vars. 412890 clauses. 4285089ms.
Solving...
End solveAll()
No counterexample found. Assertion may be valid. 1981270ms.
Run 3:
====
Solver=sat4j Bitwidth=4 MaxSeq=7 SkolemDepth=1 Symmetry=20
Generating CNF...
Generating the solution...
Begin solveAll()
209396 vars. 6897 primary vars. 412890 clauses. 4412958ms.
Solving...
End solveAll()
No counterexample found. Assertion may be valid. 2087697ms.
*/