Preventing Cross-Site Request Forgery (CSRF) in Google Apps Script Dialogs and Sidebars

Cross-Site Request Forgery (CSRF) is a web security vulnerability that allows a nefarious entity to take actions on a website that are unintended by the user.

Typically, this is done by tricking the user into using another website resembling the intended site and then submitting a form or clicking a button. The dodgy site then sends a request with its own payload of nasty stuff on the user’s behalf.

Google Workspace and Google Apps Script’s HMTL Service API protect the user with OAuth2 authorization standards and embed dialogues and sidebars in restrictive iframes to sandbox these environments. However, there may be a requirement to further protect your users from unintentionally sending form data using the google.script.run Client-side API that sends data back to Google Apps Script, with a server-side generated anti-CSRF token.

Indeed, when completing a CASA Tier 2 security assessment for a Google Workspace Editor add-on this was a requirement for me to not only pass the assessment but to also meet GDPR requirements.

An anti-CSRF token will allow us to create a unique ID for the current sidebar or dialogue session. We can store this token server-side in the User Properties of the Properties Service and then add the token to a hidden input element in our form client-side on our dialogues and sidebars. We can then send this token along with our form payload back to Apps Script where we can first validate the token before continuing.

The following script and tutorial provide a brief example of how to do this.

The Example Dialogue

We will first open a Google Sheet (but you can open a Google Doc or Slide and do the same thing) and create a bound Google Apps Script.

Our simple tasks will be to:

  1. Create a menu to access a modal dialogue in the Google Sheet.
  2. Create the modal dialogue with a form containing a radio selection and a submit button.
  3. On submission, the form is validated with the anti-CSRF token before

anti-CSRF token example Google Sheets DialogueCreate a Menu Item and Modal Dialogue in Google Sheets

First, let’s get our UIs out of the way.

Lines 5-12 generate the Google Apps Script simple trigger onOpen(). This will create our menu item that will access the dialogue using the Spreadsheet App Class’ getUi() method.

Next, we build the dialogue. Here, we invoke HtmlService to create a template from a file, referencing the ‘Index.html’ file as our source file. We will take this approach to use scriptlets in our HTML to define our CSRF token.

Finally, we will call the UI method again to display the HTML in a modal dialogue.

Hire a Google Workspace Developer for your Business Needs

Create the Front-end ‘Index’ HTML Page


<!DOCTYPE html>
<html>
<head>
<base target="_top">
<link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons1.css">
</head>
<body>
<div id="container">
<h1>Choose Your Goat</h1>
<form id="goatForm" onsubmit="event.preventDefault();">
<input type="hidden" name="_csrf" value="<?= getCsrfToken() ?>" />
<label for="goat_type">Type of Goat:</label><br>
<input type="radio" name="goat_type" id="goat_type_pygmy" value="pygmy">
<label for="goat_type_pygmy">Pygmy Goat</label><br>
<input type="radio" name="goat_type" id="goat_type_nigerian_dwarf" value="nigerian_dwarf">
<label for="goat_type_nigerian_dwarf">Nigerian Dwarf Goat</label><br>
<input type="radio" name="goat_type" id="goat_type_boer" value="boer">
<label for="goat_type_boer">Boer Goat</label><br>
</form>
<input type="button" value="Submit" onclick="submit()">
<div id="resp">Response: <span id="response">…</span></div>
</div>
</body>
<script>
/**
* When the validation process is completed successfully without Apps Scripting errors.
* @param {String.<Object>} e – event parameter containing 'hasError' boolean and 'text' string.
*/
function onSuccess(e){
const message = JSON.parse(e)
let color = message.hasError? "red" : "blue"
const resp = document.getElementById("response")
resp.innerText = message.text
resp.style.color = color
}
/**
* Submits the response back to Google Apps Script.
*/
function submit(){
const form = document.getElementById('goatForm');
// Create a FormData object
const formData = new FormData(form);
let payload = {}
// Iterate over the form data
for (const [key, value] of formData.entries()) {
console.log(key, value);
Object.assign(payload, {[key]: value})
}
console.log(payload)
google.script.run.withSuccessHandler(onSuccess).formInputs(JSON.stringify(payload));
}
</script>
</html>

view raw

Index.html

hosted with ❤ by GitHub

 The HTML form & CSRF token

Lines 10- 19: Here we are adding our form containing our three choices of goats for our survey.

Note the first input type in the form:

<input type="hidden" name="_csrf" value="<?= getCsrfToken() ?>" />

This is where we are inserting a custom token into the current dialogue session. we have named it “_csrf”. You can also see that we have used Google Apps Script’s HTML printing scriptlets to display the anti-CSRF token returned from the getCsrfToken() function.

This function is called from the Google Apps Script side and generated as a part of the HTML template-building process. More on this function later.

 

Line 20-21: The submit button is added outside the form so as not to generate an error when clicked.

A response line is also added to display if the token is correct or not.

Create and Publish Google Workspace Add-ons with Apps Script Course 300px

Submitting the Form

submit()

Lines 43-64

When the ‘Submit’ button is clicked, the submit() function is invoked.

Here we retrieve the form element and use the new FormData() constructor to gather all the form responses including our hidden CSRF token.

Next, we iterate over the form data entries sorting the keys and values in the payload object.

Finally, we use the google.script.run API to send a stringified version of the payload back serverside.

We also invoke the withSuccessHandler method to return a message once validation of the token has been carried out.

onSuccess(e)

Once the CSRF token has been validated against the stored token value serverside (Apps Script-side) a stringified object will be returned back to the HTML file containing a hasError boolean property and a text string property.

If there was no match between the sent CSRF token and the stored token, we change our message colour to red.

