Skip to content

Commit 002744c

Browse files
committed
Updating for Episode 19
1 parent 80566d7 commit 002744c

File tree

1 file changed

+217
-0
lines changed

1 file changed

+217
-0
lines changed
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
# Created 2018-09-09 Sun 16:07
2+
#+OPTIONS: num:nil toc:nil
3+
#+OPTIONS: tags:nil
4+
#+OPTIONS: reveal_center:nil reveal_control:nil width:100% height:100% prop:nil
5+
#+OPTIONS: reveal_history:t reveal_keyboard:t reveal_overview:t
6+
#+OPTIONS: reveal_slide_number:nil
7+
#+OPTIONS: reveal_title_slide:"<h2>%t</h2><h3>%d<h3>"
8+
#+OPTIONS: reveal_progress:t reveal_rolling_links:nil reveal_single_file:nil
9+
#+OPTIONS: auto-id:t ^:nil
10+
#+TITLE: Joi.js Input Validation (Part 2)
11+
#+AUTHOR: Zach Roof
12+
#+REVEAL_HLEVEL: 1
13+
#+REVEAL_MARGIN: 0
14+
#+REVEAL_MIN_SCALE: 1
15+
#+REVEAL_MAX_SCALE: 1
16+
#+REVEAL_ROOT: .
17+
#+REVEAL_TRANS: default
18+
#+REVEAL_SPEED: slow
19+
#+REVEAL_THEME: sts
20+
#+REVEAL_EXTRA_CSS: css/local.css
21+
#+REVEAL_INIT_SCRIPT: previewLinks: false
22+
#+REVEAL_PLUGINS: (classList highlight)
23+
#+REVEAL_HIGHLIGHT_CSS: %r/lib/highlight.js/src/styles/monokai-sublime.css
24+
#+REVEAL_HLEVEL: 2
25+
#+NAME: CURRENT_TUTORIAL
26+
* Joi.js Input Validation (Part 2)
27+
** Scope
28+
1. Exercise: Try to bypass a Joi.js schema
29+
2. Understand how to approach input validation (useful for any input validation library)
30+
31+
** Ex: Joi Bypass (Hint 1)
32+
- How can the schema be bypassed?
33+
- Hint 1
34+
- JSON input (with keys/values) is directly assigned into keys/values
35+
in the database
36+
- What could this /potentially/ override?
37+
38+
** Ex: Joi Bypass (Hint 1 Answer)
39+
- It could override sensitive fields in the database
40+
- Ex: ~isAdmin: false~ (set by backend logic) could be overridden with ~isAdmin: true~
41+
- Type of Mass Assignment vulnerability
42+
43+
** Ex: Joi Bypass (Hint 2)
44+
- In the json that's fed into ~Joi.validate()~, find where ~isAdmin: true~ could be injected
45+
46+
** Ex: Joi Bypass (Hint 2 Answer)
47+
- Add ~isAdmin: true~ to root level
48+
#+BEGIN_SRC js
49+
const Joi = require('joi');
50+
51+
const schema = Joi.object()
52+
.keys({
53+
// Requires a given string value
54+
username: Joi.string()
55+
.alphanum()
56+
.min(3)
57+
.max(30)
58+
.required(),
59+
// Define password complexity requirements through regex (consider more complex regex)
60+
password: Joi.string()
61+
.regex(/^[a-zA-Z0-9]{3,30}$/)
62+
.required(),
63+
// Force passwords to match
64+
password_confirmation: Joi.any()
65+
.equal(Joi.ref('password'))
66+
.required(),
67+
// Accept different Joi types. Optional, unconstrained string or number
68+
access_token: [Joi.string(), Joi.number()],
69+
// Required birthyear to be an int between range
70+
birthyear: Joi.number()
71+
.integer()
72+
.min(1900)
73+
.max(2013)
74+
.required(),
75+
// Validate email adress from example.com (remember spoofing considerations)
76+
email: Joi.string()
77+
.email()
78+
.regex(/example\.com$/),
79+
marketing_opt_out: Joi.boolean(),
80+
csrf_token: Joi.string()
81+
.guid({
82+
version: 'uuidv4',
83+
})
84+
.required(),
85+
sex: Joi.string()
86+
.equal(['M', 'F', 'MALE', 'FEMALE', 'DECLINE'])
87+
.required(),
88+
time: Joi.date()
89+
.timestamp('javascript'),
90+
roles: Joi.object()
91+
.keys(),
92+
})
93+
// email must be accompanied by marketing_opt_out
94+
.with('email', 'marketing_opt_out');
95+
96+
const result = Joi.validate({
97+
username: 'Ronald',
98+
password: 'ZZZ',
99+
password_confirmation: 'ZZZ',
100+
birthyear: 2010,
101+
102+
marketing_opt_out: true,
103+
csrf_token: '6d4d8c14-ef12-45d9-ab3c-5dddf941fb76',
104+
sex: 'F',
105+
time: 1534942475121,
106+
roles: {},
107+
isAdmin: true,
108+
}, schema);
109+
110+
// If result.error === null, payload is valid
111+
console.log(`The validation error is: ${result.error}`);
112+
#+END_SRC
113+
114+
** Ex: Joi Bypass (Hint 2 Answer CONT.)
115+
#+BEGIN_SRC json
116+
The validation error is: ValidationError: "isAdmin" is not allowed
117+
#+END_SRC
118+
** Ex: Joi Bypass (Hint 3)
119+
- Where else could ~isAdmin: true~ be inserted?
120+
- Review https://github.com/hapijs/joi/blob/master/API.md#objectkeysschema
121+
- How could this confuse a developer?
122+
123+
** Ex: Joi Bypass (Hint 3 Answer)
124+
#+BEGIN_SRC json
125+
roles: Joi.object()
126+
.keys()
127+
#+END_SRC
128+
- No keys are whitelisted
129+
- Appears that only an empty object would pass validation
130+
- Backend leverages this object to populate ACLs
131+
- https://github.com/hapijs/joi/blob/master/API.md#objectkeysschema
132+
- ~object.keys([schema])~
133+
- If schema is ~{}~ no keys are allowed. If schema is ~null~ or ~undefined~,
134+
any key is allowed
135+
136+
** Ex: Joi Bypass (Answer)
137+
#+BEGIN_SRC js
138+
const Joi = require('joi');
139+
140+
const schema = Joi.object()
141+
.keys({
142+
// Requires a given string value
143+
username: Joi.string()
144+
.alphanum()
145+
.min(3)
146+
.max(30)
147+
.required(),
148+
// Define password complexity requirements through regex (consider more complex regex)
149+
password: Joi.string()
150+
.regex(/^[a-zA-Z0-9]{3,30}$/)
151+
.required(),
152+
// Force passwords to match
153+
password_confirmation: Joi.any()
154+
.equal(Joi.ref('password'))
155+
.required(),
156+
// Accept different Joi types. Optional, unconstrained string or number
157+
access_token: [Joi.string(), Joi.number()],
158+
// Required birthyear to be an int between range
159+
birthyear: Joi.number()
160+
.integer()
161+
.min(1900)
162+
.max(2013)
163+
.required(),
164+
// Validate email adress from example.com (remember spoofing considerations)
165+
email: Joi.string()
166+
.email()
167+
.regex(/example\.com$/),
168+
marketing_opt_out: Joi.boolean(),
169+
csrf_token: Joi.string()
170+
.guid({
171+
version: 'uuidv4',
172+
})
173+
.required(),
174+
sex: Joi.string()
175+
.equal(['M', 'F', 'MALE', 'FEMALE', 'DECLINE'])
176+
.required(),
177+
time: Joi.date()
178+
.timestamp('javascript'),
179+
roles: Joi.object()
180+
.keys(),
181+
})
182+
// email must be accompanied by marketing_opt_out
183+
.with('email', 'marketing_opt_out');
184+
185+
const result = Joi.validate({
186+
username: 'Ronald',
187+
password: 'ZZZ',
188+
password_confirmation: 'ZZZ',
189+
// access_token: 1234,
190+
birthyear: 2010,
191+
192+
marketing_opt_out: true,
193+
csrf_token: '6d4d8c14-ef12-45d9-ab3c-5dddf941fb76',
194+
sex: 'F',
195+
time: 1534942475121,
196+
roles: {
197+
isAdmin: true,
198+
},
199+
}, schema);
200+
201+
// If result.error === null, payload is valid
202+
console.log(`The validation error is: ${result.error}`);
203+
#+END_SRC
204+
205+
** Ex: Joi Bypass (Answer) CONT
206+
#+BEGIN_SRC text
207+
The validation error is: null
208+
#+END_SRC
209+
210+
** Takeaways
211+
- Be very careful with library defaults when leveraging input validation
212+
- Easy for hackers to look for edge cases in validation
213+
214+
** Additional Resources
215+
- https://github.com/topics/joi
216+
- https://www.npmjs.com/package/joi-password-complexity
217+
- https://stackoverflow.com/questions/19605150/regex-for-password-must-contain-at-least-eight-characters-at-least-one-number-a?rq=1

0 commit comments

Comments
 (0)