Odoo’s modular architecture is its biggest advantage: you can shape the ERP around how your business really works. It is also its largest attack surface. Custom add-ons written in-house, by an outsourced agency or with rapid prototyping tools often build up security debt.
When a module ignores Odoo’s security framework, it quietly bypasses the access controls the rest of the system relies on. This article is part of our Odoo security series.
Key takeaways
- Every new model needs explicit access rights for each group that uses it.
- Restrict which records each group can see, especially across companies and departments.
- Declare auth on every controller and keep CSRF protection on.
- Use the ORM; if you must write SQL, always pass parameters separately.
- In Odoo 20, record rules are replaced by a domain on the access right itself, so plan for that when you upgrade.
The flaws we find most often
- Missing access rights. A new
models.Modelwithout rows insecurity/ir.model.access.csv. Standard users either hit errors, or someone “fixes” it by granting everything to everyone. - Rules that are too broad. No multi-company or departmental restriction, so any internal user can read or edit sensitive financial or HR records.
- Unsafe controllers.
@http.routeendpoints withauth='public'orauth='none'that change data, or that switch off CSRF checks. - Raw SQL built with string formatting.
cr.execute()with f-strings or%formatting opens the door to SQL injection. .sudo()as a shortcut. Used to silence an access error, and then returning privileged data to the user.
Secure development practices
- Least privilege by default. Give each group (internal users, portal, your own groups) only the read, write, create and delete rights it needs.
- Restrict records, not just models. Up to Odoo 19 that means record rules; in Odoo 20, put the domain on the access right. Always include the company restriction for multi-company databases.
- Stay in the ORM. When raw SQL is truly needed for performance, use parameter binding (
cr.execute(query, params)) or Odoo’sSQLwrapper, never string formatting. - Lock down controllers. Declare
authexplicitly, validate every input, check record ownership before returning data, and keep CSRF protection on for forms. - Review sudo(). Keep it to the smallest operation and never pass sudo-ed recordsets back to the browser.
- Test as a normal user. Log in as a restricted employee and a portal user before every release.
For broader design advice, see our complete guide to Odoo custom modules and common customization mistakes.
How Mediod can help
Mediod’s Odoo development team runs source-code security reviews of custom modules: access rights, controllers, SQL and privilege escalation. We then fix what we find or refactor modules ahead of your next upgrade. Short on in-house capacity? You can also hire Odoo developers by the month.
More from our Odoo security series
- Hardening a Dedicated Odoo Server (VPS or On-Premise): An Infrastructure Blueprint
- Odoo.sh Security: What’s Handled for You and What You Still Need to Protect
See all Odoo Security articles →
Want a second pair of eyes on your Odoo security?
Mediod Consulting is an Official Odoo Partner. We review custom modules, access rights, integrations and hosting set-ups, and give you a prioritised list of fixes. Book a free discovery call or request a quotation for a security review.
Frequently Asked Questions
What happens if a custom Odoo model has no access rights?
Odoo denies access to non-admin users and logs a warning. Teams often work around it by granting broad rights, which is where data exposure starts. Define explicit access rights for each group instead.
Is raw SQL safe in Odoo?
Only with parameter binding. Pass values separately (cr.execute(query, params)) or use Odoo’s SQL wrapper. Never build queries with string formatting.
Does Odoo 20 still have record rules?
According to the Odoo 20 release notes, record rules are removed and a domain is added directly on the access right to decide which records it applies to. Existing rules need to be reviewed during the upgrade.