Then we report the message in the ‘response’ span under the submit button.

Generate the CSRF Token in Google Apps Script

This function is called from the Index.html file template when it is being generated.

The function uses the Apps Script Utilities Service to generate a Unique User ID using the getUuid() method.

Next, the token is stored in the user’s property service. This way the token is only accessible for that use for this script. We will generate and store this token each time the user opens dialogue to make it even more challenging for an attacker to breach.

Finally, the token is returned so it can be stored in the HTML file.

If you have found the tutorial helpful, why not shout me a coffee ☕? I'd really appreciate it.

Validating the CSRF token in Apps Script

Lines 6-10: When the user submits the form, it is sent to the formInputs() function. Here we first parse the stringified form data back to an object and then set up a message object that we will return when our script is complete.

On line 14, we grab our stored CSRF token value so that we can compare it against the one coming in.

Then on lines 17- 27, we check if the form CSRF value matches the store CSRF token value. If it doesn’t, then we return the message variable with our error text.

If the tokens match, then you can carry on a validate your other form inputs before continuing with your data.

That’s it. That’s the whole script. Google Apps Script makes it really easy to implement this security token.

Is It Really Necessary to Add CSRF Tokens to your Google Workspace Dialogues and Sidebars?

Well…probably not. Particularly if you do not intend to publish your Add-on to the public. However, if you do have some restricted scopes that need to be authorised by your users then part of the CASA Tier 2 Assessment then it probably isn’t a huge deal.

The likelihood of someone finding or caring about your dialogues and then trying to exploit them along with Google OAuth and Iframe restrictions would make it pretty hard for a baddie to do any damage to your Google Workspace environment. But, you never know.

Create and Publish a Google Workspace Add-on with Apps Script Course

Need help with Google Workspace development?

My team of experts can help you with all of your needs, from custom app development to integrations and security. We have a proven track record of success in helping businesses of all sizes get the most out of Google Workspace.

Schedule a free consultation today to discuss your needs and get started or learn more about our services here.

~Yagi.

 

Adding Charts to Google Workspace Add-on Sidebar Apps with Apps Script

Creating dynamic charts in the sidebar of your Google Workspace Add-on can be an effective approach to getting across a lot of meaning in a somewhat confined space.

In this tutorial, we will use Google’s Chart API to generate a live chart, first from some static data and then live from an external data source like a Google Sheet. All with the help of a little Google Apps Script magic.

If you want to take things further still and learn how to create a dynamic chart dialogue overlay along with learning other approaches to displaying charts and handling errors, you can find this in the bonus material to my much larger and more details course Create and Publish Google Workspace Add-ons with Google Apps Script: Master Class. If you are already a course member, head over there now, ya magnificent legend you!

Let’s get into it!

Continue reading “Adding Charts to Google Workspace Add-on Sidebar Apps with Apps Script”

Add the Editor’s Email when they Tick the Check Box in Google Sheets with Apps Script

This tutorial is for Google Workspace Domain accounts.

Recently, I was a guest interviewee at a Google Workspace Developer Summit in Singapore and an attendee asked me how they could automatically add the editor’s email to an adjacent cell when the user checks a check box in Google Sheets.

This can be incredibly handy for project managers to see when a user completes a task or, at a glance, who edited a row.

Of course, there are some simple ways of doing this out-of-the-box within Google Sheets.

  1. A simple right-click > Show edit history on any cell can reveal who has edited the cell in the past.
    Show cell history in Google Sheets
  2. If you want a more detailed history of edits on your Google Sheet workbook then you can always select the version history button in the top right of your Sheet.

    Google Sheets Version History
    The ‘clock’ icon here opens the version history page.

The problem with these options is that it is not there on the screen for the user to quickly see who edited what line.

In this tutorial, we are going to use some Google Apps Script magic to automatically add the editor’s email to the row when they click that checkbox.

Let’s dive in!

Continue reading “Add the Editor’s Email when they Tick the Check Box in Google Sheets with Apps Script”

Creating Webhooks for Google Chat App with Apps Script

In this tutorial, we follow the adventures of Captain Webhook and his crew in their insatiable desire to report their latest booty conquest to me via Google Chat webhooks…

That, dear readers, did not come out right.

Webhooks are HTTP POST requests that are usually generated by a triggered event. The event could come from any third-party source within Google Workspace like:

Or it can occur with other third-party services. Imagine that you want to get an update from:

  • Your Stripe or PayPal account whenever a new payment comes in.
  • A course management service like Gumroad.
  • Patreon when another awesome supporter shows you some love.

Or when a rather rambunctious figment of my imagination insists on updating me when his latest haul of treasure has come in… live.

Sigh.

Captain Webhook of the Pirate Sheet HTTPS Request

Chat App webhooks will need an intermediary step for them to be compiled in a way that the Google Chat API can understand. We’ve chosen Google Apps Script here to do this, but you can choose to use any other language to convert your data into Chat API readable JSON or even build in a CLI to post your webhook request.

In this tutorial, we will cover how to:

Continue reading “Creating Webhooks for Google Chat App with Apps Script”

Add the Current Date to a Sheet When Data Is Added So That The Date Does Not Change(Static) – Google Sheets (Updated July 2023)

Google Sheets, Google Apps Script: onEdit

You’ve probably come across the problem where you need to know when a piece of data has been added to your spreadsheet. You probably have been equally frustrated that there is no out-of-the-box function that will do just this.

You’ve tried TODAY() and NOW(), but they change dynamically. What you really need here is something that does not change.

Let’s look at two workarounds that can help you out with this problem.

Continue reading “Add the Current Date to a Sheet When Data Is Added So That The Date Does Not Change(Static) – Google Sheets (Updated July 2023)”