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.

 

Creating an embedded interactive Chain Story app with Google Apps Script and Google Sheets

Google Apps Script: WebApp, HtmlService, LockService; Google Sheets

In this tutorial, we are going to create an interactive story chain app that we can embed into a Google Site or your own site like WordPress.

What’s a chain story, Yagi? 

Maybe you did this in school. Someone wrote the first part of a story. You then gave that story to someone else to continue writing. They then pass the story on to someone else to write the next part. And so on and so forth. In the end, the story is read out and everyone laughs at the direction the story went – except that one kid silently raging over their lack of control of the narrative.

Why are we making this? How’s it going to help me?

Well, for one, I thought it would be fun. More importantly, this will allow us to have a look at how Google Apps Scripts communicates client to server-side and vice versa in a little more advanced environment than our previous tutorial. It will also give us an opportunity to look at some more parts of Google Apps Script as they relate to creating a WebApp.

Our chain story WebApp tutorial will also give us an opportunity to look at some of the pitfalls of using WebaApp. Particularly when using the execute as me permissions. Finally, this will then launch us into our follow-up tutorial on updating the WebApp to execute as the user rather than me, the owner of the app.

This tutorial is the second part of the WebApp series. However, if you can read a bit of JS, CSS and HTML, you should be able to follow along and if you get stuck you can always go back to the first tutorial:

Google Apps Script: How to create a basic interactive interface with Web Apps

Let’s get started…

The Example: An interactive chain story

Embedded below is our interactive Chain Story web app. If you are feeling creative, read the story so far and then add your part to the story. It has been written by readers just like you:

Continue reading “Creating an embedded interactive Chain Story app with Google Apps Script and Google Sheets”

Creating Links from Custom Menus and Buttons in Google Sheets with Google Apps Script

This article should have been titled “Creating Links from Custom Menus and Buttons in Google Sheets with Google Apps Script: And Why it’s probably not a good idea”, but you know, I got to appease the SEO gods.

There is no natural or “out-of-the-box” way to create hyperlinks for custom menu items and buttons in Google Sheets. The solution I am providing below is a somewhat hacky approach, that I am not fond of and I will suggest some better alternatives in my summary.

However, there are a few occasions where you may feel forced into a corner as a developer to provide direct links from custom menus, buttons and images in Google Sheets. With this in mind, let’s get cracking.

The Example and Starter Sheet

In the example, I will provide custom links to my homepage and YouTube channel via a custom menu. I will also add an image link and button (Drawing) link.

Adding links to buttons drawings and menu items in Google Sheets with Google Apps Script

You can find a link to the starter sheet below:

The Starter Sheet

The Code

To attempt to transform a menu item or button into a link we use a Google Apps Script modal dialogue box as an intermediary. We can use HTML, JavaScript and CSS in these dialogues in the same way we would in a website.

This means that we can run the JavaScript window.open() command globally as soon as the dialogue opens which will open the link in a new tab (On most occasions) IF the user has given permission to unblock pop-ups for Google Sheets in their browser.

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

onOpen()

The onOpen() function is a built-in simple trigger in Google Apps Script that can run scripts when the Google Sheet (Or Google Doc, Slide or Form) opens.

In our example, we are using onOpen() to create our custom menu “Links”. We do this by using the SpreadsheetApp class’ getUi() instance class that allows us to build on the existing user interface. The UI class contains the Google Apps Script createMenu() builder which has its own set of methods to construct a custom menu.  The builder takes a menu name as an argument, which we have defined as “Links”.

We then use the addItem() method to build two sub-menus, ‘Yagisanatode.com’ and ‘YouTube’. This method takes a title that will appear in the menu and a function name that will be executed when the menu item is clicked.

The menu then needs to be built using the addToUi() method.

You can get a better understanding of menus in this beginner’s tutorial.

openYagi() openYT()

 

Each sub-menu directs to its own function. Both of these functions are identical and simply add the relevant URL for each button and then calls the openUrl() function.

These functions also have a secondary purpose for us. We can attach these function names to each of our buttons.

You can change out these functions to your own URL and names, just make sure you update the addItem methods in the create menu builder.

You can learn a lot more about how to run script from buttons, diagrams and images in Google Sheets with Apps Script in this tutorial:

https://yagisanatode.com/2019/03/07/google-apps-script-how-to-connect-a-button-to-a-function-in-google-sheets/

