Pythian Blog: Technical Track

Cloud Armor pt. 3 – The Right Way

Cloud Armor Coding

In the last post, I discussed some of Cloud Armor’s advanced features and how to work around various customized rules. However, this begs the question of how to code for a web-application firewall (WAF) to minimize those rules and customizations. There will always be business requirements that trigger the WAF rules, and while we can tune the engine to reduce most false positives, there are circumstances where bypassing rules less securely isn’t ideal.

Base64 to the rescue

The most basic way to deal with this issue is to encode using Base64. It is heavily supported by all programming languages and clients and will output a clean string containing only “A–Z, a–z, 0–9 and +/=.” These usually pass WAF rules and will undoubtedly make tuning Cloud Armor easier. However, there are several other ways to do this, which you may need to accommodate depending on your scenario.  

Legacy Applications – Base64 only what you need to

When the compatibility of your legacy applications is essential and the desire for a significant overhaul is low, it is best to follow a selective approach. Pick and choose specifically where to Base64 things. 

For instance, you may need a feature allowing rich text messaging between parties. This assumes all the appropriate precautions have been taken to ensure the permitted HTML will be secure. As previously discussed, you could have a special rule for this situation. However, another option is to encode it with Base64.

Traditionally, you may have sent this as JSON to your backend in a string looking something like this: 

{ 
   "postingid":"1234", 
   "html": "<p>\nIn <a href=\"/wiki/Computer_programming\" title=\"Computer programming\">computer programming</a>, <b>Base64</b> is a group of <a href=\"/wiki/Binary-to-text_encoding\" title=\"Binary-to-text encoding\">binary-to-text encoding</a> schemes that represent <a href=\"/wiki/Binary_data\" title=\"Binary data\">binary data</a> (more specifically, a sequence of 8-bit <a href=\"/wiki/Byte\" title=\"Byte\">bytes</a>) in sequences of 24 <a href=\"/wiki/Bit\" title=\"Bit\">bits</a> that can be represented by four 6-bit Base64 digits.\n</p>"

}

However, these special characters will trigger an alert in Cloud Armor. If you Base64 that single property, you will receive something like this:

{ 
  "postingid":"1234", 
  "html": "PHA+CkluIDxhIGhyZWY9Ii93aWtpL0NvbXB1dGVyX3Byb2dyYW1taW5nIiB0aXRsZT0iQ29tcHV0ZXIgcHJvZ3JhbW1pbmciPmNvbXB1dGVyIHByb2dyYW1taW5nPC9hPiwgPGI+QmFzZTY0PC9iPiBpcyBhIGdyb3VwIG9mIDxhIGhyZWY9Ii93aWtpL0JpbmFyeS10by10ZXh0X2VuY29kaW5nIiB0aXRsZT0iQmluYXJ5LXRvLXRleHQgZW5jb2RpbmciPmJpbmFyeS10by10ZXh0IGVuY29kaW5nPC9hPiBzY2hlbWVzIHRoYXQgcmVwcmVzZW50IDxhIGhyZWY9Ii93aWtpL0JpbmFyeV9kYXRhIiB0aXRsZT0iQmluYXJ5IGRhdGEiPmJpbmFyeSBkYXRhPC9hPiAobW9yZSBzcGVjaWZpY2FsbHksIGEgc2VxdWVuY2Ugb2YgOC1iaXQgPGEgaHJlZj0iL3dpa2kvQnl0ZSIgdGl0bGU9IkJ5dGUiPmJ5dGVzPC9hPikgaW4gc2VxdWVuY2VzIG9mIDI0IDxhIGhyZWY9Ii93aWtpL0JpdCIgdGl0bGU9IkJpdCI+Yml0czwvYT4gdGhhdCBjYW4gYmUgcmVwcmVzZW50ZWQgYnkgZm91ciA2LWJpdCBCYXNlNjQgZGlnaXRzLgo8L3A+"
}

It is unlikely that this will trigger Cloud Armor rules beyond a bit of tuning. Be aware that some fields may require some special handling, however. 

Base64 It all

Next up is Base64 everything: the most extreme example. If we use the same sample above, after producing the JSON file, you can just Base64 everything like this:

eyAKICAicG9zdGluZ2lkIjoiMTIzNCIsIAogICAiaHRtbCI6ICI8cD5cbkluIDxhIGhyZWY9XCIvd2lraS9Db21wdXRlcl9wcm9ncmFtbWluZ1wiIHRpdGxlPVwiQ29tcHV0ZXIgcHJvZ3JhbW1pbmdcIj5jb21wdXRlciBwcm9ncmFtbWluZzwvYT4sIDxiPkJhc2U2NDwvYj4gaXMgYSBncm91cCBvZiA8YSBocmVmPVwiL3dpa2kvQmluYXJ5LXRvLXRleHRfZW5jb2RpbmdcIiB0aXRsZT1cIkJpbmFyeS10by10ZXh0IGVuY29kaW5nXCI+YmluYXJ5LXRvLXRleHQgZW5jb2Rpbmc8L2E+IHNjaGVtZXMgdGhhdCByZXByZXNlbnQgPGEgaHJlZj1cIi93aWtpL0JpbmFyeV9kYXRhXCIgdGl0bGU9XCJCaW5hcnkgZGF0YVwiPmJpbmFyeSBkYXRhPC9hPiAobW9yZSBzcGVjaWZpY2FsbHksIGEgc2VxdWVuY2Ugb2YgOC1iaXQgPGEgaHJlZj1cIi93aWtpL0J5dGVcIiB0aXRsZT1cIkJ5dGVcIj5ieXRlczwvYT4pIGluIHNlcXVlbmNlcyBvZiAyNCA8YSBocmVmPVwiL3dpa2kvQml0XCIgdGl0bGU9XCJCaXRcIj5iaXRzPC9hPiB0aGF0IGNhbiBiZSByZXByZXNlbnRlZCBieSBmb3VyIDYtYml0IEJhc2U2NCBkaWdpdHMuXG48L3A+Igp9

This will always pass through Cloud Armor effectively, and a basic Cloud Armor rule would easily handle it. However, there are challenges on the application side. Some applications may be unusable or may be more difficult to use. Also, if the content needs to be inspected, it will require constant decoding. This won’t necessarily be a huge burden, but there are times that this may become problematic.

Lastly, Base64 tends to obscure things such that on the web, components like proxies and other interpreters can’t deal with it natively. Effectively, you now have an obtuse content type of “application/x-www-form-urlencoded,” which is effectively text.

JSON Wrappers

Lastly is producing a JSON wrapper. This is likely the best-case scenario if you are building something new and have control over all aspects of your application. With the best of both worlds, you can define a suitable wrapper with Cloud Armor while allowing the data you need to pass through. The key here is that the properties and values not in Base64 are known entities that will not trigger Cloud Armor. An excellent example is the Google Pub/Sub Message format to deviate from the previous examples.

{
  "message": {
    "attributes": {
      "one": "two"
    },
    "data": "eyAKICAicG9zdGluZ2lkIjoiMTIzNCIsIAogICAiaHRtbCI6ICI8cD5cbkluIDxhIGhyZWY9XCIvd2lraS9Db21wdXRlcl9wcm9ncmFtbWluZ1wiIHRpdGxlPVwiQ29tcHV0ZXIgcHJvZ3JhbW1pbmdcIj5jb21wdXRlciBwcm9ncmFtbWluZzwvYT4sIDxiPkJhc2U2NDwvYj4gaXMgYSBncm91cCBvZiA8YSBocmVmPVwiL3dpa2kvQmluYXJ5LXRvLXRleHRfZW5jb2RpbmdcIiB0aXRsZT1cIkJpbmFyeS10by10ZXh0IGVuY29kaW5nXCI+YmluYXJ5LXRvLXRleHQgZW5jb2Rpbmc8L2E+IHNjaGVtZXMgdGhhdCByZXByZXNlbnQgPGEgaHJlZj1cIi93aWtpL0JpbmFyeV9kYXRhXCIgdGl0bGU9XCJCaW5hcnkgZGF0YVwiPmJpbmFyeSBkYXRhPC9hPiAobW9yZSBzcGVjaWZpY2FsbHksIGEgc2VxdWVuY2Ugb2YgOC1iaXQgPGEgaHJlZj1cIi93aWtpL0J5dGVcIiB0aXRsZT1cIkJ5dGVcIj5ieXRlczwvYT4pIGluIHNlcXVlbmNlcyBvZiAyNCA8YSBocmVmPVwiL3dpa2kvQml0XCIgdGl0bGU9XCJCaXRcIj5iaXRzPC9hPiB0aGF0IGNhbiBiZSByZXByZXNlbnRlZCBieSBmb3VyIDYtYml0IEJhc2U2NCBkaWdpdHMuXG48L3A+Igp9",
    "messageId": "5481363137518973",
    "publishTime": "2022-08-24T16:39:11.08Z",
  },
  "subscription": "projects/my-project/subscriptions/my-subscription-name"
}

Obviously, Google could have just allowed a JSON-encoded string in the data tag. This could have created many issues I’ve recently mentioned, as this content could be “anything.” However, this hybrid approach allows Google to not continually decode Base64. This also allows access to the necessary properties to route the package without worrying about other content. 

Conclusion

Each Base64 option has pros and cons as to when to implement them. These options will allow you to handle most issues that are Cloud Armor related to whatever application you are supporting or building. However, this now puts much more onus on your code handling situations correctly (SQL Injection, for instance).

Thanks for reading.

 

Supporting Links:

 

No Comments Yet

Let us know what you think

Subscribe by email