Open Exchange App Anti CSRF Methods

IRIS provides us with anti login CSRF attack mitigation, however this is not the same as a CSRF attack, as login attacks only occur on the login form. There are currently no built-in tools to mitigate CSRF attacks on api calls and other forms, so this is a step in mitigating these attacks.

See the following link from OWASP for the definition of a CSRF attack:

https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)

The method shown in this article for mitigating these attacks is currently not proactive, but a minimum that needs to be combined with other attack vector prevention methods, like disabling CORS, mitigating http pollution, etc. By proactive, I mean that it is possible in the future for attackers to coerce a browser into creating arbitrary custom headers and values. Because this is not currently possible, the mere presence of a custom header is enough to mitigate this risk. See the following for further explanation:

https://security.stackexchange.com/questions/23371/csrf-protection-with-custom-headers-and-without-validating-token

Example

A rest call made to a CSP dispatch class extends the %CSP.REST class which gives us access to handling the headers before any intrusion. In the client code, we add the custom header to our request, along with junk data that can be populated in the future with specific information as a proactive approach:

$.ajax({
  beforeSend: function(request) {
  // Where we set the custom header. The value doesn't matter. In the future we want to set this and verify the values matches the servers'
    request.setRequestHeader("Grandmas-Cookies", Math.random());
  },
  url:serverURL+url+data,
  type: "GET",
  async: true,
  contentType: "application/json",
  dataType: "text",
  error: function(response) { 
    console.log(response);
  },
  success: successCallback
  });

Then on the server's Page function of the %CSP.REST extended class, we check for the presence of this header.

set header = %request.GetCgiEnv("HTTP_GRANDMAS_COOKIES")
// If blank, toss it out. Client browsers cannot be coerced into setting values of custom headers
if header = "" {
  Set tSC=..Http403()
	Quit
}

If the header isn't there or it has no value, the request is dropped. We've tested this with several penetration testing tools including OWASP ZAP and Burp-Suite, and it works well. I'm curious if any of you out there have worked on anti CSRF methods for your Intersystems based applications, and how you helped mitigate them. A lot of security prevention is not "out of the box" with Caché/IRIS, so many vulnerabilities require custom solutions.

Thank you!

Comments

Doesn't CORS exist to prevent exactly that?

Check that origin/referer headers in your OnHandleCorsRequest are what you expect them to be and all would be good.

@Eduard Lebedyuk no, this is a misconception. In a nutshell CORS is the opposite, by default every green browser will apply the Same Origin Policy to allow or negate the access. However CORS has been made for making that policy lax (since it allow you to access a resource outside the origin's domain). 

Needless to say, this wouldn't prevent the attacker from GET'ing a resource because GET methods don't execute a pre-flight request, and that's just the tip of the iceberg, you can read more about it here:

About the misconception https://www.nccgroup.trust/uk/about-us/newsroom-and-events/blogs/2017/september/common-csrf-prevention-misconceptions/

About the correct implementation https://www.acunetix.com/websitesecurity/csrf-attacks/

Btw, this implementation doesn't validate the token, neither it generates something randomically strong (using CPRNG). Checking if the header is present  is not enough. OWASP advises you to generate tokens that you can validate. It also suggests that the implementation should use stateful URLs, like the ones containing the token themselves (which is similar to the CSPToken that each private CSP page has).

Checking for the existence of a custom header, as I mentioned, is a good baseline because a browser cannot be coerced into setting one in these requests. But you're right that this isn't the OWASP recommendation, because it isn't proactive like the token method and is restricted to rest calls. The example I have is definitely not comprehensive, but due to the lack of support for these vulnerabilities I was wanting to discuss this.

Not technically. CORS exists to prevent certain responses from being read by another origin, and while a CSRF attack comes from another origin, there is a percentage of cases where the origin/referer is not included or is complex to identify correctly (the server is being indirectly accessed via proxy or some type of F5 config). See "Identifying the Target Origin" here which mentions how token based is preferred due to the work required in the long run, and how using origin/referer isn't 100% reliable:

https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html

A good CORS policy helps considerably though and I'd say is necessary. Just like how https doesn't directly stop CSRF, but it certainly assists in other areas that help mitigate these vulnerabilities.

Just as a side comment, this has actually nothing to do with cookies. Cookies don't protect against CSRF. This is a custom HTTP header with a word "Cookie" in its name. So while it's correct it's a bit confusing.

I was not sure if checking for just presence of the header and not an actual value is enough (we assign a random value at logon time and store it in session), but apparently all browsers prevent cross-site AJAX requests so 3rd party site can't send custom header.