However, the basics are:

  1. Right-click the image.
  2. Select the vertical ellipses from the top right of the image.
  3. Select ‘Assign Script’.
  4. Paste in your function (without the braces () ).
  5. Select ‘Ok’.

openUrl(url)

The openUrl() function contains the selected URL as a parameter.

The is the function that gets all of our work done.

blob(The HTML)

blob gif for the chapter on html blob content for dialog boxes in apps script

Our blob text is basically our HTML page that will make up our modal dialogue box. The constant variable blob looks like this:

Keep in mind that the above HTML is encapsulated in template literals (backticks (`)).

I’ve set the stylesheet to use Google’s recommend CSS package for Add-on, sidebars and dialogues for ease and consistency. Line 5

The Script tags

Let’s skip the div for now and head down to the content in the script tags. Lines 16-25

First, we set a variable named, urlLinked to window.open("${url}"). This is a JavaScript DOM method that opens a URL when called. In our case, we have added our selected URL as a template literal tag. Line 18

If the URL is able to be opened in a new tab urlLinked will return an object or for our purpose a “truthy’ result so we know everything worked and we can close the dialogue automatically with google.script.host.close()Lines 19-20

If the URL could not be opened in a new tab (likely a result of your browser’s pop-up blocker) then urlLinked will return nullWe then need to select our HTML div with an id of “blocked” and unhide it. Line 21-22

The “blocked” div

Our “blocked” HTML div is unhidden when the browser prevents the script from automatically opening the new window.

The first thing we need to do is give the user a link to the URL they were looking for. You will notice that we use window.open() again instead of the URL directly. This is because dialogues and sidebars in Google Sheets are embedded iframes. You can learn more about this here:

Using Hyperlinks in Dialogs and Sidebars to open a URL in a new Tab with Google Apps Script

Next, we provide a message explaining what is going on and how they might want to fix it in future.

Finally, we provide a close button for them to exit from the dialogue box should they have an aversion to clicking the “x” in the top right of the box.

Backup dialog box if url did not automatically open google apps script

Why directly linking from Menu items images and buttons is not a great approach.

The user still needs to authorise the script

This might be fine if you have some links say to an external help page as a part of a larger project, but it is probably not a sound idea to create a simple project that just produces links from menus or buttons. The user is already going to be annoyed that they have to go through the Google Apps Script permission process.

Perhaps if you are publishing an add-on this might be okay, because the permission process is a lot smoother.

It doesn’t always work

As you can see in the script above, we have included a failsafe if a pop-up blocker is enabled. More often than not this will be the result of running the script unless the user specifically unblocks popups in their browser for Google Sheets.

It’s hacky – Google is probably not amused

There are always a few bugs and better implementations that arise in large software projects like Google Sheets. However, in this case, I get the feeling that Google has a very good reason for you to not directly link from menu bars and buttons in this way.

If you disagree:

  1. Let me know in the comments below. It’s always good to get alternate perspectives.
  2. You could always make a feature request on Google’s Issue Tracker (feel free to link your issue in the comments below).

Workarounds and alternatives

Simulated mouse clicking

simulated mouse clicking in demolition man
Simulated mouse event first seen in Demolition Man

There is a clever bit of code by Stephen M Harris that used the same modal dialogue event but first sets a timeout on the dialogue to close. The script then generates an anchor element followed by a scripted mouse click event which is a common workaround to prevent pop-up blockers from running.

Stephen also handles for Firefox’s idiosyncracies with his script too.

Personally, I think this is a little too forced for my liking, but many devs would disagree and I have seen a number of Google Workspace Marketplace apps using Stephen’s code. So, what do I know? 🤷‍♂️

You can find a link to the solution here on StackOverflow. If anything, give him some upvote love for the clever workaround.

External link dialogue warning

As you’ve traversed the Internet, have you ever seen a dialogue that warns you that you are about to navigate to an external link?

Yeah, this is one extra step for the user and that is a definite downside, but I also think that it better conforms with how Google intended links to be accessed from the UI.

This alternative is probably better suited to menu items rather than buttons and images.

Let’s go ahead and update the blob constant variable.

External link warning dialog Google Apps Script

Add a links page as a dialogue or sidebar

Another really useful approach is to create a separate links page as a dialogue or sidebar. This keeps all of your links in one location and out of your main document.

This is probably my main go-to and my clients tend to learn quickly how to get to these links to find resources and more instructions that complement the document or sheet that they are working in.

You can see some examples of this in this tutorial:

Using Hyperlinks in Dialogs and Sidebars to open a URL in a new Tab with Google Apps Script

Add a links sheet tab

If you are working in Google Sheets and you want to get something out quickly, why not just make a “Links” sheet tab for users to access? You can always protect the sheet tab, users will still be able to access the link without editing the page.

Button and Images can be linked without code

In a previous tutorial, I cover a few approaches on how to apply hyperlinks to images in cells in Google Sheets. This way when the user hovers over the link, they will see the link (with preview) that they can click on.

Adding links to images in Google Sheets

Conclusion

Well, this was probably not the satisfying answer you were, hoping for, sorry. But we have covered a number of ways for you to provide links to users from the somewhat ethically dubious to some good alternatives.

I would love to hear what your solutions were in the comments below and how you went about implementing them. Not only does it interest me, but others may find your perspectives and use cases helpful for your own projects.

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

How to Automatically Share Teachable Students to Google Drive Files and Folders when they Enroll in your Course

Not only are Google Workspaces, Google Sheets, Docs, Forms and Slide great to work in and look awesome, but the convenience of collaborating and sharing your Google Drive Files and Folders is also super useful. So much so that many course creators share their documents with their students all the time.

The problem that course creators have is that they are generally stuck with two options when sharing their Google Drive files and folders:

  1. Set sharing to Anyone with Link can view. And hope other freeloading students don’t find and share their course material.
  2. Manually share each student as they enrol. Which is time-consuming for the course creator and annoying for the student who needs to wait to be shared before they can get their hands on your awesome course content.

Both options are really terrible.

I reluctantly chose option one for my first Google Sheets Essentials Teachable Course and it really bothered me. I needed to find a way to share my Google Drive course content with only those students who signed up for my course.

In this tutorial, I will guide you through creating a Google Apps Script web app that receives a webhook notification when a student enrols onto one of my Teachable courses. If a student enrolled with a non-Gmail or non-Google Workspace domain email account, they will be sent an email with an attached form to add a Google-friendly email.

If you want a copy of the Google Sheet with the Apps Script attached, without coding it all yourself, plus written-visual tutorials on how to quickly set up your sheet head over to my teachable page now and purchase the sheet and instructions for just $2.95. Yeap not even the price of a cuppa.

The fun thing is that you will experience how the whole process works, because…well…that’s how I am going to share the Google Sheets file with you when you enrol. Neat, hey?

As a part of your purchase you will also get a few other perks:

  • Set files or folders for ‘view’, ‘comment’ or ‘edit’ access. 
  • Add existing students to your selected course Google Drive Files and Folders.
  • Get your full course list from your Teachable site right in your Sheet. 
  • A choice to bulk set your files and folders to:
    • prevent downloads, copying and print.
    • Prevent sharing by any documents you have provided ‘edit’ permission to.

If you want to understand how it all works and build your own, read on, you can always throw a couple of coins at me and enrol to run the workflow just for fun.

Instantly share ONLY Teach:able Students to selected Google Drive Files and Folders

 

If you are looking to build your own Teachable Course you can check out a how-to guide here:

How to launch an online course—and craft your email strategy

Continue reading “How to Automatically Share Teachable Students to Google Drive Files and Folders when they Enroll in your Course”

How to Validate Specific Users on a Web App in Google Apps Scripts

You’ve created an awesome Google Apps Script web app for your secret society within your Google Workspace organisation or …dom! dom! DOM! … the world. The problem is that you only want to share your web app with the worthy. Those selected few. 🐐🛐🛐🛐

How do you do this? How to prevent this most coveted of apps from reaching the wrong hands?

It’s actually surprisingly simple.

In this tutorial, we will explore how to validate selected users to provide access to your web app. For our example, we validate users based on whether or not they have edit access to a Google Drive file ( a common occurrence). In the discussion, we will also look at alternative ways of validating emails.

One of the bonuses of the approach we will go through is that it can also be easily adapted for use in Google Workspace Add-ons, and Editor Add-ons like sidebars and dialogue boxes.

We’ll start off with an example and then move to a quick-use guide for those of you who just want to get in and apply the code to your own project. Then for those who want to know how it all works, I’ll dive into the details.

Let’s get started!

Continue reading “How to Validate Specific Users on a Web App in Google Apps Scripts”