本篇文章更新時間:2025/01/18
如有資訊過時或語誤之處,歡迎使用 Contact 功能通知或向一介資男的 LINE 社群反應。
如果本站內容對你有幫助,歡迎贊助支持 。


Google Workspace 作為企業日常工作工具的解決方案是真的很方便。不過一但企業使用人數到達一定規模後,相關控管的需求隨之而來,真的就不能只是用了而已。

現在人都習慣直接開雲端硬碟的檔案來使用,然後分享給外部人士可能也是直接權限全開,只要有連結就看得到內容。

更不用說同個瀏覽器多個 Google 帳號登入的公私帳號混用情況下,有時候還不是用公司的信箱來處理公務。針對這個問題其實也真的不是使用者的錯,這個算是公司層級需要去顧慮的,也是目前 Chrome 也還有的問題(無法同一個瀏覽器指定哪個帳號為角色預設認定)。

總之,接手這樣可能檔案權限各種混亂的情境,第一件事就是要先想辦法全面性的調整。

Google Workspace Admin 的工具中,在 稽核與調查 中可以調到使用者對 Drive 的各種操作紀錄,第一步可以先從這邊來做觀察,與設定報告通知,在第一時間內有人打開檢視權限都可以收到通知,近一步展開調查。

那原本已經權限大開的檔案怎辦?

Admin 這邊並未開放任何可以管理或觀看到使用者信件或檔案的權限,這部分需要透過整合 App 的 全網域委派 來下手了。

前置準備

  1. 需要有 Google Workspace Admin 總管理員權限的賬號
  2. 需要開通 Google Cloud Platform(GCP) 服務與有權限能進一步開通 Drive API 與 Admin API

Google Cloud Platform 端的設定

先在 GCP 上開一個專案,然後到 API 和服務 中開啟 Google Drive API 與 Admin SDK API 這兩個。

再來到 憑證 的分頁,建立一個 服務帳戶,然後把 Workspace 管理者的帳號授予權限。

建立過程中會產生一組接下來程式會需要用到的登錄識別檔案,要好好保存不能外流。

建立完成後回到這個服務帳戶的編輯頁面,看到進階設定處點開,會發現一個 網域層級委派 的區塊,先是複製上方提供的 用戶端 ID 然後點擊查看 Workspace 管理控制台,如下圖:

gcp_screent

Google Workspace Admin 端的設定

接著來到 Admin 端介面,要先到 安全性 > API 控制項 > 全網域委派 這裡,新增 API 用戶端,把剛剛複製的 用戶端 ID 貼上去,並把下方的權限授權給這個用戶:

https://www.googleapis.com/auth/drive
https://www.googleapis.com/auth/drive.metadata.readonly
https://www.googleapis.com/auth/admin.directory.user.readonly

到這邊就完成接下來開發上需要的存取控制權限了~

撈取全公司 Workspace 使用者的方法

這邊我使用 PHP 開發,需要先行安裝 [Google API Client](composer require google/apiclient) 的函式庫

composer require google/apiclient

然後取的全部使用者的程式碼片段如下:

<?php
require dirname(__FILE__) . '/vendor/autoload.php';

use Google\Client;
use Google\Service\Directory;
use Google\Service\Drive;

// 服務帳戶金鑰檔案的路徑
$service_account_file = dirname(__FILE__) . '/path/to/service/account/file.json';

$client = new Client();
$client->setSubject('[email protected]'); // 替代此用戶進行操作
$client->setAuthConfig($service_account_file); // 設定服務帳戶金鑰
$client->addScope([Google\Service\Drive::DRIVE, Google\Service\Drive::DRIVE_METADATA_READONLY, Directory::ADMIN_DIRECTORY_USER_READONLY]);
$directoryService = new Directory($client);

$users_emails = [];
$pageToken = null;

do {
    // 呼叫 API 取得用戶清單
    $response = $directoryService->users->listUsers([
        'domain' => 'domain.com', // 替換成你的 Google Workspace 域名
        'pageToken' => $pageToken,
        'maxResults' => 100, // 請求最多也就 100 筆
        'fields' => 'nextPageToken, users(primaryEmail)',
    ]);

    foreach ($response->getUsers() as $user) {
        $users_emails[] = $user->getPrimaryEmail(); // 主電子郵件
    }
    $pageToken = $response->getNextPageToken(); // 下一頁 Token
} while ($pageToken);

print_r($users_emails);

這邊有兩個有趣的地方:

  1. setSubject 方法是一種「選擇替身」的操作。也就是你當前是用哪個帳號來做哪件事。有些時候是管理員才能做的事,你就要帶入管理員帳號。

  2. pageToken 與 nextPageToken 的關係還要靠 fields 欄位。如果回傳的欄位沒有帶入 nextPageToken,那就算有填寫 pageToken 也撈不到 nextPageToken 值!

調整每位使用者雲端硬碟檔案的檢視權限

上方程式碼片段已經取得到每位公司成員的信箱了,再來就是要透過 setSubject 替身方法,來「站在這位使用者」的角度來調整自己的檔案。

就算是使用總管理員的帳號來帶入,Google 還是不允許這帳號去接觸到沒有分享編輯權限的其他人的檔案。

所以就是需要個別將撈取到的使用者做一輪搜尋檔案+修改檔案權限的操作,程式碼片段如下:

