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 tocrm.example.org
, eg<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); other techniques are used for web forms (such as HTML_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¶
All requests for APIv3 REST and APIv4 REST 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:
orAuthorization:
).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 bycivicrm/ajax/*
-- regardless of whether you use standard or bespoke authentication.X-Requested-With:
is not required byextern/rest.php
. This is not required becauseextern/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¶
TODO: Discuss qfid mechanism