/**
* Does this request appear to be a web-service request?
*
- * This is used to mitigate CSRF risks.
+ * It is important to distinguish regular browser-page-loads from web-service-requests. Regular
+ * page-loads can be CSRF vectors, and we don't web-services to run via CSRF.
*
* @return bool
* TRUE if the current request appears to either XMLHttpRequest or non-browser-based.
* FALSE if the current request looks like a standard browser request. This request may be generated by
* <A HREF>, <IFRAME>, <IMG>, `Location:`, or similar CSRF vector.
*/
- protected static function isWebServiceRequest(): bool {
+ public static function isWebServiceRequest(): bool {
if (($_SERVER['HTTP_X_REQUESTED_WITH'] ?? NULL) === 'XMLHttpRequest') {
return TRUE;
}
+ // If authx is enabled, and if the user gives a credential, it will store metadata.
$authx = \CRM_Core_Session::singleton()->get('authx');
- $allowFlows = ['legacyrest', 'param', 'xheader'];
- // <legacyrest> Current request has valid `?api_key=SECRET&key=SECRET` ==> Strong-secret params
- // <param> Current request has valid `?_authx=SECRET` ==> Strong-secret param
- // <xheader> Current request has valid `X-Civi-Auth:` ==> Custom header AND strong-secret param
- // NOTE: Prohibited flows: `login`, `auto`, and `header` are driven by standard headers (`Cookie:`/`Authorization:`)
+ $allowFlows = [
+ // Some flows are resistant to CSRF. Allow these:
+
+ // <legacyrest> Current request has valid `?api_key=SECRET&key=SECRET` ==> Strong-secret params
+ 'legacyrest',
+
+ // <param> Current request has valid `?_authx=SECRET` ==> Strong-secret param
+ 'param',
+
+ // <xheader> Current request has valid `X-Civi-Auth:` ==> Custom header AND strong-secret param
+ 'xheader',
+
+ // Other flows are not resistant to CSRF on their own (need combo w/`X-Requested-With:`).
+ // Ignore these:
+ // <login> Relies on a session `Cookie:` (which browsers re-send automatically).
+ // <auto> First request might be resistant, but all others use session `Cookie:`.
+ // <header> Browsers often retain list of credentials and re-send automatically.
+ ];
+
if (!empty($authx) && in_array($authx['flow'], $allowFlows)) {
return TRUE;
}