[IMP] csrf: better warning and documentation

After initial implementation CSRF protection had been left poorly
documented tripping up users and developers (#9538, #10139).

Add a warning in the logs for developers, and a more extensive
explanation of the whole thing in the @route docstring (and the official
documentation).

Fixes #10158
This commit is contained in:
Xavier Morel
2015-12-21 13:10:45 +01:00
parent f839c9358a
commit 8595bc2ff7
+76 -10
View File
@@ -420,8 +420,7 @@ class WebRequest(object):
return consteq(hm, hm_expected)
def route(route=None, **kw):
"""
Decorator marking the decorated method as being a handler for
"""Decorator marking the decorated method as being a handler for
requests. The method must be part of a subclass of ``Controller``.
:param route: string or array. The route part that will determine which
@@ -446,16 +445,55 @@ def route(route=None, **kw):
:param cors: The Access-Control-Allow-Origin cors directive value.
:param bool csrf: Whether CSRF protection should be enabled for the route.
Defaults to ``True``.
Defaults to ``True``. See :ref:`CSRF Protection
<csrf>` for more.
CSRF protection only applies to *UNSAFE* methods as
defined by :rfc:`7231`: GET, HEAD, TRACE and OPTIONS are
safe, all other methods are unsafe.
.. _csrf:
.. admonition:: CSRF Protection
:class: alert-warning
.. versionadded:: 9.0
Odoo implements token-based `CSRF protection
<https://en.wikipedia.org/wiki/CSRF>`_.
CSRF protection is enabled by default and applies to *UNSAFE*
HTTP methods as defined by :rfc:`7231` (all methods other than
``GET``, ``HEAD``, ``TRACE`` and ``OPTIONS``).
CSRF protection is implemented by checking requests using
unsafe methods for a value called ``csrf_token`` as part of
the request's form data. That value is removed from the form
as part of the validation and does not have to be taken in
account by your own form processing.
When adding a new controller for an unsafe method (mostly POST
for e.g. forms):
* if the form is generated in Python, a csrf token is
available via :meth:`request.csrf_token()
<openerp.http.WebRequest.csrf_token`, the
:data:`~openerp.http.request` object is available by default
in QWeb (python) templates, it may have to be added
explicitly if you are not using QWeb.
* if the form is generated in Javascript, the CSRF token is
added by default to the QWeb (js) rendering context as
``csrf_token`` and is otherwise available as ``csrf_token``
on the ``web.core`` module:
.. code-block:: javascript
require('web.core').csrf_token
* if the endpoint can be called by external parties (not from
Odoo) as e.g. it is a REST API or a `webhook
<https://en.wikipedia.org/wiki/Webhook>`_, CSRF protection
must be disabled on the endpoint. If possible, you may want
to implement other methods of request validation (to ensure
it is not called by an unrelated third-party).
CSRF protection requires a csrf token to be sent as part
of the request's form data, it can be obtained via
:meth:`request.csrf_token()
<openerp.http.WebRequest.csrf_token>`
"""
routing = kw.copy()
assert 'type' not in routing or routing['type'] in ("http", "json")
@@ -769,6 +807,34 @@ class HttpRequest(WebRequest):
and request.endpoint.routing.get('csrf', True): # csrf checked by default
token = self.params.pop('csrf_token', None)
if not self.validate_csrf(token):
if token is not None:
_logger.warn("CSRF validation failed on path '%s'",
request.httprequest.path)
else:
_logger.warn("""No CSRF validation token provided for path '%s'
Odoo URLs are CSRF-protected by default (when accessed with unsafe
HTTP methods). See
https://www.odoo.com/documentation/9.0/reference/http.html#csrf for
more details.
* if this endpoint is accessed through Odoo via py-QWeb form, embed a CSRF
token in the form, Tokens are available via `request.csrf_token()`
can be provided through a hidden input and must be POST-ed named
`csrf_token` e.g. in your form add:
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
* if the form is generated or posted in javascript, the token value is
available as `csrf_token` on `web.core` and as the `csrf_token`
value in the default js-qweb execution context
* if the form is accessed by an external third party (e.g. REST API
endpoint, payment gateway callback) you will need to disable CSRF
protection (and implement your own protection if necessary) by
passing the `csrf=False` parameter to the `route` decorator.
""", request.httprequest.path)
raise werkzeug.exceptions.BadRequest('Invalid CSRF Token')
r = self._call_function(**self.params)