Odoo 安全强化:身份验证、访问权限和加密
企业 ERP 系统是网络攻击的高价值目标。包含客户 PII、财务记录、供应商合同和员工数据的 Odoo 实例代表了一个重要的攻击面,需要精心设计的分层安全控制。
本指南涵盖了 Odoo 19 Enterprise 的完整安全强化流程:从身份验证强化和访问权限设计到网络强化、加密、审核日志记录和事件响应准备。
要点
- 从安全角度来看,默认的 Odoo 配置尚未准备好用于生产
- 对于具有敏感访问权限的所有用户,应强制执行双因素身份验证 (2FA)
- 访问权限遵循最小权限原则——用户仅获得他们需要的内容
- 记录规则提供行级安全性以强制数据隔离
- 所有服务间通信必须使用TLS;生产环境中从不使用 HTTP
- 数据库凭据必须定期轮换且切勿硬编码
- 审核日志必须防篡改并保留以符合合规要求
- 应在 Odoo SA 公告后 48 小时内应用安全补丁
身份验证强化
密码政策
Odoo 19 支持在设置→用户和公司→密码重置策略下配置密码策略。
推荐设置:
- 最小长度:12 个字符
- 需要大写、小写、数字和特殊字符
- 密码有效期:90 天
- 防止重复使用最后 12 个密码
- 尝试失败 5 次后帐户将被锁定(15 分钟锁定)
# Custom password validator in a security module
from odoo import api, models
from odoo.exceptions import UserError
import re
class ResUsers(models.Model):
_inherit = 'res.users'
@api.constrains('password')
def _check_password_strength(self):
for user in self:
if user.password:
pwd = user.password
if len(pwd) < 12:
raise UserError("Password must be at least 12 characters.")
if not re.search(r'[A-Z]', pwd):
raise UserError("Password must contain an uppercase letter.")
if not re.search(r'[a-z]', pwd):
raise UserError("Password must contain a lowercase letter.")
if not re.search(r'\d', pwd):
raise UserError("Password must contain a number.")
if not re.search(r'[!@#$%^&*(),.?":{}|<>]', pwd):
raise UserError("Password must contain a special character.")
双因素身份验证 (2FA)
在设置→权限→双因素身份验证下启用 2FA。
配置选项:
- 可选:用户选择是否启用2FA
- 特定群体需要:适用于管理员和会计师
- 所有用户都需要:最大安全性
对于企业部署,需要 2FA:
- 管理员帐户 (
base.group_system) - 金融用户 (
account.group_account_manager) - 拥有薪资访问权限的人力资源经理
- 所有具有 API 密钥访问权限的用户
OAuth 和 SSO 集成
对于拥有现有身份提供商的组织,将 Odoo 配置为使用 SAML 2.0 或 OAuth 2.0:
# In odoo.conf — configure OAuth
oauth_providers = [
{
'name': 'Your SSO Provider',
'client_id': 'odoo-client-id',
'auth_endpoint': 'https://sso.company.com/auth',
'validation_endpoint': 'https://sso.company.com/userinfo',
'scope': 'openid email profile',
}
]
这可以在您的身份提供商中集中进行身份验证,从而实现单点登录、集中式 MFA 以及员工离职时自动帐户取消配置等功能。
API 密钥安全
# Restrict API key creation to administrators only
# In custom security module:
# ir.rule to prevent non-admin users from creating API keys
<record id="rule_api_key_admin_only" model="ir.rule">
<field name="name">API Keys: Admin Only</field>
<field name="model_id" ref="base.model_auth_api_key"/>
<field name="domain_force">[('create_uid', '=', user.id)]</field>
<field name="groups" eval="[(4, ref('base.group_user'))]"/>
<field name="perm_create" eval="False"/>
<field name="perm_unlink" eval="False"/>
</record>
执行策略:API 密钥将在 90 天后过期,与具有最低权限的专用服务帐户绑定,并在任何可疑的泄露时轮换。
访问权限架构
组层次结构设计
Odoo 的访问控制基于组。设计反映您的组织结构的组层次结构:
base.group_user (Internal User)
├── sales.group_sale_salesman (Salesperson)
│ └── sales.group_sale_manager (Sales Manager)
├── account.group_account_user (Accounting User)
│ └── account.group_account_manager (Accounting Manager)
├── stock.group_stock_user (Inventory User)
│ └── stock.group_stock_manager (Inventory Manager)
└── base.group_system (Administrator)
最小权限原则:每个用户都应该拥有执行其工作所需的最低访问权限。这限制了受感染帐户的爆炸半径。
访问控制列表 (ACL)
ir.model.access 中的 ACL 条目定义模型级 CRUD 权限:
id,name,model_id,group_id,perm_read,perm_write,perm_create,perm_unlink
# Salesperson: can read/write own orders, cannot delete
access_sale_order_salesman,sale.order.salesman,sale.model_sale_order,sales.group_sale_salesman,1,1,1,0
# Manager: full CRUD
access_sale_order_manager,sale.order.manager,sale.model_sale_order,sales.group_sale_manager,1,1,1,1
# Accounting user: read-only on sales orders
access_sale_order_accountant,sale.order.accountant,sale.model_sale_order,account.group_account_user,1,0,0,0
记录规则(行级安全性)
记录规则过滤器,记录用户可以在模型中看到的记录:
<!-- Salesperson sees only their own opportunities -->
<record id="rule_crm_salesman" model="ir.rule">
<field name="name">CRM: Own Leads</field>
<field name="model_id" ref="crm.model_crm_lead"/>
<field name="domain_force">
['|', ('user_id', '=', user.id), ('user_id', '=', False)]
</field>
<field name="groups" eval="[(4, ref('sales.group_sale_salesman'))]"/>
</record>
<!-- Multi-company isolation: users see only their company's records -->
<record id="rule_sale_order_company" model="ir.rule">
<field name="name">Sale Order: Company</field>
<field name="model_id" ref="sale.model_sale_order"/>
<field name="domain_force">
[('company_id', 'in', company_ids)]
</field>
<field name="groups" eval="[(4, ref('base.group_user'))]"/>
</record>
性能警告:具有许多 search() 或 child_of 运算符的复杂记录规则会显着减慢列表视图的速度。在部署到生产之前,针对大型数据集测试所有记录规则。
网络安全
TLS/HTTPS 执行
切勿在生产中通过纯 HTTP 运行 Odoo。所有流量都必须加密。
Nginx TLS 配置:
server {
listen 80;
server_name your-odoo.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name your-odoo.com;
ssl_certificate /etc/ssl/certs/your-odoo.com.pem;
ssl_certificate_key /etc/ssl/private/your-odoo.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:
ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m;
ssl_session_tickets off;
# HSTS: 2 years, include subdomains
add_header Strict-Transport-Security
"max-age=63072000; includeSubDomains; preload" always;
# Security headers
add_header X-Frame-Options DENY always;
add_header X-Content-Type-Options nosniff always;
add_header Referrer-Policy strict-origin-when-cross-origin always;
add_header Permissions-Policy
"camera=(), microphone=(), geolocation=()" always;
}
防火墙配置
# UFW firewall rules for Odoo server
ufw default deny incoming
ufw default allow outgoing
# Allow SSH from management IP only
ufw allow from 10.0.1.100 to any port 22
# Allow HTTPS from anywhere
ufw allow 443/tcp
# Allow HTTP (for Let's Encrypt renewal)
ufw allow 80/tcp
# Allow PostgreSQL only from Odoo server (if separate DB server)
ufw allow from 10.0.1.10 to any port 5432
# Block direct access to Odoo port (must go through Nginx)
ufw deny 8069/tcp
ufw deny 8072/tcp
ufw enable
速率限制
在Nginx中配置速率限制以防止暴力攻击:
# Rate limiting zones
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
limit_req_zone $binary_remote_addr zone=api:10m rate=60r/m;
server {
# Apply rate limiting to login endpoint
location /web/login {
limit_req zone=login burst=3 nodelay;
limit_req_status 429;
proxy_pass http://odoo;
}
# Apply rate limiting to API
location /web/dataset/ {
limit_req zone=api burst=20 nodelay;
proxy_pass http://odoo;
}
}
数据加密
数据库级加密
对于高度敏感的数据,Odoo 字段可以静态加密:
# Custom encrypted field using Fernet symmetric encryption
from cryptography.fernet import Fernet
from odoo import api, fields, models
import base64, os
class EncryptedField(models.Model):
_name = 'my.sensitive.model'
_fernet_key = os.environ.get('ODOO_FIELD_ENCRYPTION_KEY')
ssn_encrypted = fields.Char(string='SSN (Encrypted)', groups='hr.group_hr_user')
ssn_display = fields.Char(
string='SSN', compute='_compute_ssn_display',
inverse='_inverse_ssn_display'
)
def _get_fernet(self):
return Fernet(self._fernet_key.encode())
def _compute_ssn_display(self):
f = self._get_fernet()
for record in self:
if record.ssn_encrypted:
decrypted = f.decrypt(record.ssn_encrypted.encode()).decode()
record.ssn_display = f"***-**-{decrypted[-4:]}"
else:
record.ssn_display = False
def _inverse_ssn_display(self):
f = self._get_fernet()
for record in self:
if record.ssn_display:
encrypted = f.encrypt(record.ssn_display.encode()).decode()
record.ssn_encrypted = encrypted
环境变量安全
切勿以明文形式将机密存储在 odoo.conf 中。使用环境变量:
# /etc/systemd/system/odoo.service
[Service]
Environment="ODOO_DB_PASSWORD=your_secure_password"
Environment="ODOO_ADMIN_PASSWD=your_master_password"
Environment="ODOO_FIELD_ENCRYPTION_KEY=your_fernet_key"
EnvironmentFile=/etc/odoo/secrets.env
# odoo.conf — reference env vars
[options]
db_password = ${ODOO_DB_PASSWORD}
admin_passwd = ${ODOO_ADMIN_PASSWD}
审计日志记录
Odoo 的内置审核日志记录会跟踪所有模型更改。为敏感模型启用它:
# Enable audit logging via the Audit Trail module
# Or implement custom logging:
class AuditableMixin(models.AbstractModel):
_name = 'audit.mixin'
def write(self, vals):
# Log what changed before the write
for record in self:
old_vals = {f: record[f] for f in vals if f in record._fields}
self.env['audit.log'].create({
'model_name': self._name,
'record_id': record.id,
'user_id': self.env.uid,
'action': 'write',
'old_values': str(old_vals),
'new_values': str(vals),
'ip_address': self.env.context.get('remote_addr'),
})
return super().write(vals)
def unlink(self):
for record in self:
self.env['audit.log'].create({
'model_name': self._name,
'record_id': record.id,
'user_id': self.env.uid,
'action': 'unlink',
'old_values': str({f: record[f] for f in ['name', 'id']}),
})
return super().unlink()
要审核的合规性关键模型:
res.users(用户创建、权限更改)account.move(发票修改)hr.payslip(工资变更)res.partner(客户/供应商 PII 更改)ir.model.access、ir.rule(权限更改)ir.config_parameter(系统配置更改)
安全补丁管理
订阅 Odoo 安全公告:
Odoo SA 在 https://github.com/odoo/odoo/security/advisories 发布安全公告。订阅此存储库的 GitHub 通知。
补丁申请流程:
# Check current Odoo version
python3 odoo-bin --version
# Pull latest security patches (within a minor version)
cd /opt/odoo
git fetch origin
git log origin/19.0..HEAD --oneline # See what's changed
# Apply patches on staging first
git merge origin/19.0
python3 odoo-bin -d staging_db -u all --stop-after-init
# Test critical workflows on staging
# If all clear, apply to production during maintenance window
目标:在出现严重或高严重性通报后 48 小时内修复。中等严重程度:1 周内。低严重性:下一个计划维护时段。
常见问题
如何审核谁在 Odoo 中删除了记录?
默认情况下,Odoo 不记录删除。安装 Odoo 社区协会 (OCA) 的 auditlog 模块或在敏感模型中实现自定义 unlink() 覆盖。 OCA 审核日志模块将创建、写入和取消链接操作记录到 auditlog.log 模型,并提供用户、时间戳和字段级更改跟踪。
我可以通过 IP 地址限制 Odoo 访问吗?
不是 Odoo 原生的。在 Nginx 或防火墙级别实施 IP 白名单。对于管理访问,需要 VPN 连接作为先决条件。 Odoo 的 ir.config_parameter 确实支持 web.base.url 限制,但真正的基于 IP 的访问控制属于网络层。
访问权限(ACL)和记录规则有什么区别?
访问权限(ACL,存储在 ir.model.access 中)控制用户组是否可以读取、写入、创建或删除模型的任何记录 - 这是模型级控制。记录规则使用域表达式过滤用户可以在模型中访问哪些特定记录 - 这是行级控制。两者协同工作:用户需要 ACL 读取权限并且必须通过记录规则域过滤器才能查看记录。
我应该如何处理 Odoo 主密码 (admin_passwd)?
odoo.conf 中的 admin_passwd 控制对 /web/database/manager 处数据库管理界面的访问。在生产中:将其设置为长随机字符串(超过 32 个字符),将其存储在密码管理器中,切勿共享它,如果您通过直接 PostgreSQL 访问管理数据库,请考虑使用 odoo.conf 中的 list_db = False 完全禁用数据库管理器。
我应该以 root 身份运行 Odoo 吗?
绝不。创建一个没有 shell 访问权限且文件系统权限有限的专用 odoo 系统用户。 odoo 用户需要对 Odoo 代码库进行读取访问,并且仅对文件存储和日志目录进行写入访问。以该用户身份运行 systemd 服务。以 root 身份运行违反了最小权限原则,并且使任何代码执行漏洞造成的损害最大化。
后续步骤
安全强化不是一项一次性任务,而是一项持续的实践。新的漏洞不断出现,用户访问需求发生变化,合规框架不断发展。安全的 Odoo 部署需要定期安全审查、补丁管理规则和访问权限审核。
ECOSIRE 提供 Odoo 安全强化评估,可识别错误配置、过多权限、缺失控制和合规性差距。我们的安全团队在 Odoo 环境中满足 ISO 27001、SOC 2 和 GDPR 要求方面拥有丰富的经验。
我们将根据行业标准审查您当前的配置,并提供优先的修复计划和实施支持。
作者
ECOSIRE TeamTechnical Writing
The ECOSIRE technical writing team covers Odoo ERP, Shopify eCommerce, AI agents, Power BI analytics, GoHighLevel automation, and enterprise software best practices. Our guides help businesses make informed technology decisions.
相关文章
电子商务人工智能欺诈检测:在不阻止销售的情况下保护收入
实施 AI 欺诈检测,捕获 95% 以上的欺诈交易,同时将误报率控制在 2% 以下。机器学习评分、行为分析和投资回报率指南。
AI 支持的客户细分:从 RFM 到预测聚类
了解 AI 如何将客户细分从静态 RFM 分析转变为动态预测聚类。使用 Python、Odoo 和真实 ROI 数据的实施指南。
用于供应链优化的人工智能:可见性、预测和自动化
利用人工智能改变供应链运营:需求感知、供应商风险评分、路线优化、仓库自动化和中断预测。 2026年指南。