foreach ($users_emails as $index => $owner_email) {
    $account = explode('@', $owner_email);
    $client = new Client();
    $client->setSubject($owner_email); // 代替此用戶進行操作
    $client->setAuthConfig($serviceAccountFile); // 設定服務帳戶金鑰
    $client->addScope([Google\Service\Drive::DRIVE, Google\Service\Drive::DRIVE_METADATA_READONLY]);
    // 初始化 Google Drive 服務
    $drive_service = new Drive($client);
    $files = [];
    $pageToken = null;
    do {
        // 呼叫 Google Drive API 列出檔案
        $results = $drive_service->files->listFiles([
            'q' => "(visibility = 'anyoneWithLink' OR visibility='anyoneCanFind') AND 'me' in owners",
            'fields' => 'nextPageToken, files(id, name, mimeType, owners, permissions, modifiedTime)',
            'pageSize' => 100,
            'pageToken' => $pageToken,
        ]);

        foreach ($results->getFiles() as $file) {

            $permissions = $file->getPermissions();
            $permissionDetails = [];

            if ($permissions) {
                foreach ($permissions as $permission) {
                    $permissionDetails[] = [
                        'type' => $permission->getType(),
                        'role' => $permission->getRole(),
                        'emailAddress' => $permission->getEmailAddress() ?? 'N/A',
                        'domain' => $permission->getDomain() ?? 'N/A',
                    ];
                }
            }
            $mimeType = $file->getMimeType();
            $tmp = explode('.', $mimeType);
            $mimeType_patrs = end($tmp);
            $type = 'file';
            if ($mimeType_patrs == 'folder') {
                $type = 'folder';
            }
            $owner = $file->getOwners()[0]->getEmailAddress();
            $log_file = [
                'name' => $file->getName(),
                'id' => $file->getId(),
                'mimeType' => $mimeType,
                'url' => "https://drive.google.com/{$type}/d/{$file->getId()}",
                'owner' => $owner,
                'modifiedTime' => $file->getModifiedTime(),
                'permissions' => $permissionDetails,
            ];

            if (strpos(strtolower($owner), strtolower($owner_email)) !== false) {
                // 更新檔案權限
                // $permissions = $drive_service->permissions->listPermissions($log_file['id'])->getPermissions();
                foreach ($permissions as $permission) {
                    // 如果是公開共享,將其權限刪除
                    if ($permission->getType() === 'anyone') {
                        try {
                            $drive_service->permissions->delete($log_file['id'], $permission->getId());
                            echo "移除公開權限檔案: " . $log_file['name'] . PHP_EOL;
                            // 加新的權限(僅限組織內查看)
                            $newPermission = new Google\Service\Drive\Permission([
                                'type' => 'domain', // 組織內
                                'role' => 'reader', // 只讀
                                'domain' => 'domain.com', // 替換為組織域名
                            ]);
                            try {
                                $drive_service->permissions->create($log_file['id'], $newPermission, [
                                    'sendNotificationEmail' => false, // 禁止發送通知
                                ]);

                                echo "已更新權限檔案: " . $log_file['name'] . PHP_EOL;
                                $files[] = $log_file;
                                file_put_contents(dirname(__FILE__) . '/' . $account[0] . '_modify.log', $log_file['name'] . '|' . $log_file['url'] . '|' . json_encode($permissions) . PHP_EOL, FILE_APPEND | LOCK_EX);
                            } catch (Exception $e) {
                                echo '更新權限失敗:' . $log_file['name'] . ' / ' . $e->getMessage();
                            }

                        } catch (Exception $e) {
                            echo '移除權限失敗: ' . $log_file['name'] . ' / ' . $e->getMessage();
                        }
                    }
                }
            }
        }
        $pageToken = $results->getNextPageToken();
    } while ($pageToken !== null);
    if (!empty($files)) {
        file_put_contents(dirname(__FILE__) . '/' . $account[0] . '_modify_' . date('Y-m-d-H-i-s', time()) . '.json', json_encode($files), FILE_APPEND | LOCK_EX);
    }
}

上述程式操作的重點是:

  1. 撈取當前檢視權限是「公開」的(不限制類型)
  2. 針對這樣的檔案去移除公開權限,並補上「限定組織內部觀看」。

跑完一圈就能全部調整好權限了~

後記

前言有提到可以先設定好稽核的報告,將日後有設定公開的檔案都發通知給指定管理員。

這個通知基本上就可以在這一波掃描修改後發會功用。確實了解到有哪些人注意到了檔案外連公開失效,又還去改變回公開權限的狀況。

針對資安的操作,這都還很淺,有專門做到這類型檢測資料外流的工具 Forcepoint,不過基本使用單位是用 100 人以上來去估,每個不同規模的公司都有不同要解決的情境呀~

至於關於 Google Workspace Admin 的管理這塊,網路上也能找到一些工具,真是哪裡有困難,哪裡就有商機XD

  • GAM 開源指令版管理工具
  • Patronum 商業版管理工具


Share:

作者: Chun

WordPress 社群貢獻者、開源社群推廣者。專注於 WordPress 外掛開發、網站效能最佳化、伺服器管理,以及 iDempiere 開源 ERP 導入與客製開發。曾參與 WordCamp Taipei 等社群活動,GitHub Arctic Code Vault Contributor。提供資訊顧問、WordPress 開發教學、主機最佳化與企業 ERP 整合服務。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *


文章
Filter
Mastodon