Merge pull request #23565 from eileenmcnaughton/preffered_mail
[civicrm-core.git] / CRM / Utils / REST.php
index b82866867d970edc5dab201181614e8b94e339fa..c34b9e1be3ac51c1ece992a2b325134eb2d8be1c 100644 (file)
@@ -630,7 +630,8 @@ class CRM_Utils_REST {
   /**
    * 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.
@@ -640,17 +641,32 @@ class CRM_Utils_REST {
    *   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;
     }