for safer - odoo
TRANSCRIPT
![Page 1: FOR SAFER - Odoo](https://reader031.vdocuments.net/reader031/viewer/2022022206/62137926b20dca4f41254577/html5/thumbnails/1.jpg)
10 RULES FOR
SAFERCODE
![Page 2: FOR SAFER - Odoo](https://reader031.vdocuments.net/reader031/viewer/2022022206/62137926b20dca4f41254577/html5/thumbnails/2.jpg)
RULE #1EVAL is EVIL
Don’t trust strings supposed to contain expressions or code
![Page 3: FOR SAFER - Odoo](https://reader031.vdocuments.net/reader031/viewer/2022022206/62137926b20dca4f41254577/html5/thumbnails/3.jpg)
“eval” breaks the barrier betweencode and data
Is this safe?
Maybe… it depends.
Is it a good idea?
No, because eval() is not necessary!
![Page 4: FOR SAFER - Odoo](https://reader031.vdocuments.net/reader031/viewer/2022022206/62137926b20dca4f41254577/html5/thumbnails/4.jpg)
There are safer and smarter waysto parse data in python PYTHON
“42” int(x)float(x)
Given thisstring Parse it
like this
“[1,2,3,true]”json.loads(x)
‘{“widget”: “monetary”}’
“[1,2,3,True]” ast.literal_eval(x)”{‘widget’: ‘monetary’}”
![Page 5: FOR SAFER - Odoo](https://reader031.vdocuments.net/reader031/viewer/2022022206/62137926b20dca4f41254577/html5/thumbnails/5.jpg)
There are safer and smarter waysto parse data in python JAVASCRIPT
“42” parseInt(x)parseFloat(x)
Given thisstring Parse it
like this
“[1,2,3,true]”JSON.parse(x)‘{“widget”: “monetary”}’
![Page 6: FOR SAFER - Odoo](https://reader031.vdocuments.net/reader031/viewer/2022022206/62137926b20dca4f41254577/html5/thumbnails/6.jpg)
If you must eval parametersuse a safe eval method
# YESfrom odoo.tools import safe_evalres = safe_eval(’foo’, {’foo’: 42});
# NOfrom odoo.tools import safe_eval as evalres = eval(’foo’, {’foo’: 42});
Alias built-in eval as ”unsafe_eval”
Show your meaning!
# YESunsafe_eval = evalres = unsafe_eval(trusted_code);
# NO!res = eval(trusted_code);
Import as ”safe_eval”, not as ”eval”!
![Page 7: FOR SAFER - Odoo](https://reader031.vdocuments.net/reader031/viewer/2022022206/62137926b20dca4f41254577/html5/thumbnails/7.jpg)
If you must eval parametersuse a safe eval method
// py.js is included by defaultpy.eval(’foo’, {’foo’: 42});
// require(”web.pyeval”) for // domains/contexts/groupby evaluationpyeval.eval(’domains’, my_domain);
Do not use the built-in JS eval!
![Page 8: FOR SAFER - Odoo](https://reader031.vdocuments.net/reader031/viewer/2022022206/62137926b20dca4f41254577/html5/thumbnails/8.jpg)
50%of vulnerabilities found every year include
remote code execution injected via
unsafe eval
![Page 9: FOR SAFER - Odoo](https://reader031.vdocuments.net/reader031/viewer/2022022206/62137926b20dca4f41254577/html5/thumbnails/9.jpg)
RULE #2YOU SHALL NOT
PICKLE
Don’t use it. Ever. Use JSON.
![Page 10: FOR SAFER - Odoo](https://reader031.vdocuments.net/reader031/viewer/2022022206/62137926b20dca4f41254577/html5/thumbnails/10.jpg)
“Warning: The pickle module is not intended to be secure against erroneous or maliciously constructed data. Never unpickle data received from an untrusted or unauthenticated source.
”
![Page 11: FOR SAFER - Odoo](https://reader031.vdocuments.net/reader031/viewer/2022022206/62137926b20dca4f41254577/html5/thumbnails/11.jpg)
Python’s pickle serialization is:+unsafe +not portable
+unreadable
pickle.dumps({“widget”:“monetary”}) == "(dp0\nS'widget'\np1\nS'monetary'\np2\ns."
Actually astack-basedlanguage!
![Page 12: FOR SAFER - Odoo](https://reader031.vdocuments.net/reader031/viewer/2022022206/62137926b20dca4f41254577/html5/thumbnails/12.jpg)
Pickle is unsafeSeriously.
>>> yummy = "cos\nsystem\n(S'cat /etc/shadow | head -n 5'\ntR.'\ntR.">>> pickle.loads(yummy)root:$6$m7ndoM3p$JRVXomVQFn/KH81DEePpX98usSoESUnml3e6Nlf.:14951:0:99999:7:::daemon:x:14592:0:99999:7:::(…)>>>
![Page 13: FOR SAFER - Odoo](https://reader031.vdocuments.net/reader031/viewer/2022022206/62137926b20dca4f41254577/html5/thumbnails/13.jpg)
Use JSON instead!
json.dumps({“widget”:“monetary”}) == '{"widget": "monetary"}'
+safe +portable+readable
![Page 14: FOR SAFER - Odoo](https://reader031.vdocuments.net/reader031/viewer/2022022206/62137926b20dca4f41254577/html5/thumbnails/14.jpg)
RULE #3USE THE CURSOR
WISELY
Use the ORM API. And when you can’t, use query parameters.
![Page 15: FOR SAFER - Odoo](https://reader031.vdocuments.net/reader031/viewer/2022022206/62137926b20dca4f41254577/html5/thumbnails/15.jpg)
SQL injection is a classical privilege escalation vector
self.search(domain)The ORM is here to help you build safe queries:
Psycopg can also help you do that , if you tell it what is code and what is data:
query = ”””SELECT * FROM res_partnerWHERE id IN %s”””
self._cr.execute(query, (tuple(ids),))
SQL code
SQL data parameters
![Page 16: FOR SAFER - Odoo](https://reader031.vdocuments.net/reader031/viewer/2022022206/62137926b20dca4f41254577/html5/thumbnails/16.jpg)
Learn the API to avoid hurting yourselfandother people!
![Page 17: FOR SAFER - Odoo](https://reader031.vdocuments.net/reader031/viewer/2022022206/62137926b20dca4f41254577/html5/thumbnails/17.jpg)
This method is vulnerableto SQL injection
def compute_balance_by_category(self, categ=’in’):query = ”””SELECT sum(debit-credit)
FROM account_invoice_line lJOIN account_invoice i ON (l.invoice_id = i.id)
WHERE i.categ = ’%s_invoice’GROUP BY i.partner_id ”””
self._cr.execute(query % categ)return self._cr.fetchall()
What if someone calls it with
categ = ”””in_invoice’; UPDATE res_usersSET password = ’god’ WHERE id=1; SELECT sum(debit-credit) FROM account_invoice_lineWHERE name = ’”””
![Page 18: FOR SAFER - Odoo](https://reader031.vdocuments.net/reader031/viewer/2022022206/62137926b20dca4f41254577/html5/thumbnails/18.jpg)
This method is still vulnerableto SQL injection
def _compute_balance_by_category(self, categ=’in’):query = ”””SELECT sum(debit-credit)
FROM account_invoice_line lJOIN account_invoice i ON (l.invoice_id = i.id)
WHERE i.categ = ’%s_invoice’GROUP BY i.partner_id ”””
self._cr.execute(query % categ)return self._cr.fetchall()
Better, but it could still be called indirectly!
Now private!
![Page 19: FOR SAFER - Odoo](https://reader031.vdocuments.net/reader031/viewer/2022022206/62137926b20dca4f41254577/html5/thumbnails/19.jpg)
This method is still vulnerableto SQL injection
def _compute_balance_by_category(self, categ=’in’):assert categ in (’in’, ’out’)query = ”””SELECT sum(debit-credit)
FROM account_invoice_line lJOIN account_invoice i ON (l.invoice_id = i.id)
WHERE i.categ = ’%s_invoice’GROUP BY i.partner_id ”””
self._cr.execute(query % categ)return self._cr.fetchall()
Better, but assert can be optimized out and ignored
(e.g. in Windows builds)
Now checked with assert!
![Page 20: FOR SAFER - Odoo](https://reader031.vdocuments.net/reader031/viewer/2022022206/62137926b20dca4f41254577/html5/thumbnails/20.jpg)
This method is safe againstSQL injection
def _compute_balance_by_category(self, categ=’in’):categ = ’%s_invoice’ % categquery = ”””SELECT sum(debit-credit)
FROM account_invoice_line lJOIN account_invoice i ON (l.invoice_id = i.id)
WHERE i.categ = %sGROUP BY i.partner_id ”””
self._cr.execute(query, (categ,))return self._cr.fetchall()
Separates codeand parameters!
![Page 21: FOR SAFER - Odoo](https://reader031.vdocuments.net/reader031/viewer/2022022206/62137926b20dca4f41254577/html5/thumbnails/21.jpg)
RULE #4Fight XSS-FORCE
(t-raw, uploadS, html fields, . . .)
So many XSS vectors – gotta watch ’em all