Commit d734af94 authored by colemanw's avatar colemanw
Browse files

Merge branch 'master-authx-rest' into 'master'

APIv3 REST: Document CSRF, backward-compatibility, migration

See merge request documentation/docs/dev!982
parents 5a497857 c0e5a3e0
......@@ -6,6 +6,99 @@ Also see: [Differences Between Api v3 and v4](../v4/differences-with-v3.md) and
## APIv3: Framework
### 5.47 Migration from `extern/rest.php` to `civicrm/ajax/rest` {:#restendpoint}
The traditional [APIv3 REST end-point](rest.md#end-point-url), `extern/rest.php`, is not supported by some common environments and lacks some newer features.
As of v5.47, `civicrm/ajax/rest` may be used as a drop-in replacement. It supports a wider range of environments and authentication protocols.
For a minimal migration, change the URL. For a full migration, you may also change the authentication protocol.
??? question "Change URL to `civicrm/ajax/rest` (Reasons)"
1. Stop maintaining manual copy of `extern/rest.php`.
2. Improve compatibility with upgraded, migrated, or reconfigured CMS.
??? example "Change URL to `civicrm/ajax/rest` (Example)"
On the CiviCRM server, enable the extension [AuthX](../../framework/authx.md). (*Navigate to "Administer => System Settings => Extensions" and enable "AuthX".*)
On the CiviCRM client, change the URL. Suppose you were sending requests via `curl`. The old request was:
```bash
curl -X 'POST' -d 'entity=Contact&action=get&json=1&key=MY_SITE_KEY&api_key=MY_API_KEY' \
'http://example.org/sites/all/modules/civicrm/extern/rest.php'
```
The new request would be:
```bash
curl -X 'POST' -d 'entity=Contact&action=get&json=1&key=MY_SITE_KEY&api_key=MY_API_KEY' \
'http://example.org/civicrm/ajax/rest'
```
Here are a few more examples of old and new URLs:
```
/* Old URLs */
https://backdrop.example.org/modules/civicrm/extern/rest.php
https://drupal.example.org/sites/all/modules/civicrm/extern/rest.php
https://wordpresss.example.org/wp-content/plugins/civicrm/civicrm/extern/rest.php
/* New URLs */
https://backdrop.example.org/civicrm/ajax/rest
https://drupal.example.org/civicrm/ajax/rest
https://wordpress.example.org/wp-admin/admin.php?page=CiviCRM&q=civicrm/ajax/rest
```
<!-- FIXME: Can we rely on WP clean URLs for this? -->
<!-- Haven't tested Joomla. Expecting http://joomla.example.org/administrator/index.php?option=com_civicrm&task=civicrm/ajax/rest&reset=1 -->
??? question "Change authentication protocol (Reasons)"
1. __Remove dependence on `CIVICRM_SITE_KEY`. Use role-based access-control.__
AuthX does not require sharing `CIVICRM_SITE_KEY`. Instead, you configure access-control by granting or revoking permissions
(`authenticate with api key` and/or `authenticate with password`).
2. __Support username/password, JWT, and/or API key.__
API keys are great - but not always. AuthX still accepts API keys -- but it also supports JSON Web Tokens (*by default*)
and usernames/passwords (*requires opt-in*).
3. __Support more authentication flows (with more correct session-management).__
Legacy REST performed authentication with HTTP parameters (`?key=...&api_key=...`). This looked like a "stateless HTTP" style, but
it often left behind extraneous records in session-storage. AuthX supports both stateless styles (eg HTTP parameters and HTTP
headers) and stateful styles (eg HTTP login/session). Stateless styles do not leave behind extraneous records in session-storage.
4. __Support a wide range of CiviCRM end-points.__
Legacy REST authentication only supported APIv3. With AuthX, you may access any `civicrm/ajax/*` end-point.
??? example "Change authentication protocol (Example)"
Legacy REST authentication is very similar to AuthX's "HTTP Parameter" authentication. Here are the steps to switch:
1. Change the URL to `civicrm/ajax/rest`, as described earlier.
2. On the CiviCRM server, verify the settings. Navigate to "Administer => System Settings => Authentication". "HTTP
Parameter" should accept credentials of type "API Key". (*This should be enabled by default.*)
3. On the CiviCRM server, ensure that appropriate users have permission `authenticate with api key`.
4. On the CiviCRM client, remove `?key=...&api_key=...`. Add `?_authx=...`. For an API key, the value must begin with `Bearer`.
```bash
## Legacy REST authentication
curl -X 'POST' -d 'entity=Contact&action=get&json=1&key=MY_SITE_KEY&api_key=MY_API_KEY' \
'http://example.org/civicrm/ajax/rest'
## AuthX HTTP parameter authentication
curl -X 'POST' -d 'entity=Contact&action=get&json=1&_authx=Bearer+MY_API_KEY' \
'http://example.org/civicrm/ajax/rest'
```
### 5.12 Support for string 'NULL' as an accepted value for custom data fields
The API has now been changed such that when setting the value of a custom field using the API if you pass the string 'NULL' then it will set the field value to be the String NULL not the database NULL. If you want the value to be truely NULL then you need to either not pass in the value or use 'null' instead
......
......@@ -52,7 +52,7 @@ As we proceed through this chapter, we will consider different API features. Whe
| APIv4 | **Yes** | No | No |
| Authentication: AuthX | **Yes** (v5.36+) | No | No |
| Authentication: Session-Cookie | **Yes** | No | No |
| Authentication: Traditional Keys | Work-in-progress | **Yes** | **Yes** |
| Authentication: Traditional Keys | **Yes** (v5.47+) | **Yes** | **Yes** |
| CMS: Backdrop | **Yes** | **Yes** | No |
| CMS: Drupal 7 | **Yes** | **Yes** | No |
| CMS: Drupal 8+ | **Yes** | Unofficial, Deprecated | No |
......@@ -77,24 +77,22 @@ You may choose among a handful of end-point URLs. Expand the sections below for
https://example.org/civicrm/ajax/rest
```
In other deployments, you may need a verbose URL - such as this WordPress URL:
In other deployments, you may need a verbose URL - such as these WordPress and Joomla URLs:
```
https://example.org/wp-admin/admin.php?page=CiviCRM&q=civicrm/ajax/rest
https://wordpress.example.org/wp-admin/admin.php?page=CiviCRM&q=civicrm/ajax/rest
https://joomla.example.org/administrator/index.php?option=com_civicrm&task=civicrm/ajax/rest
```
The `civicrm/ajax/rest` end-point is frequently used for browser-based API calls ("AJAX"). In newer versions (CiviCRM v5.36+), it can also be used for remote applications.
Stylisitcally, this is similar to [APIv4 REST](../v4/rest.md) end-point.
!!! warning "HTTP Header "`X-Requested-With: XMLHttpRequest`""
When using this end-point, one must pass an extra HTTP header (`X-Requested-With: XMLHttpRequest`).
<!-- Hopefully, this will be obviated for AuthX-style requests within a few cycles. -->
??? example "APIv3 REST via standalone script: `extern/rest.php`"
!!! warning "As of v5.47+, there should be no reason to use `extern/rest.php` - other end-points should be more compatible and more featureful. See [APIv3 Changelog](changes.md#restendpoint) for migration notes."
The `extern/rest.php` end-point is a standalone PHP script. It generally lives in the CiviCRM source tree, although the location will depend on the specific deployment.
```
......@@ -117,7 +115,7 @@ You may choose among a handful of end-point URLs. Expand the sections below for
??? example "APIv3 REST via WordPress service"
[WP REST](wp-rest.md) (CiviCRM v5.25+) replaces all `extern/*.php` scripts with WordPress-optimized equivalents. These end-points support
clean URLs, and they provide wider compatibility with more WordPress *environments*.
clean URLs, and they provide wider compatibility with more WordPress environments.
```
## Typical WP REST URL
......@@ -135,20 +133,20 @@ Every request for APIv3 should include authentication details. These may be subm
The core extension "AuthX" (v5.36+) provides extended authentication support. If configured, [it can accept credentials in several different formats](../../framework/authx.md), such as:
```
## Conventional HTTP header
/* Conventional HTTP header */
Authorization: Bearer MY_API_KEY
Authorization: Bearer MY_JSON_WEB_TOKEN
Authorization: Basic B64(USER:PASS)
Authorization: Bearer THE_USER_API_KEY
Authorization: Bearer THE_JSON_WEB_TOKEN
## Civi HTTP header
/* Civi HTTP header */
X-Civi-Auth: Bearer MY_API_KEY
X-Civi-Auth: Bearer MY_JSON_WEB_TOKEN
X-Civi-Auth: Basic B64(USER:PASS)
X-Civi-Auth: Bearer THE_USER_API_KEY
X-Civi-Auth: Bearer THE_JSON_WEB_TOKEN
## HTTP parameter
/* HTTP parameter */
?_authx=Bearer+MY_API_KEY
?_authx=Bearer+MY_JSON_WEB_TOKEN
?_authx=Basic+B64(USER:PASS)
?_authx=Bearer+THE_USER_API_KEY
?_authx=Bearer+THE_JSON_WEB_TOKEN
```
AuthX is compatible with normal CiviCRM routes like `civicrm/ajax/rest`.
......@@ -167,13 +165,35 @@ Every request for APIv3 should include authentication details. These may be subm
```
?key=THE_SITE_KEY
&api_key=THE_USER_API_KEY
&api_key=MY_API_KEY
```
The [site key](https://docs.civicrm.org/sysadmin/en/latest/setup/secret-keys/#civicrm_site_key) is a general pre-requisite, and it will be the
same for all traditional REST users. The [API key](https://docs.civicrm.org/sysadmin/en/latest/setup/api-keys/) uniquely identifies the
user or agent making the request.
Support for traditional REST keys is limited to specific end-points and versions:
| End-Point URL | Support Traditional REST Keys? |
| -- | -- |
| `civicrm/ajax/rest` | Yes (v5.47+) |
| `extern/rest.php` | Yes (All versions) |
| WP REST | Yes (v5.25+) |
## X-Requested-With
To ensure broad compatibility, APIv3 REST clients should set this HTTP header:
```http
X-Requested-With: XMLHttpRequest
```
The header is not required for all requests, but sometimes it is required, and there is generally no harm to
setting it.
For more complete details, see [Cross-Site Request Forgery (CSRF)](../../security/csrf.md) and specifically
[CSRF: APIv3/APIv4 REST](../../security/csrf.md#rest).
## API Data
As you may recall, APIv3 is built around the triplet [(`$entity`, `$action`, `$params`)](usage.md), e.g.
......@@ -194,22 +214,6 @@ above example and apply appropriate escaping rules, the corresponding HTTP param
!!! tip "Even if there are no `$params`, one should pass a placeholder value for `&json=1`."
The response will be a JSON document, e.g.
```javascript
{
"is_error": 0,
"version": 3,
"count": 1,
"values": [
{
"contact_id": "10",
"contact_type": "Individual",
"contact_sub_type": "",
"display_name": "Example Person",
...
```
??? warning "Deprecated: Extra Key-Value Parameters"
Any unrecognized parameters in the URL are passed through to the `$params`. The previous example could be written as:
......@@ -228,6 +232,24 @@ The response will be a JSON document, e.g.
JSON addresses all these issues, and it has very broad support across many tools/environments/clients.
## Response
The response will be a JSON document, e.g.
```javascript
{
"is_error": 0,
"version": 3,
"count": 1,
"values": [
{
"contact_id": "10",
"contact_type": "Individual",
"contact_sub_type": "",
"display_name": "Example Person",
...
```
??? warning "Deprecated: XML Responses"
If you omit the `&json=...` parameter, then the APIv3 REST will send responses in XML format. This is retained for
......
# APIv4 REST
TODO
## Overview
<!--
FIXME: When the explorer is updated, enable the comments about it...
We can copy some excerpts form APIv3 wrt end-point URL and authentication.
!!! tip "Get started with APIv4 Explorer"
The API data needs to be formatted differently.
CiviCRM has a built-in tool for working with APIv4 (`Support => Developer => APIv4 Explorer`). You may use
this to generate complete HTTP/REST examples that are tuned to your instance of CiviCRM.
Overall, APIv4 REST should be simpler to present than APIv3 because we
don't have as many variations of entr-point/authn.
This documentation provides a deeper explanation of the HTTP/REST interface.
-->
-->
\ No newline at end of file
CiviCRM APIv4 provides HTTP/REST bindings, enabling remote applications to read and write data.
As you may recall, the APIv4 architecture uses the triplet [(`$entity`, `$action`, `$params`)](usage.md), e.g.
```php
$result = civicrm_api4($entity, $action, $params);
$result = civicrm_api4('Contact', 'get', ['limit' => 10]);
```
A typical APIv4 REST request includes these three elements and an authentication credential. For example:
```
https://{$website}/civicrm/ajax/api4/{$entity}/{$action}
?params={$params}&_authx={$credential}
```
In the remaining sections, we will examine this formula more closesly - considering more concrete examples
and more precise descriptions of each part.
??? question "Is APIv4 a REST-ful interface?"
HTTP-based APIs are sometimes described as "REST-ful" if they closely follow certain HTTP conventions. CiviCRM APIv4 is more REST-ful than APIv3, but it is not strictly REST-ful.
Never-the-less, it is generally used for reading and writing resource records, and (by tradition) the subsystem is referred to as "REST".
## Examples
As we review the structure of an APIv4 REST call, it may help to have a few examples. Here is a representative example:
```
1: GET https://www.example.org/civicrm/ajax/api4/Contact/get
2: ?params=%7B%22where%22%3A%5B%5B%22id%22%2C%22%3D%22%2C22%5D%5D%7D
3: &_authx=ABCD1234
```
In line 1, we see an *end-point (base) URL* (`https://www.example.org/civicrm/ajax/api4`) along with entity and action (`Contact/get`). Line 2 conveys the specific API data-parameters (encoded as JSON form-data). Line 3 provides the *authentication* parameters.
Similar elements should appear in any API call, but the content of each can differ. Here is another example:
```
1: POST https://crm.example.org/civicrm/ajax/api4/OptionGroup/get
2: Authorization: Basic dXNlcjpwYXNz
3: X-Requested-With: XMLHttpRequest
4:
5: %7B%22where%22%3A%5B%5B%22name%22%2C%22%3D%22%2C%22activity_type%22%5D%5D%2C%22limit%22%3A25%2C%22debug%22%3Atrue%7D
```
Here, we have a different *end-point (base) URL* (line 1; `https://crm.example.org/civicrm/ajax/api4`) with a different entity and action (`OptionGroup/get`). Line 2 shows a different *authentication* style. The specific API data also differs (line 5).
## End-Point URL
All requests should be submitted to the CiviCRM route `civicrm/ajax/api4/{ENTITY}/{ACTION}`.
Requests are typically submitted with HTTP POST, but read-only operations may use HTTP GET.
??? info "Comparison: URLs on Backdrop, Drupal, Joomla, WordPress"
On Drupal, Backdrop, and some WordPress deployments, you may access it with a clean URL.
```
https://example.org/civicrm/ajax/rest
```
In other deployments, you may need a verbose URL - such as these WordPress and Joomla URLs:
```
https://wordpress.example.org/wp-admin/admin.php?page=CiviCRM&q=civicrm/ajax/rest
https://joomla.example.org/administrator/index.php?option=com_civicrm&task=civicrm/ajax/rest
```
The `civicrm/ajax/rest` end-point is frequently used for browser-based API calls ("AJAX"). In newer versions (CiviCRM v5.36+), it can also be used for remote applications.
Stylisitcally, this is similar to [APIv4 REST](../v4/rest.md) end-point.
## Authentication
Every request for APIv4 should include authentication details. These may be submitted in a few forms, such as:
```
/* HTTP parameter */
?_authx=Bearer+MY_API_KEY
?_authx=Bearer+MY_JSON_WEB_TOKEN
?_authx=Basic+B64(USER:PASS)
/* Civi HTTP header */
X-Civi-Auth: Bearer MY_API_KEY
X-Civi-Auth: Bearer MY_JSON_WEB_TOKEN
X-Civi-Auth: Basic B64(USER:PASS)
/* Conventional HTTP header */
Authorization: Bearer MY_API_KEY
Authorization: Bearer MY_JSON_WEB_TOKEN
Authorization: Basic B64(USER:PASS)
```
!!! tip "Which authentication forms can I use?"
By default, new AuthX deployments are configured to accept:
* __[Credentials](../../framework/authx.md#credentials)__: API keys or JSON Web Tokens (*Passwords are not accepted by default.*)
* __[Flows](../../framework/authx.md#flows)__: `?_authx=`, `X-Civi-Auth:`, or `Authorization:`
The system administrator may enable or disable support for each credential and flow.
Some hosting environments may have compatibility issues with `Authorization:`. If you need to communicate with
several different CiviCRM deployments, then `?_authx=` and `X-Civi-Auth:` may be more reliable.
For API keys and passwords, the user must have the matching permission (`authenticate with api key` or
`authenticate with password`).
## X-Requested-With
To ensure broad compatibility, APIv4 REST clients should set this HTTP header:
```http
X-Requested-With: XMLHttpRequest
```
The header is not required for all requests, but sometimes it is required, and there is generally no harm to
setting it.
For more complete details, see [Cross-Site Request Forgery (CSRF)](../../security/csrf.md) and specifically
[CSRF: APIv4/APIv4 REST](../../security/csrf.md#rest).
## Parameters
APIv4 `$params` are transmitted with JSON and URL encoding. Thus, if the PHP `$params` are:
```php
['limit' => 10]
```
Then the equivalent JSON will be:
```javascript
{"limit":10}
```
and the URL-encoded JSON will be:
```
%7B%22limit%22%3A10%7D
```
## Response
The response will be a JSON document. A well-formed response provides a list of `values`:
```javascript
{
"version": 4,
"count": 10,
"values": [
{
"contact_id": "1",
"contact_type": "Individual",
"contact_sub_type": "",
"display_name": "Example Person",
...
```
Alternatively, if there is an error, then it will be reported with `error_message` and `error_code`:
```javascript
{
"error_code": 0,
"error_message": "Api FakeEntity fakeAction version 4 does not exist.",
}
```
## History
* __CiviCRM v5.19__: Added HTTP bindings for APIv4 to `civicrm-core`. However, these were only available for
browser-based AJAX applications -- not for remote REST applications.
* __CiviCRM v5.36__: Added the [AuthX extension](../../framework/authx.md) to `civicrm-core`. This enabled REST-style
authentication for APIv4. However, configuring AuthX may require CLI interaction to update hidden settings.
* __CiviCRM v5.47__: Added the AuthX configuration screen, making it easier for more sysadmins to setup.
......@@ -94,6 +94,7 @@ For each general flow, we have a few variations.
| Stateless | Common Header<br/> (`header`) | The credential is submitted with an HTTP header (`Authorization: <credential>`). |
| Stateless | X-Header<br/> (`xheader`) | The credential is submitted with a custom HTTP header (`X-Civi-Auth: <credential>`). This header is otherwise identical to `Authorization:`. |
| Stateless | Parameter<br/> (`param`) | The credential is submitted with an HTTP GET or POST parameter (`?_authx=<credential>`) |
| Stateless | Legacy REST<br/> (`legacyrest`) | The credential is submitted with a pair of HTTP parameters (`?key=MY_SITE_KEY&api_key=MY_API_KEY`). This is only valid for the route `civicrm/ajax/rest`. |
| Stateful | Login session (`login`) | The client explicitly logs in (`POST /civicrm/authx/login`) which creates a session and cookie. |
| Stateful | Auto session (`auto`) | The clients submits a GET request for any page (`?_authx=<credential>&_authxSes=1`). The session is initialized. The user redirects to original page. |
......@@ -184,19 +185,21 @@ A *guard* limits access to AuthX authentication services. By default, a user wh
For each authentication flow, one may toggle support for different credentials and user-links. Here is the default configuration:
| Setting (Default) | Description |
| -- | -- |
| `authx_guards` <br/>(`['site_key', 'perm']`) | List of guards that permit authentication. If blank, then no guards are required. |
| `authx_param_cred` <br/>(`['jwt']`) | List of credential-types that will be accepted via `?_authx` parameters |
| `authx_param_user` <br/>(`optional`) | Is it important to load a CMS user? (`require`, `optional`, `ignore`) |
| `authx_header_cred` <br/>(`['jwt']`) | List of credential-types that will be accepted via `Authorization:` header |
| `authx_header_user` <br/>(`optional`) | Is it important to load a CMS user? (`require`, `optional`, `ignore`) |
| `authx_xheader_cred` <br/>(`['jwt']`) | List of credential-types that will be accepted via `X-Civi-Auth:` header |
| `authx_xheader_user` <br/>(`optional`) | Is it important to load a CMS user? (`require`, `optional`, `ignore`) |
| `authx_login_cred` <br/>(`['jwt']`) | List of credential-types that will be accepted on `POST /civicrm/authx/login` |
| `authx_login_user` <br/>(`require`) | Is it important to load a CMS user? (`require`, `optional`, `ignore`) |
| `authx_auto_cred` <br/>(`['jwt']`) | List of credential-types that will be accepted via `?_authx=...&_authxSes=1` parameters |
| `authx_auto_user` <br/>(`require`) | Is it important to load a CMS user? (`require`, `optional`, `ignore`) |
| Setting (Default) | Description | Version |
| -- | -- | -- |
| `authx_guards` <br/>(`['site_key', 'perm']`) | List of guards that permit authentication. If blank, then no guards are required. | 5.36 |
| `authx_param_cred` <br/>(`['jwt']`) | List of credential-types that will be accepted via `?_authx` parameters | 5.36 |
| `authx_param_user` <br/>(`optional`) | Is it important to load a CMS user? (`require`, `optional`, `ignore`) | 5.36 |
| `authx_header_cred` <br/>(`['jwt']`) | List of credential-types that will be accepted via `Authorization:` header | 5.36 |
| `authx_header_user` <br/>(`optional`) | Is it important to load a CMS user? (`require`, `optional`, `ignore`) | 5.36 |
| `authx_xheader_cred` <br/>(`['jwt']`) | List of credential-types that will be accepted via `X-Civi-Auth:` header | 5.36 |
| `authx_xheader_user` <br/>(`optional`) | Is it important to load a CMS user? (`require`, `optional`, `ignore`) | 5.36 |
| `authx_legacyrest_cred` <br/>(`['jwt', 'api_key']`) | List of credential-types that will be accepted via `?key=&api_key=` parameters | 5.47 |
| `authx_legacyrest_user` <br/>(`require`) | Is it important to load a CMS user? (`require`, `optional`, `ignore`) | 5.47 |
| `authx_login_cred` <br/>(`['jwt']`) | List of credential-types that will be accepted on `POST /civicrm/authx/login` | 5.36 |
| `authx_login_user` <br/>(`require`) | Is it important to load a CMS user? (`require`, `optional`, `ignore`) | 5.36 |
| `authx_auto_cred` <br/>(`['jwt']`) | List of credential-types that will be accepted via `?_authx=...&_authxSes=1` parameters | 5.36 |
| `authx_auto_user` <br/>(`require`) | Is it important to load a CMS user? (`require`, `optional`, `ignore`) | 5.36 |
??? example "Example: Relax all settings to permit testing"
......
# Cross Site Request Forgery (CSRF)
## General
### What is CSRF?
_Cross-site request forgery (CSRF)_ is a type of browser-based attack involving a user who visits two sites:
* A web-based user logs into one site (eg `https://crm.example.org`). They have an *active browser session/cookie* on this site.
* Later, the same user casually browses to another site (eg `https://evil.example.com`).
* The `evil.example.com` site emits some kind of hyperlink (`<A HREF>`, `<FORM ACTION>`, `<IFRAME>`, `<IMG>`, `<SCRIPT>`, etc) which
tells the browser to send a request to `crm.example.org`, eg
```html
<img src="https://crm.example.org/civicrm/ajax/rest?entity=Contact&action=delete&..."/>
```
* The browser requests this hyperlink. Because the user has an active session and suitable permission, `crm.example.org`
performs the requested action (eg deleting random contacts).
* Of course, the user didn't actually want to delete anything. This was a surreptitious request sent on their behalf.
### How does CiviCRM protect against CSRF?
There are different techniques - some techniques are used for [REST services (eg APIv3/APIv4)](#rest); other techniques are used for
[web forms (such as HTML_QuickForm)](#quickform).
### When should CSRF protections be required?
Requests which actively manipulate data must have CSRF protection. For example, `civicrm/ajax/rest` must have CSRF protection.
Requests which passively display linkable screens must _not_ have CSRF protection. For example, `civicrm/event/info` must not have CSRF protection.
Many workflows involve a hybrid. For example, it is useful to have external hyperlinks to `civicrm/event/register`. The first page-load
presents a form to a user; this page does not require CSRF protection. As the user interacts with the form (eg using rich widgets that require
APIs; eg submitting the form to finish regstration), the page sends additional requests - and these do require CSRF protection.
## APIv3/APIv4 REST {:#rest}
All requests for [APIv3 REST](../api/v3/rest.md) and [APIv4 REST](../api/v4/rest.md) are assessed for CSRF risk - they must have some attribute to indicate that CSRF is not a concern. Either:
* Send the header `X-Requested-With: XMLHttpRequest`.
* Authenticate with a mechanism that is not susceptible to CSRF.
The rationales for each are examined below.
### When does APIv3/APIv4 require `X-Requested-With`?
It varies by version, authentication mechanism, and/or end-point. For APIv3/APIv4:
* After v5.47+, CSRF protection depends on the authentication mechanism:
* `X-Requested-With:` is required if you authenticate with standard HTTP headers (`Cookie:` or `Authorization:`).
* `X-Requested-With:` is not required for bespoke authentication mechanisms (`X-Civi-Auth:`, `?_authx=`, `?api_key=`).
* Before v5.47, CSRF protection depends on the end-point URL:
* `X-Requested-With:` is required by `civicrm/ajax/*` -- regardless of whether you use standard or bespoke authentication.
* `X-Requested-With:` is not required by `extern/rest.php`. This is not required because `extern/rest.php` only supports bespoke authentication (`?api_key`).
### How does `X-Requested-With` mitigate CSRF?
When the browser follows a regular HTML hyperlink, it only sends standard headers. `X-Requested-With:` is a custom-header.
`evil.example.com` can generate HTML hyperlinks in many ways (`<A HREF>`, `<FORM ACTION>`, etc), but none of them can specify the
custom-header `X-Requested-With:`.
Browsers will send custom-headers under some other circumstances; notably, Javascript logic can send a custom-header. However, this is
subject to the standard *Same Origin Policy*. Javascript on `https://crm.example.org` can send custom-headers to `crm.example.org`; but
Javascript on `https://evil.example.com` cannot.
### What authentication mechanisms are susceptible to CSRF?
CSRF attacks exploit standard HTTP headers such as `Cookie:` and/or `Authorization:`. For example, when a user logs into
`crm.example.org`, the browser makes a note to continue sending `Cookie:` and/or `Authorization:` with every subsequent request (regardless
of how the request is initiated). This automatic behavior creates the opportunity for CSRF.
For contrast, consider the legacy end-point `extern/rest.php`. Instead of standard headers, it uses the bespoke parameter `?api_key=`.
The browser does not send `?api_key=` automatically -- so it's not a vector for CSRF.
Hypothetically, `evil.example.com` could construct a hyperlink to `extern/rest.php?api_key=MY_API_KEY`. *But they need to specify
`MY_API_KEY`.* If they don't specify `MY_API_KEY`, then the request is anonymous - and conveys no extra privileges. If they do specify
`MY_API_KEY`, then you have a prior security compromise. CSRF countermeasures like `X-Requested-With:` won't protect you from an attacker
who already has an API key.
In short, CSRF is an important consideration for standard browser-based authentication flows (`Cookie:`/`Authorization:`) but not for
bespoke authentication flows (`?api_key=`, `X-Civi-Auth:`).
## QuickForm {:#quickform}
!!! todo "TODO: Discuss qfid mechanism"
......@@ -104,10 +104,10 @@ nav:
- Option Lists: api/v4/pseudoconstants.md
- Chaining: api/v4/chaining.md
- Custom Data: api/v4/custom-data.md
# - APIv4 REST: api/v4/rest.md
- Differences Between Api v3 and v4: api/v4/differences-with-v3.md
- APIv4 Architecture: api/v4/architecture.md
- APIv4 Changelog: api/v4/changes.md
- APIv4 REST: api/v4/rest.md
- APIv3:
- APIv3 Usage: api/v3/usage.md
- APIv3 Interfaces: api/v3/interfaces.md
......@@ -302,6 +302,7 @@ nav:
- Permissions: security/permissions.md
- Access Control: security/access.md
- Reporting Vulnerabilities: security/reporting.md
- Request Forgery: security/csrf.md
- Framework:
- AJAX Pages and Forms Reference: framework/ajax.md
- AngularJS:
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment