Skip to content

Commit c4f7047

Browse files
Merge pull request #12264 from nextcloud/fix/clarification-rest-ocs
2 parents 02a3a20 + 179d774 commit c4f7047

4 files changed

Lines changed: 202 additions & 7 deletions

File tree

developer_manual/basics/controllers.rst

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,10 @@ A :doc:`template <front-end/templates>` can be rendered by returning a TemplateR
393393
394394
}
395395
396+
Showing a template is the only exception to the rule to :ref:`not disable CSRF checks <csrf_introduction>`:
397+
The user might type the URL directly (or use a browser bookmark or similar) to navigate to a HTML template.
398+
Therefore, usage of the ``#[NoCSRFRequired]`` attribute (see :ref:`below<controller_authentication>`) is acceptable in this context.
399+
396400
Public page templates
397401
^^^^^^^^^^^^^^^^^^^^^
398402

@@ -434,6 +438,11 @@ A ``OCP\\AppFramework\\Http\\Template\\SimpleMenuAction`` will be a link with an
434438
developers can implement their own types of menu renderings by adding a custom
435439
class implementing the ``OCP\\AppFramework\\Http\\Template\\IMenuAction`` interface.
436440

441+
As the public template is also some HTML template, the same argumentation as for :ref:`regular templates<controller_template>` regarding the CSRF checks hold true:
442+
The usage of ``#[NoCSRFRequired]`` for public pages is considered acceptable for some pages:
443+
Each page that the user should be able to directly access (by typing/pastig the URL in the browser or clicking on a link in a mail) should have this attribute set.
444+
For multi-page forms in the second and later stages, this should **not** be set as the user should follow the series of pages.
445+
437446
Data-based responses
438447
--------------------
439448

@@ -448,10 +457,14 @@ The user only indirectly requested the data by user interaction with the fronten
448457
OCS
449458
^^^
450459

460+
In order to simplify exchange of data between the Nextcloud backend and any client (be it the web frontend or whatever else), the OCS API has been introduced.
461+
Here, JSON and XML responders have been prepared and are installed without additional effort.
462+
451463
.. note::
452-
This is purely for compatibility reasons. If you are planning to offer an external API, go for a :ref:`REST APIs <rest-apis>` instead.
464+
The usage of OCS is closely related to the usage of :doc:`../digging_deeper/rest_apis`.
465+
Unless you have a clear use-case, it is advised to use OCS over pure REST.
466+
A more detailed description can be found in :ref:`ocs-vs-rest`.
453467

454-
In order to simplify exchange of data between the Nextcloud backend and any client (be it the web frontend or whatever else), the OCS API has been introduced.
455468
To use OCS in your API you can use the **OCP\\AppFramework\\OCSController** base class and return your data in the form of a **DataResponse** in the following way:
456469

457470
.. code-block:: php
@@ -509,6 +522,10 @@ Now your method will be reachable via ``<server>/ocs/v2.php/apps/<APPNAME>/api/v
509522
JSON
510523
^^^^
511524

525+
.. warning::
526+
The usage of standard controller to access content data like JSON (no HTML) is considered legacy.
527+
Better use :ref:`OCS <ocscontroller>` for this type of requests.
528+
512529
Returning JSON is simple, just pass an array to a JSONResponse:
513530

514531
.. code-block:: php
@@ -547,6 +564,11 @@ Because returning JSON is such a common task, there's even a shorter way to do t
547564
548565
Why does this work? Because the dispatcher sees that the controller did not return a subclass of a Response and asks the controller to turn the value into a Response. That's where responders come in.
549566

567+
.. deprecated:: 30
568+
569+
Usage of "index.php"-controllers for data transmission should be avoided. Use OCS instead.
570+
571+
550572
Handling errors
551573
^^^^^^^^^^^^^^^
552574

developer_manual/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@
100100
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-latex-output
101101

