Google Workspace Hardening Guide
Comprehensive security hardening for Google Workspace, Gmail, Drive, and Google Admin Console
Overview
Google Workspace is used by over 9 million organizations worldwide for email, document collaboration, and cloud storage. As a primary target for phishing and credential theft, Google Workspace security is critical—phishing was responsible for financially devastating data breaches for 9/10 organizations in 2024. According to CISA, accounts with MFA enabled are 99% less likely to be compromised.
Intended Audience
- Security engineers managing Google Workspace environments
- IT administrators configuring Admin Console security
- GRC professionals assessing cloud productivity compliance
- Third-party risk managers evaluating Google integrations
How to Use This Guide
- L1 (Baseline): Essential controls for all organizations
- L2 (Hardened): Enhanced controls for security-sensitive environments
- L3 (Maximum Security): Strictest controls for regulated industries
Scope
This guide covers Google Workspace Admin Console security configurations including authentication policies, OAuth app controls, Drive sharing settings, Gmail protection, and device management. Google Cloud Platform (GCP) infrastructure is covered in a separate guide.
Table of Contents
- Authentication & Access Controls
- Network Access Controls
- OAuth & Integration Security
- Data Security
- Monitoring & Detection
- Third-Party Integration Security
- Compliance Quick Reference
1. Authentication & Access Controls
1.1 Enforce Multi-Factor Authentication for All Users
Profile Level: L1 (Baseline)
| Framework | Control |
|---|---|
| CIS Controls | 6.3, 6.5 |
| NIST 800-53 | IA-2(1), IA-2(6) |
| CIS Google Workspace | 1.1 |
Description
Require 2-Step Verification (2SV) for all users with enforcement, not just enrollment. Microsoft found that enabling MFA prevents 99.9% of automated attacks on cloud accounts.
Rationale
Why This Matters:
- Phishing remains the #1 attack vector against Google Workspace
- Password reuse and credential stuffing are common attack methods
- Accounts with MFA are 99% less likely to be compromised (CISA)
Attack Prevented: Credential theft, phishing, password spray, account takeover
Real-World Incidents:
- Twitter (2020): Compromised employee credentials led to high-profile account takeover
- Colonial Pipeline (2021): VPN credentials without MFA enabled ransomware deployment
Prerequisites
- Super Admin access to Google Admin Console
- User communication plan for 2SV enrollment
- Security keys for privileged users (recommended)
ClickOps Implementation
Step 1: Enable 2-Step Verification Enrollment
- Navigate to: Admin Console → Security → Authentication → 2-Step Verification
- Check Allow users to turn on 2-Step Verification
- Set Enforcement to On for all organizational units
- Configure New user enrollment period: 7 days (grace period)
- Click Save
Step 2: Configure Allowed Methods
- In the same section, click Allowed methods
- Select Any except verification codes via text or phone call (recommended)
- This forces users to use authenticator apps or security keys instead of vulnerable SMS
Step 3: Enforce Security Keys for Admins
- Navigate to: Security → Authentication → 2-Step Verification
- Select the Admin organizational unit
- Set Allowed methods to Only security key
- Click Save
Time to Complete: ~30 minutes
Code Implementation
Validation & Testing
How to verify the control is working:
- Sign in as test user - 2SV prompt should appear
- Check Admin Console → Reports → User Reports → Security
- Verify 2SV enrollment percentage approaches 100%
- Attempt sign-in with only password - should fail after enforcement
Expected result: All users prompted for second factor, enforcement active
Monitoring & Maintenance
Ongoing monitoring:
- Monitor Security → Investigation Tool for failed 2SV attempts
- Alert on suspicious sign-in attempts
- Track 2SV enrollment completion
Admin Console Query: Navigate to Security, then Investigation Tool. Set Event to Login, and filter by 2SV method = None, Login result = Success.
Maintenance schedule:
- Weekly: Review new user 2SV enrollment
- Monthly: Audit 2SV enforcement exceptions
- Quarterly: Review and rotate Super Admin security keys
Operational Impact
| Aspect | Impact Level | Details |
|---|---|---|
| User Experience | Low-Medium | Initial enrollment required; subsequent logins add ~5 seconds |
| System Performance | None | No performance impact |
| Maintenance Burden | Low | Minimal after initial rollout |
| Rollback Difficulty | Easy | Disable enforcement in Admin Console |
Potential Issues:
- Users without smartphones: Provide hardware security keys
- Shared device environments: Use security keys instead of mobile apps
Rollback Procedure:
- Navigate to Admin Console → Security → 2-Step Verification
- Set Enforcement to Off
- Note: This leaves accounts vulnerable; use only for emergency troubleshooting
Compliance Mappings
| Framework | Control ID | Control Description |
|---|---|---|
| SOC 2 | CC6.1 | Logical access security |
| NIST 800-53 | IA-2(1) | Multi-factor authentication |
| ISO 27001 | A.9.4.2 | Secure log-on procedures |
| CIS Google Workspace | 1.1 | Ensure 2-Step Verification is enforced |
Code Pack: Terraform
# Retrieve all users to audit 2SV enrollment status.
# The googleworkspace provider does not expose a direct 2SV enforcement toggle;
# enforcement is configured via Admin Console or GAM. This data source lets
# Terraform surface enrollment gaps so you can detect non-compliant users.
data "googleworkspace_users" "all" {
filter = "isEnrolledIn2Sv=false"
}
# Create a dedicated OU for users who must complete 2SV enrollment.
# Moving users into this OU lets you apply stricter policies (e.g., security-
# key-only) via Admin Console while tracking the OU in Terraform state.
resource "googleworkspace_org_unit" "mfa_enforcement" {
name = "MFA Enforcement"
description = "HTH 1.1 -- Users in this OU are subject to 2SV enforcement policies"
parent_org_unit_path = var.target_org_unit_path
}
# Create an OU for Super Admins who require security-key-only 2SV (L3).
resource "googleworkspace_org_unit" "super_admin_mfa" {
count = var.profile_level >= 3 ? 1 : 0
name = "Super Admins - Security Key Only"
description = "HTH 1.1 L3 -- Super Admins restricted to hardware security keys"
parent_org_unit_path = var.target_org_unit_path
}
# Group for tracking users who have NOT enrolled in 2SV.
# Membership is managed outside Terraform (via GAM or Admin SDK scripts) but
# having the group in state ensures it exists and can be referenced by alerts.
resource "googleworkspace_group" "mfa_not_enrolled" {
email = "mfa-not-enrolled@${var.primary_domain}"
name = "MFA Not Enrolled"
description = "HTH 1.1 -- Users who have not yet enrolled in 2-Step Verification. Auto-populated by audit scripts."
}
Code Pack: CLI Script
# List users not enrolled in 2SV
gam print users query "isEnrolledIn2Sv=false"
# Generate report of 2SV status
gam report users parameters accounts:is_2sv_enrolled,accounts:is_2sv_enforced
# Send reminder to users not enrolled
gam print users query "isEnrolledIn2Sv=false" | \
gam csv - gam user ~primaryEmail sendemail subject "MFA Enrollment Required" \
message "Please enroll in 2-Step Verification within 7 days."
# Admin Console monitoring query for users without 2SV
# Navigate to: Security → Investigation Tool
# Event: Login
# Filter: 2SV method = None, Login result = Success
Code Pack: SDK Script
# Enable 2SV enforcement via Admin SDK
from googleapiclient.discovery import build
from google.oauth2 import service_account
SCOPES = ['https://www.googleapis.com/auth/admin.directory.user']
SERVICE_ACCOUNT_FILE = 'service-account.json'
credentials = service_account.Credentials.from_service_account_file(
SERVICE_ACCOUNT_FILE, scopes=SCOPES)
credentials = credentials.with_subject('admin@yourdomain.com')
service = build('admin', 'directory_v1', credentials=credentials)
# Check 2SV enrollment status for users
results = service.users().list(
customer='my_customer',
maxResults=100,
orderBy='email',
projection='full'
).execute()
users = results.get('users', [])
for user in users:
email = user['primaryEmail']
is_enrolled = user.get('isEnrolledIn2Sv', False)
is_enforced = user.get('isEnforcedIn2Sv', False)
print(f"{email}: Enrolled={is_enrolled}, Enforced={is_enforced}")
1.2 Restrict Super Admin Account Usage
Profile Level: L1 (Baseline)
| Framework | Control |
|---|---|
| CIS Controls | 5.4 |
| NIST 800-53 | AC-6(1), AC-6(5) |
| CIS Google Workspace | 1.2 |
Description
Limit Super Admin privileges to 2-4 dedicated accounts, enforce security keys for authentication, and use delegated admin roles for day-to-day administration.
Rationale
Why This Matters:
- Super Admin accounts have unrestricted access to all data and settings
- Compromised Super Admin = complete organization compromise
- Delegated roles follow principle of least privilege
Attack Prevented: Privilege escalation, lateral movement, admin account compromise
Prerequisites
- Inventory of current Super Admin accounts
- Security keys for all Super Admins
- Defined delegated admin roles
ClickOps Implementation
Step 1: Audit Current Super Admins
- Navigate to: Admin Console → Account → Admin roles
- Click Super Admin role
- Review assigned users - should be 2-4 maximum
- Document and remove unnecessary assignments
Step 2: Create Delegated Admin Roles
- Navigate to: Admin Console → Account → Admin roles
- Click Create new role
- Create role-specific admins:
- User Admin: Manage users, reset passwords
- Groups Admin: Manage groups and memberships
- Help Desk Admin: Reset passwords, view user info
- Assign appropriate permissions for each role
Step 3: Enforce Security Keys for Super Admins
- Create organizational unit: Super Admins
- Move Super Admin accounts to this OU
- Navigate to: Security → 2-Step Verification
- Select Super Admins OU
- Set Allowed methods to Only security key
Time to Complete: ~45 minutes
Code Implementation
Validation & Testing
- Verify only 2-4 Super Admin accounts exist
- Confirm all Super Admins use security keys
- Test delegated admin can perform assigned tasks only
- Verify delegated admin cannot access Super Admin functions
Code Pack: Terraform
# Create delegated admin roles following the principle of least privilege.
# Super Admin accounts should be limited to 2-4; day-to-day admin tasks
# should use these scoped roles instead.
resource "googleworkspace_role" "user_admin" {
name = "HTH User Administrator"
description = "HTH 1.2 -- Delegated role for user management (password resets, profile updates)"
privileges {
service_id = "00haapch16h1ysv" # Admin SDK - Users
privilege = "USERS_RETRIEVE"
}
privileges {
service_id = "00haapch16h1ysv"
privilege = "USERS_UPDATE"
}
privileges {
service_id = "00haapch16h1ysv"
privilege = "USERS_ALIAS"
}
}
resource "googleworkspace_role" "groups_admin" {
name = "HTH Groups Administrator"
description = "HTH 1.2 -- Delegated role for group management (create, update, membership)"
privileges {
service_id = "00haapch16h1ysv"
privilege = "GROUPS_RETRIEVE"
}
privileges {
service_id = "00haapch16h1ysv"
privilege = "GROUPS_UPDATE"
}
}
resource "googleworkspace_role" "help_desk_admin" {
name = "HTH Help Desk Administrator"
description = "HTH 1.2 -- Delegated role for help desk (password resets, view user info)"
privileges {
service_id = "00haapch16h1ysv"
privilege = "USERS_RETRIEVE"
}
privileges {
service_id = "00haapch16h1ysv"
privilege = "USERS_UPDATE"
}
}
# Create custom delegated roles from variable input
resource "googleworkspace_role" "custom" {
for_each = var.delegated_admin_roles
name = each.key
description = each.value.description
dynamic "privileges" {
for_each = each.value.privileges
content {
service_id = privileges.value.service_id
privilege = privileges.value.privilege
}
}
}
# Create an OU for Super Admin accounts so security-key-only 2SV
# can be applied at the OU level via Admin Console.
resource "googleworkspace_org_unit" "super_admins" {
name = "Super Admins"
description = "HTH 1.2 -- Dedicated OU for Super Admin accounts with security-key-only 2SV"
parent_org_unit_path = var.target_org_unit_path
}
# Group for auditing Super Admin role holders.
# Membership should be manually managed and kept to 2-4 accounts.
resource "googleworkspace_group" "super_admin_audit" {
email = "super-admin-audit@${var.primary_domain}"
name = "Super Admin Audit"
description = "HTH 1.2 -- Tracks Super Admin role holders. Should contain 2-4 members maximum."
}
Code Pack: CLI Script
# List all Super Admins
gam print admins role "Super Admin"
# Create delegated admin role
gam create adminrole "Help Desk Admin" privileges \
USERS_RETRIEVE,USERS_UPDATE,USERS_ALIAS
# Assign delegated role
gam create admin user helpdesk@domain.com role "Help Desk Admin"
# Remove Super Admin from non-essential users
gam delete admin user bob@domain.com role "Super Admin"
1.3 Configure Context-Aware Access
Profile Level: L2 (Hardened)
| Framework | Control |
|---|---|
| CIS Controls | 6.4, 13.5 |
| NIST 800-53 | AC-2(11), AC-6(1) |
Description
Implement context-aware access policies that evaluate device, location, and user risk before granting access to Google Workspace applications.
Rationale
Why This Matters:
- Allows enforcement of device compliance before access
- Can block access from high-risk locations
- Provides additional layer beyond authentication
Prerequisites
- Google Workspace Enterprise Standard or Plus
- BeyondCorp Enterprise (for advanced features)
- Endpoint Verification deployed to managed devices
ClickOps Implementation
Step 1: Deploy Endpoint Verification
- Navigate to: Admin Console → Devices → Mobile & endpoints → Settings
- Enable Endpoint Verification
- Deploy Chrome extension to managed devices
- Or use Google’s Endpoint Verification app for unmanaged devices
Step 2: Create Access Level
- Navigate to: Security → Access and data control → Context-Aware Access
- Click Access Levels → Create Access Level
- Configure conditions:
- Device must have Endpoint Verification
- Device must be encrypted
- Device must have screen lock
- Save access level
Step 3: Assign Access Level to Apps
- In Context-Aware Access, click Assign Access Levels
- Select apps (Gmail, Drive, etc.)
- Assign the created access level
- Enable enforcement after testing
Time to Complete: ~1 hour
Code Implementation
Code Pack: Terraform
# Context-Aware Access requires Google Workspace Enterprise Standard/Plus
# and uses Access Context Manager (part of BeyondCorp Enterprise).
# Access policy -- one per organization. If you already have an access
# policy, import it rather than creating a new one.
resource "google_access_context_manager_access_policy" "workspace" {
count = var.profile_level >= 2 && var.organization_id != "" ? 1 : 0
parent = "organizations/${var.organization_id}"
title = var.access_policy_name
}
# Access level: require managed device with Endpoint Verification,
# disk encryption, and screen lock.
resource "google_access_context_manager_access_level" "managed_device" {
count = var.profile_level >= 2 && var.organization_id != "" ? 1 : 0
parent = "accessPolicies/${google_access_context_manager_access_policy.workspace[0].name}"
name = "accessPolicies/${google_access_context_manager_access_policy.workspace[0].name}/accessLevels/hth_managed_device"
title = "HTH Managed Device"
basic {
conditions {
device_policy {
require_screen_lock = true
allowed_encryption_statuses = ["ENCRYPTED"]
require_admin_approval = false
require_corp_owned = false
allowed_device_management_levels = ["BASIC", "COMPLETE"]
}
}
}
}
# L3: Stricter access level requiring corporate-owned, fully managed devices
resource "google_access_context_manager_access_level" "corp_device" {
count = var.profile_level >= 3 && var.organization_id != "" ? 1 : 0
parent = "accessPolicies/${google_access_context_manager_access_policy.workspace[0].name}"
name = "accessPolicies/${google_access_context_manager_access_policy.workspace[0].name}/accessLevels/hth_corp_device"
title = "HTH Corporate-Owned Device"
basic {
conditions {
device_policy {
require_screen_lock = true
allowed_encryption_statuses = ["ENCRYPTED"]
require_admin_approval = true
require_corp_owned = true
allowed_device_management_levels = ["COMPLETE"]
}
}
}
}
2. Network Access Controls
2.1 Configure Allowed IP Ranges for Admin Console
Profile Level: L2 (Hardened)
| Framework | Control |
|---|---|
| CIS Controls | 13.5 |
| NIST 800-53 | AC-17, SC-7 |
Description
Restrict Admin Console access to specific IP ranges (corporate network, VPN) to prevent unauthorized administrative access.
ClickOps Implementation
Step 1: Configure Allowed IPs
- Navigate to: Admin Console → Security → Access and data control → Context-Aware Access
- Create access level with IP conditions
- Specify corporate egress IP ranges
- Apply to Admin Console access
Step 2: Alternative - Session Control
- Navigate to: Security → Google Cloud session control
- Configure reauthentication frequency for sensitive apps
Code Implementation
Code Pack: Terraform
# Restrict Admin Console access to known corporate IP ranges.
# Uses Access Context Manager to create an IP-based access level that
# can be applied to Admin Console and other sensitive applications.
resource "google_access_context_manager_access_level" "admin_ip_allowlist" {
count = var.profile_level >= 2 && length(var.admin_allowed_cidrs) > 0 && var.organization_id != "" ? 1 : 0
parent = "accessPolicies/${google_access_context_manager_access_policy.workspace[0].name}"
name = "accessPolicies/${google_access_context_manager_access_policy.workspace[0].name}/accessLevels/hth_admin_ip_allowlist"
title = "HTH Admin Console IP Allowlist"
basic {
conditions {
ip_subnetworks = var.admin_allowed_cidrs
}
}
}
# L3: Combine IP restriction with managed device requirement
resource "google_access_context_manager_access_level" "admin_ip_and_device" {
count = var.profile_level >= 3 && length(var.admin_allowed_cidrs) > 0 && var.organization_id != "" ? 1 : 0
parent = "accessPolicies/${google_access_context_manager_access_policy.workspace[0].name}"
name = "accessPolicies/${google_access_context_manager_access_policy.workspace[0].name}/accessLevels/hth_admin_ip_and_device"
title = "HTH Admin Console IP + Device"
basic {
combining_function = "AND"
conditions {
ip_subnetworks = var.admin_allowed_cidrs
}
conditions {
device_policy {
require_screen_lock = true
allowed_encryption_statuses = ["ENCRYPTED"]
require_corp_owned = true
allowed_device_management_levels = ["COMPLETE"]
}
}
}
}
3. OAuth & Integration Security
3.1 Enable OAuth App Whitelisting
Profile Level: L1 (Baseline)
| Framework | Control |
|---|---|
| CIS Controls | 2.5 |
| NIST 800-53 | AC-3, CM-7 |
| CIS Google Workspace | 2.1 |
Description
Restrict which third-party applications can access Google Workspace data via OAuth. Block unverified apps and require admin approval for new integrations.
Rationale
Why This Matters:
- OAuth consent phishing is a growing attack vector
- Malicious apps can gain persistent access to email and files
- Users often grant excessive permissions without understanding risks
Attack Prevented: OAuth consent phishing, malicious app installation, data exfiltration
Real-World Incidents:
- Google Docs Phishing (2017): Fake “Google Docs” app tricked users into granting email access
- Multiple incidents of data-stealing apps masquerading as productivity tools
Prerequisites
- Inventory of currently authorized OAuth apps
- Business justification for each approved app
- User communication about approval process
ClickOps Implementation
Step 1: Review Current OAuth Apps
- Navigate to: Admin Console → Security → API controls → App access control
- Click Manage Third-Party App Access
- Review list of apps with access to organizational data
- Document apps that should be allowed
Step 2: Configure App Whitelisting
- In App access control, click Settings
- Set default policy: Block all third-party API access or Block unconfigured third-party apps
- Configure trusted apps list
- Click Save
Step 3: Whitelist Approved Apps
- Click Add app → OAuth App Name or Client ID
- Search for app or enter Client ID
- Configure access level:
- Trusted: Full access to requested scopes
- Limited: Access to only non-sensitive scopes
- Blocked: No access
- Add business justification
- Click Configure
Time to Complete: ~1 hour (initial configuration), ongoing for new app requests
Code Implementation
Validation & Testing
- Verify blocked apps cannot access data
- Test app approval workflow
- Review Security Investigation Tool for blocked app attempts
- Confirm whitelisted apps function correctly
Expected result: Only approved apps can access organizational data
Compliance Mappings
| Framework | Control ID | Control Description |
|---|---|---|
| SOC 2 | CC6.1 | Logical access security |
| NIST 800-53 | AC-3 | Access enforcement |
| CIS Google Workspace | 2.1 | Ensure third-party apps are audited and controlled |
Code Pack: Terraform
# The googleworkspace provider does not directly manage the OAuth app
# allowlist/blocklist setting. These resources create the organizational
# infrastructure for OAuth governance:
#
# 1. A group to track approved apps and their owners
# 2. An OU for users with restricted OAuth access
# 3. Documentation of the manual Admin Console steps required
#
# Full API-level control requires GAM or the Admin SDK.
# Group for OAuth app governance -- members are app owners/reviewers
resource "googleworkspace_group" "oauth_reviewers" {
email = "oauth-app-reviewers@${var.primary_domain}"
name = "OAuth App Reviewers"
description = "HTH 3.1 -- Members review and approve third-party OAuth app requests"
}
# Group to receive notifications about blocked OAuth app access attempts
resource "googleworkspace_group" "oauth_blocked_alerts" {
email = "oauth-blocked-alerts@${var.primary_domain}"
name = "OAuth Blocked App Alerts"
description = "HTH 3.1 -- Receives alerts when users attempt to authorize blocked OAuth apps"
}
# OU for users with strictly no third-party OAuth access (high-security users)
resource "googleworkspace_org_unit" "oauth_restricted" {
count = var.profile_level >= 2 ? 1 : 0
name = "OAuth Restricted Users"
description = "HTH 3.1 L2 -- Users in this OU cannot authorize any third-party OAuth apps"
parent_org_unit_path = var.target_org_unit_path
}
# Retrieve the domain to verify it exists before creating domain-level resources
data "googleworkspace_domain" "primary" {
domain_name = var.primary_domain
}
Code Pack: CLI Script
# List all OAuth tokens in use
gam all users print tokens
# List apps with specific scopes
gam all users print tokens scopes "https://mail.google.com/"
# Revoke tokens for specific app
gam all users deprovision token clientid 1234567890.apps.googleusercontent.com
# Block unverified apps (via Admin SDK)
# Note: Use Admin Console for comprehensive control
Code Pack: SDK Script
# List OAuth tokens for a user
from googleapiclient.discovery import build
service = build('admin', 'directory_v1', credentials=credentials)
tokens = service.tokens().list(userKey='user@domain.com').execute()
for token in tokens.get('items', []):
print(f"App: {token['displayText']}")
print(f"Client ID: {token['clientId']}")
print(f"Scopes: {token['scopes']}")
print("---")
3.2 Disable Less Secure Apps
Profile Level: L1 (Baseline)
| Framework | Control |
|---|---|
| CIS Controls | 4.2 |
| NIST 800-53 | IA-2 |
Description
Disable “Less Secure Apps” access which allows applications to authenticate with just username/password, bypassing 2-Step Verification.
Rationale
Why This Matters:
- Less Secure Apps bypass MFA completely
- Legacy protocols are targets for password spray
- Google has deprecated this feature, but some tenants may still have it enabled
ClickOps Implementation
Step 1: Disable Less Secure Apps
- Navigate to: Admin Console → Security → Less secure apps
- Select Disable access to less secure apps (Recommended)
- Click Save
Note: This should be disabled by default for most tenants as of recent Google updates.
Code Implementation
Code Pack: Terraform
# "Less Secure Apps" allow authentication with username/password only,
# completely bypassing 2-Step Verification. Google has deprecated this
# feature but some legacy tenants may still have it enabled.
#
# The googleworkspace provider does not expose a direct toggle for the
# organization-wide "Less Secure Apps" setting. This control creates
# the organizational structure to enforce the policy:
#
# 1. An OU for legacy app users who temporarily need access (with
# an expiration plan)
# 2. A tracking group for audit and remediation
#
# Enforcement is done via:
# Admin Console > Security > Less secure apps > Disable access (Recommended)
#
# Or via GAM:
# gam ou / update less_secure_apps DISABLED
# Tracking group for applications still requiring less-secure-app access.
# This group should be empty -- any members represent technical debt.
resource "googleworkspace_group" "legacy_app_tracking" {
email = "legacy-app-tracking@${var.primary_domain}"
name = "Legacy App Tracking"
description = "HTH 3.2 -- Tracks applications requiring less-secure-app access. Target: zero members."
}
# Temporary OU for users who need transitional legacy app access.
# Should be emptied and removed within 90 days.
resource "googleworkspace_org_unit" "legacy_app_exception" {
count = var.profile_level >= 1 ? 1 : 0
name = "Legacy App Exceptions"
description = "HTH 3.2 -- Temporary OU for users needing legacy app access. Must be emptied within 90 days."
parent_org_unit_path = var.target_org_unit_path
}
4. Data Security
4.1 Configure External Drive Sharing Restrictions
Profile Level: L1 (Baseline)
| Framework | Control |
|---|---|
| CIS Controls | 3.3 |
| NIST 800-53 | AC-3, AC-22 |
| CIS Google Workspace | 3.1 |
Description
Restrict external sharing of Google Drive files to prevent unauthorized data exposure. Configure default sharing settings to “Restricted” and control “Anyone with the link” sharing.
Rationale
Why This Matters:
- Oversharing is one of the biggest security risks in Google Workspace
- “Anyone with the link” files can be accessed by anyone who discovers the URL
- Data exposure from misconfigured sharing is common
Attack Prevented: Data exfiltration, accidental data exposure, insider threats
Prerequisites
- Inventory of current sharing policies
- Business requirements for external collaboration
ClickOps Implementation
Step 1: Configure Organization-Wide Sharing
- Navigate to: Admin Console → Apps → Google Workspace → Drive and Docs
- Click Sharing settings
- Configure Sharing options:
- Sharing outside of [organization]: Off or Allowlisted domains only
- Default link sharing: Restricted (only people added)
- Click Save
Step 2: Configure Sharing for Specific OUs
- Select organizational unit from left panel
- Override settings for teams requiring external collaboration
- Use most restrictive settings possible
Step 3: Disable “Anyone with the link”
- In Sharing settings, find Link sharing default
- Set to Restricted (not “Anyone with the link”)
- Optionally block “Anyone with the link” entirely
Time to Complete: ~30 minutes
Code Implementation
Validation & Testing
- Create test file and verify default sharing is Restricted
- Attempt to share externally - verify appropriate restrictions apply
- Audit existing files with external sharing
- Confirm allowed external sharing still functions
Code Pack: Terraform
# Restrict external sharing of Google Drive files. The googleworkspace
# provider does not directly manage Drive sharing settings (those are
# configured in Admin Console > Apps > Drive and Docs > Sharing settings).
#
# This control creates the organizational infrastructure to support
# sharing restrictions:
#
# 1. An OU for teams that need external collaboration (override at OU level)
# 2. Groups for managing allowed external domains
# 3. Audit tracking for files shared externally
# OU for teams that require external Drive sharing (e.g., Sales, Partnerships)
# These teams get slightly relaxed sharing settings while the rest of the
# organization defaults to internal-only.
resource "googleworkspace_org_unit" "external_sharing_allowed" {
name = "External Sharing Allowed"
description = "HTH 4.1 -- Users in this OU may share Drive files with approved external domains"
parent_org_unit_path = var.target_org_unit_path
}
# OU for highly sensitive teams with no external sharing whatsoever
resource "googleworkspace_org_unit" "no_external_sharing" {
count = var.profile_level >= 2 ? 1 : 0
name = "No External Sharing"
description = "HTH 4.1 L2 -- Users in this OU cannot share Drive files externally under any circumstances"
parent_org_unit_path = var.target_org_unit_path
}
# Group for tracking external sharing exceptions and approvals
resource "googleworkspace_group" "external_sharing_approvers" {
email = "external-sharing-approvers@${var.primary_domain}"
name = "External Sharing Approvers"
description = "HTH 4.1 -- Members can approve external Drive sharing requests"
}
# Group for collecting external sharing audit notifications
resource "googleworkspace_group" "external_sharing_audit" {
email = "external-sharing-audit@${var.primary_domain}"
name = "External Sharing Audit"
description = "HTH 4.1 -- Receives notifications about external file sharing activity"
}
Code Pack: CLI Script
# Audit files shared externally
gam all users print filelist query "visibility='anyoneWithLink' or visibility='anyoneCanFind'"
# Find files shared with specific external domains
gam all users print filelist query "sharedWithExternalUsers"
# Generate sharing report
gam report drive user all parameters doc_type,visibility,shared_with_user_accounts
4.2 Enable Data Loss Prevention (DLP)
Profile Level: L2 (Hardened)
| Framework | Control |
|---|---|
| CIS Controls | 3.1, 3.2 |
| NIST 800-53 | SC-8, SC-28 |
Description
Configure Google Workspace DLP rules to detect and prevent sharing of sensitive information like credit cards, SSNs, and confidential documents.
Prerequisites
- Google Workspace Enterprise Standard or Plus
- Defined sensitive data types for your organization
ClickOps Implementation
Step 1: Access DLP Settings
- Navigate to: Admin Console → Security → Access and data control → Data protection
- Click Manage Rules
Step 2: Create DLP Rule
- Click Create rule
- Configure:
- Name: Block sharing of PII
- Scope: Entire organization or specific OUs
- Apps: Drive, Chat
- Conditions: Content matches predefined detectors (SSN, Credit Card, etc.)
- Actions: Block external sharing, warn user, alert admin
- Save and enable rule
Code Implementation
Code Pack: Terraform
# Google Workspace DLP uses the Cloud DLP API to detect sensitive data
# in Drive, Chat, and other Workspace services. DLP requires Google
# Workspace Enterprise Standard or Plus.
#
# This creates a Cloud DLP inspect template with common PII detectors
# and a job trigger for scheduled scanning. The actual Workspace DLP
# rules (block sharing, warn user) must be configured in:
# Admin Console > Security > Data protection > Manage Rules
# DLP inspect template with common sensitive data detectors
resource "google_data_loss_prevention_inspect_template" "workspace_pii" {
count = var.profile_level >= 2 && var.gcp_project_id != "" ? 1 : 0
parent = "projects/${var.gcp_project_id}"
display_name = "HTH Workspace PII Template"
description = "HTH 4.2 -- Detects PII (SSN, credit cards, email addresses) in Workspace content"
inspect_config {
info_types {
name = "US_SOCIAL_SECURITY_NUMBER"
}
info_types {
name = "CREDIT_CARD_NUMBER"
}
info_types {
name = "EMAIL_ADDRESS"
}
info_types {
name = "PHONE_NUMBER"
}
info_types {
name = "US_PASSPORT"
}
info_types {
name = "US_DRIVERS_LICENSE_NUMBER"
}
min_likelihood = "LIKELY"
limits {
max_findings_per_request = 100
}
}
}
# L3: Extended DLP template with financial and healthcare data types
resource "google_data_loss_prevention_inspect_template" "workspace_regulated" {
count = var.profile_level >= 3 && var.gcp_project_id != "" ? 1 : 0
parent = "projects/${var.gcp_project_id}"
display_name = "HTH Workspace Regulated Data Template"
description = "HTH 4.2 L3 -- Detects regulated data (HIPAA, PCI) in Workspace content"
inspect_config {
info_types {
name = "US_SOCIAL_SECURITY_NUMBER"
}
info_types {
name = "CREDIT_CARD_NUMBER"
}
info_types {
name = "CREDIT_CARD_TRACK_NUMBER"
}
info_types {
name = "US_BANK_ROUTING_MICR"
}
info_types {
name = "US_DEA_NUMBER"
}
info_types {
name = "US_HEALTHCARE_NPI"
}
info_types {
name = "IBAN_CODE"
}
info_types {
name = "SWIFT_CODE"
}
info_types {
name = "US_INDIVIDUAL_TAXPAYER_IDENTIFICATION_NUMBER"
}
min_likelihood = "POSSIBLE"
limits {
max_findings_per_request = 500
}
}
}
# Group for DLP incident notifications
resource "googleworkspace_group" "dlp_incidents" {
count = var.profile_level >= 2 ? 1 : 0
email = "dlp-incidents@${var.primary_domain}"
name = "DLP Incidents"
description = "HTH 4.2 -- Receives notifications when DLP rules are triggered"
}
5. Monitoring & Detection
5.1 Enable Audit Logging and Investigation Tool
Profile Level: L1 (Baseline)
| Framework | Control |
|---|---|
| CIS Controls | 8.2 |
| NIST 800-53 | AU-2, AU-3, AU-6 |
| CIS Google Workspace | 5.1 |
Description
Enable and configure audit logging across all Google Workspace services. Use the Security Investigation Tool for threat detection and incident response.
Rationale
Why This Matters:
- Audit logs are essential for incident investigation
- Provides visibility into admin actions, file access, and sign-in events
- Required for compliance with most security frameworks
ClickOps Implementation
Step 1: Verify Audit Logging
- Navigate to: Admin Console → Reporting → Audit and investigation
- Verify logs are being captured for:
- Admin activities
- Login activities
- Drive activities
- Token activities
- Rules activities
Step 2: Configure Audit Log Exports
- Navigate to: Reporting → Audit and investigation → Export to BigQuery
- Enable export to BigQuery for long-term retention
- Configure retention period
Step 3: Create Alert Rules
- Navigate to: Security → Alert center → Configure alerts
- Enable critical alerts:
- Suspicious login
- Government-backed attack
- Device compromised
- Super Admin added
Time to Complete: ~30 minutes
Key Events to Monitor
| Event | Log Source | Detection Use Case |
|---|---|---|
CHANGE_PASSWORD |
Admin | Unauthorized password resets |
GRANT_ADMIN_ROLE |
Admin | Privilege escalation |
CREATE_APPLICATION_SETTING |
Admin | OAuth app approval |
LOGIN_FAILURE |
Login | Brute force attempts |
SUSPICIOUS_LOGIN |
Login | Account compromise |
DOWNLOAD |
Drive | Data exfiltration |
Code Implementation
Code Pack: Terraform
# Google Workspace audit logs are enabled by default. This control
# ensures long-term retention via BigQuery export, creates alert
# notification groups, and sets up the GCP infrastructure for log
# analysis.
# BigQuery dataset for long-term audit log retention
resource "google_bigquery_dataset" "audit_logs" {
count = var.gcp_project_id != "" ? 1 : 0
dataset_id = var.bigquery_dataset_id
project = var.gcp_project_id
friendly_name = "Google Workspace Audit Logs"
description = "HTH 5.1 -- Long-term retention of Google Workspace audit logs"
location = "US"
default_table_expiration_ms = var.log_retention_days * 24 * 60 * 60 * 1000
labels = {
purpose = "security-audit"
managed_by = "terraform"
hth = "5-01"
}
}
# IAM binding to allow Workspace to write logs to BigQuery.
# The Workspace service account must have BigQuery Data Editor access.
resource "google_bigquery_dataset_iam_member" "workspace_writer" {
count = var.gcp_project_id != "" ? 1 : 0
dataset_id = google_bigquery_dataset.audit_logs[0].dataset_id
project = var.gcp_project_id
role = "roles/bigquery.dataEditor"
member = "serviceAccount:${var.service_account_email}"
}
# Group for security alert notifications
resource "googleworkspace_group" "security_alerts" {
email = "security-alerts@${var.primary_domain}"
name = "Security Alerts"
description = "HTH 5.1 -- Receives Google Workspace security alert notifications (suspicious login, gov attack, device compromise)"
}
# Group for admin audit notifications
resource "googleworkspace_group" "admin_audit" {
email = "admin-audit@${var.primary_domain}"
name = "Admin Audit Notifications"
description = "HTH 5.1 -- Receives notifications for admin actions (role changes, policy modifications)"
}
# L2: Create a dedicated service account for log analysis with read-only access
resource "google_bigquery_dataset_iam_member" "analyst_reader" {
count = var.profile_level >= 2 && var.gcp_project_id != "" ? 1 : 0
dataset_id = google_bigquery_dataset.audit_logs[0].dataset_id
project = var.gcp_project_id
role = "roles/bigquery.dataViewer"
member = "group:security-alerts@${var.primary_domain}"
}
# L2: BigQuery view for failed login attempts (pre-built detection query)
resource "google_bigquery_table" "failed_logins" {
count = var.profile_level >= 2 && var.gcp_project_id != "" ? 1 : 0
dataset_id = google_bigquery_dataset.audit_logs[0].dataset_id
project = var.gcp_project_id
table_id = "vw_failed_logins"
view {
query = <<-SQL
SELECT
actor.email,
COUNT(*) as failed_attempts,
ARRAY_AGG(DISTINCT ip_address IGNORE NULLS) as source_ips
FROM `${var.gcp_project_id}.${var.bigquery_dataset_id}.login_logs`
WHERE event_name = 'login_failure'
AND _PARTITIONTIME >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY)
GROUP BY actor.email
HAVING failed_attempts > 10
ORDER BY failed_attempts DESC
SQL
use_legacy_sql = false
}
labels = {
purpose = "security-detection"
managed_by = "terraform"
hth = "5-01"
}
}
# L2: BigQuery view for external file sharing activity
resource "google_bigquery_table" "external_sharing" {
count = var.profile_level >= 2 && var.gcp_project_id != "" ? 1 : 0
dataset_id = google_bigquery_dataset.audit_logs[0].dataset_id
project = var.gcp_project_id
table_id = "vw_external_sharing"
view {
query = <<-SQL
SELECT
actor.email,
doc_title,
target_user,
event_time
FROM `${var.gcp_project_id}.${var.bigquery_dataset_id}.drive_logs`
WHERE event_name = 'change_user_access'
AND target_user NOT LIKE '%@${var.primary_domain}'
AND _PARTITIONTIME >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 24 HOUR)
ORDER BY event_time DESC
SQL
use_legacy_sql = false
}
labels = {
purpose = "security-detection"
managed_by = "terraform"
hth = "5-01"
}
}
Code Pack: CLI Script
# Generate login report
gam report login start -7d end today
# Generate admin audit report
gam report admin start -7d end today
# Export Drive audit events
gam report drive start -7d end today event download
# Find suspicious logins
gam report login filter "is_suspicious==True"
Code Pack: DB Query
-- Find failed login attempts by user
SELECT
actor.email,
COUNT(*) as failed_attempts
FROM `project.dataset.login_logs`
WHERE event_name = 'login_failure'
AND _PARTITIONTIME >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY)
GROUP BY actor.email
HAVING failed_attempts > 10
ORDER BY failed_attempts DESC;
-- Find external file sharing
SELECT
actor.email,
doc_title,
target_user
FROM `project.dataset.drive_logs`
WHERE event_name = 'change_user_access'
AND target_user NOT LIKE '%@yourdomain.com'
AND _PARTITIONTIME >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 24 HOUR);
6. Third-Party Integration Security
6.1 Integration Risk Assessment Matrix
| Risk Factor | Low | Medium | High |
|---|---|---|---|
| Data Access | Read-only, limited scope | Read most data | Write access, full API |
| OAuth Scopes | Specific scopes | Broad API access | Full admin/Gmail access |
| Session Duration | Short-lived tokens | Refresh tokens | Offline access |
| Vendor Security | SOC 2 Type II + ISO | SOC 2 Type I | No certification |
6.2 Common Integrations and Recommended Controls
Obsidian Security
Data Access: Read (Gmail metadata, Drive metadata, audit logs) Recommended Controls:
- ✅ Use dedicated service account
- ✅ Grant minimum required OAuth scopes
- ✅ Review access quarterly
- ✅ Monitor API usage via Reports
Slack
Data Access: Medium (Google Calendar, Drive file links) Recommended Controls:
- ✅ Approve specific scopes only
- ✅ Limit to approved workspaces
- ✅ Monitor for unusual activity
7. Compliance Quick Reference
SOC 2 Trust Services Criteria Mapping
| Control ID | Google Workspace Control | Guide Section |
|---|---|---|
| CC6.1 | MFA for all users | 1.1 |
| CC6.1 | OAuth app controls | 3.1 |
| CC6.2 | Super Admin restrictions | 1.2 |
| CC6.6 | External sharing restrictions | 4.1 |
| CC7.2 | Audit logging | 5.1 |
NIST 800-53 Rev 5 Mapping
| Control | Google Workspace Control | Guide Section |
|---|---|---|
| IA-2(1) | MFA enforcement | 1.1 |
| AC-6(1) | Least privilege admin | 1.2 |
| AC-3 | OAuth app control | 3.1 |
| AU-2 | Audit logging | 5.1 |
CIS Google Workspace Foundations Benchmark Mapping
| Recommendation | Google Workspace Control | Guide Section |
|---|---|---|
| 1.1 | Ensure 2SV is enforced | 1.1 |
| 1.2 | Limit Super Admin accounts | 1.2 |
| 2.1 | Control third-party apps | 3.1 |
| 3.1 | Restrict external sharing | 4.1 |
Appendix A: Edition/Tier Compatibility
| Control | Business Starter | Business Standard | Business Plus | Enterprise Standard | Enterprise Plus |
|---|---|---|---|---|---|
| 2-Step Verification | ✅ | ✅ | ✅ | ✅ | ✅ |
| Security Keys enforcement | ✅ | ✅ | ✅ | ✅ | ✅ |
| OAuth app whitelisting | ❌ | ✅ | ✅ | ✅ | ✅ |
| Context-Aware Access | ❌ | ❌ | ✅ | ✅ | ✅ |
| Data Loss Prevention | ❌ | ❌ | ❌ | ❌ | ✅ |
| Security Investigation Tool | ❌ | ❌ | ❌ | ✅ | ✅ |
| BigQuery export | ❌ | ❌ | ❌ | ✅ | ✅ |
Appendix B: References
Official Google Documentation:
- Google Workspace Admin Help
- Security Best Practices
- Google Cloud MFA Requirement
- Data Protection and Compliance
- Compliance Reports Manager
API & Developer Tools:
Compliance Frameworks:
- SOC 2 Type II, SOC 3, ISO/IEC 27001:2022, ISO 27017, ISO 27018, ISO 27701, FedRAMP (High), BSI C5, MTCS – via Compliance Reports Manager
- ISO/IEC 27001 Compliance
- SOC 2 Compliance
Security Incidents:
- Google Workspace has not had a major platform-level breach. Notable ecosystem incidents include the Google Docs OAuth Phishing Attack (2017), where a fake “Google Docs” app tricked users into granting email access via OAuth consent.
Third-Party Security Guides:
Changelog
| Date | Version | Maturity | Changes | Author |
|---|---|---|---|---|
| 2025-02-05 | 0.1.0 | draft | Initial guide with authentication, OAuth, data security, and monitoring controls | Claude Code (Opus 4.5) |
Contributing
Found an issue or want to improve this guide?
- Report outdated information: Open an issue with tag
content-outdated - Propose new controls: Open an issue with tag
new-control - Submit improvements: See Contributing Guide