102102
latex_elements = {
103-
"preamble": "\extrafloats{100}\maxdeadcycles=500\DeclareUnicodeCharacter{274C}{\sffamily X}",
103+
"preamble": "\\extrafloats{100}\\maxdeadcycles=500\\DeclareUnicodeCharacter{274C}{\\sffamily X}",
104104
}
105105
latex_documents = [
106106
(

developer_manual/digging_deeper/rest_apis.rst

Lines changed: 147 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ REST APIs
66

77
.. sectionauthor:: Bernhard Posselt <dev@bernhard-posselt.com>
88

9-
Offering a RESTful API is not different from creating a :doc:`route <../basics/routing>` and :doc:`controllers <../basics/controllers>` for the web interface. It is recommended though to inherit from ApiController and add ``#[CORS]`` attribute to the methods so that `web applications will also be able to access the API <https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS>`_.
9+
Offering a RESTful API is not different from creating a :doc:`route <../basics/routing>` and :doc:`controllers <../basics/controllers>` for the web interface.
10+
It is recommended though to inherit from ApiController and add ``#[CORS]`` attribute to the methods so that `web applications will also be able to access the API <https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS>`_.
1011

1112
.. code-block:: php
1213
@@ -44,7 +45,8 @@ CORS also needs a separate URL for the preflighted **OPTIONS** request that can
4445
)
4546
4647
47-
Keep in mind that multiple apps will likely depend on the API interface once it is published and they will move at different speeds to react to changes implemented in the API. Therefore it is recommended to version the API in the URL to not break existing apps when backwards incompatible changes are introduced::
48+
Keep in mind that multiple apps will likely depend on the API interface once it is published and they will move at different speeds to react to changes implemented in the API.
49+
Therefore it is recommended to version the API in the URL to not break existing apps when backwards incompatible changes are introduced::
4850

4951
/index.php/apps/myapp/api/1.0/resource
5052

@@ -79,3 +81,146 @@ To add an additional method or header or allow less headers, simply pass additio
7981
}
8082
8183
}
84+
85+
.. _ocs-vs-rest:
86+
87+
Relation of REST and OCS
88+
------------------------
89+
90+
There is a close relationship between REST APIs and :ref:`OCS <ocscontroller>`.
91+
Both provide a way to transmit data between the backend of the app in the Nextcloud server and some frontend.
92+
This is explicitly not about :ref:`HTML template responses <controller_html_responses>`.
93+
94+
State-of-the-Art methods and comparison
95+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
96+
97+
The following combinations of attributes might be relevant for various scenarios:
98+
99+
#. Plain frontend route: ``Controller`` class
100+
#. OCS route: ``OCSController`` class
101+
#. OCS route with CORS enabled: ``OCSController`` class and ``#[CORS]`` attribute on the method
102+
103+
.. warning::
104+
Adding the ``#[NoCRSFRequired]`` attribute imposes a security risk.
105+
You should not add this to your controller methods unless you understand the implications and be sure that you absolutely need the attribute.
106+
Typically, you can use the ``OCS-APIRequest`` header for data requests instead, in order to satisfy the CSRF checks in your API requests.
107+
108+
.. warning::
109+
Adding the attribute ``#[CORS]`` alone is not sufficient to allow access using CORS with plain frontend routes.
110+
Without further measures, the CSRF checker would fail.
111+
So, enabling CORS for plain controllers is generally and highly discouraged.
112+
113+
You would have to disable the CSRF checks (one more security risk) or use the ``OCP-APIRequest`` header or send a CSRF token to successfully pass the checks.
114+
The latter requires dedicated JS code on the importing page.
115+
116+
There are different ways a clients might interact with your APIs.
117+
These ways depend on your API configuration (what you allow) and on which route the request is finally made.
118+
119+
- *Access from web frontend* means the user is accessing the Nextcloud web frontend with a web browser.
120+
- *Access from non-browser* is if the user accesses the resource or page using something that is not a web browser, like an Android app or a curl command.
121+
- *Access from external website* means that the user browses some third party web site and data from your Nextcloud server appears.
122+
The other website has to embed/load/use images, JSON data, or other resources from a URL pointing to the Nextcloud server, to be able to do this.
123+
124+
.. hint::
125+
The discussion here is for data requests only.
126+
If you think of controller :ref:`methods serving (HTML) templates <controller_html_responses>`, disabling CSRF is considered fine.
127+
128+
.. list-table:: Comparison of different API types
129+
:header-rows: 1
130+
:align: center
131+
132+
* - Description
133+
- ``Controller`` class
134+
- ``OCSController`` class
135+
- ``OCSController`` class & ``CORS`` on method
136+
* - URL prefix (relative to server)
137+
- ``/apps/<appid>/``
138+
- ``/ocs/v2.php/apps/<appid>/``
139+
- ``/ocs/v2.php/apps/<appid>/``
140+
* - Access from web frontend
141+
- yes
142+
- yes
143+
- yes
144+
* - Access from non-browser
145+
- partial [#]_
146+
- yes
147+
- yes
148+
* - Access from external website
149+
- no
150+
- no
151+
- yes
152+
* - Encapsulated data
153+
- no
154+
- yes (JSON or XML)
155+
- yes (JSON or XML)
156+
157+
.. [#] The external app has to satisfy the CSRF checks.
158+
That is, you need to have the ``OCS-APIRequest`` HTTP request header set to ``true``.
159+
This is only possible for Nextcloud 30 onwards, older versions do not respect the header.
160+
161+
Methods from ``Controller`` classes can return ``DataResponse`` objects similar to ``OCSController`` class methods.
162+
For methods of a ``Controller`` class, the data of this response is sent e.g. as JSON as you provide it.
163+
Basically, the output is very similar to what ``json_encode`` would do.
164+
In contrast, the ``OCSController`` will encapsulate the data in an outer shell that provides some more (meta) information.
165+
For example a status code (similar to the HTTP status code) is transmitted at top level.
166+
The actual data is transmitted in the ``data`` property.
167+
168+
As a rule of thumb one can conclude that OCS provides a good way to handle most use cases including sufficient security checks.
169+
The only exception to this is if you want to provide an API for external usage where you have to comply with an externally defined API scheme.
170+
Here, the encapsulation introduced in OCS and CSRF checks might be in your way.
171+
172+
173+
Historical options
174+
~~~~~~~~~~~~~~~~~~
175+
176+
.. deprecated:: 30
177+
The information in this section are mainly for reference purposes. Do not use the approaches in new code.
178+
179+
Before Nextcloud 30 the plain ``Controller`` classes' methods did not respect the ``OCS-APIRequest`` header.
180+
Thus, to provide access to this type of controller methods for external apps, it was necessary to use the ``#[NoCSRFRequired]`` attribute (or the corresponding ``@NoCSRFRequired`` annotation).
181+
182+
The following combinations of attributes were relevant for various scenarios:
183+
184+
#. Plain frontend route: ``Controller`` class
185+
#. Plain frontend with CRSF checks disabled: ``Controller`` class and ``#[NoCSRFRequired]`` attribute on the method
186+
#. Plain frontend route with CORS enabled: ``Controller`` class and ``#[CORS]`` and ``#[NoCSRFRequired]`` attributes on the route
187+
#. OCS route: ``OCSController`` class
188+
#. OCS route with CORS enabled: ``OCSController`` class and ``#[CORS]`` attribute on the method
189+
190+
.. hint::
191+
The two scenarios involving the ``OCSController`` have not changed and, thus, the state-of-the-art documentation as noted above still holds true.
192+
Thus, these options are not reconsidered here again for simplicity reasons and to get the overall view more crisp.
193+
194+
The warnings about not using ``NoCSRFRequired`` and ``CORS`` as mentioned in the state-of-the-art section holds true here as well.
195+
196+
.. list-table:: Comparison of different API types
197+
:header-rows: 1
198+
:align: center
199+
200+
* - | Description
201+
- | ``Controller`` class
202+
- | ``Controller`` class with
203+
| ``NoCSRFRequired`` on method
204+
- | ``Controller`` class with
205+
| ``NoCSRFRequired`` and ``CORS``
206+
| on method
207+
* - URL prefix (relative to server)
208+
- ``/apps/<appid>/``
209+
- ``/apps/<appid>/``
210+
- ``/apps/<appid>/``
211+
* - Access from web frontend
212+
- yes
213+
- yes (CSRF risk)
214+
- yes (CSRF risk)
215+
* - Access from non-browser
216+
- no
217+
- yes
218+
- yes
219+
* - Access from external website
220+
- no
221+
- no
222+
- yes
223+
* - Encapsulated data
224+
- no
225+
- no
226+
- no

developer_manual/prologue/security.rst

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,10 +213,12 @@ Sensitive data exposure
213213

214214
Always store user data or configuration files in safe locations, e.g. **nextcloud/data/** and not in the webroot where they can be accessed by anyone using a web browser.
215215

216+
.. _csrf_introduction:
217+
216218
Cross site request forgery
217219
--------------------------
218220

219-
Using `CSRF <https://en.wikipedia.org/wiki/Cross-site_request_forgery>`_ one can trick a user into executing a request that they did not want to make. Thus every POST and GET request needs to be protected against it. The only places where no CSRF checks are needed are in the main template, which is rendering the application, or in externally callable interfaces.
221+
Using `CSRF <https://en.wikipedia.org/wiki/Cross-site_request_forgery>`_ (see also on `MDN <https://developer.mozilla.org/en-US/docs/Glossary/CSRF>`__) one can trick a user into executing a request that they did not want to make. Thus every POST and GET request needs to be protected against it. The only places where no CSRF checks are needed are in the main template, which is rendering the application, or in externally callable interfaces.
220222

221223
.. note:: Submitting a form is also a POST/GET request!
222224

@@ -227,7 +229,11 @@ To prevent CSRF in an app, be sure to call the following method at the top of al
227229
<?php
228230
OCP\JSON::callCheck();
229231
230-
If you are using the App Framework, every controller method is automatically checked for CSRF unless you explicitly exclude it by setting the ``#[NoCSRFRequired]`` attribute or ``@NoCSRFRequired`` annotation before the controller method, see :doc:`../basics/controllers`
232+
If you are using the App Framework, every controller method is automatically checked for CSRF unless you explicitly exclude it by setting the ``#[NoCSRFRequired]`` attribute or ``@NoCSRFRequired`` annotation before the controller method, see :doc:`../basics/controllers`.
233+
234+
Additionally, it is advised to carefully select the HTTP method used for requests.
235+
Requests of type ``GET`` should not alter data but just read existing data.
236+
This way, at least no typed (or copied) URL might alter data (e.g. clicking a link from a spam mail message by accident).
231237

232238
Unvalidated redirects
233239
---------------------
@@ -250,6 +256,28 @@ Always validate the URL before redirecting if the requested URL is on the same d
250256
<?php
251257
header('Location: https://example.com'. $_GET['redirectURL']);
252258
259+
260+
CORS
261+
----
262+
263+
`Cross-origin resource sharing (CORS) <https://en.wikipedia.org/wiki/Cross-origin_resource_sharing>`_ (see also on `MDN <https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS>`__) is a method implemented by browser to access resources from different domains at the same time.
264+
Assume, there is a website published on host A.
265+
The URL would for example be ``https://A/path/to/index.html``.
266+
If there is a _different_ host B that serves a resource (e.g. an image file) as ``https://B/assets/image.jpg``, the index file on host A could simply link to the image on B.
267+
However, to protect B and its property (the image), the browsers do not silently embed the image of B into the page of A.
268+
Instead, B is kindly asked by the browser if embedding is allowed (the so-called `preflight <https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request>`_).
269+
270+
To do so, there is a first request made to the resource on B with the ``OPTIONS`` HTTP command/verb.
271+
The server only answers with the headers as specified and adds ``Access-Control-*`` headers.
272+
These define, what the browser is to be allowed to do.
273+
Only if the destination server B confirms cross site resource sharing is allowed, the browser access the resource.
274+
275+
Basically, accessing foreign resources is not limited to embedding images.
276+
Using JavaScript, arbitrary XHR/Ajax requests can be directed at arbitrary other hosts, which might be used to call APIs that leak your data.
277+
There are some safety measurements in place (especially about cookie handling), but one has still to be careful not to leak information unwillingly.
278+
Especially, if the destination server B allows to sent credentials using ``Access-Control-Allow-Credentials: true``, cross site scripting is very critical.
279+
You need :ref:`CSRF protection <csrf_introduction>` in place or your users are at relatively high risk.
280+
253281
Getting help
254282
------------
255283

0 commit comments

Comments
 (0)