個人用の OneDrive で共有しているフォルダやファイルを特定する

## この記事の要約(3行)

- 個人用の OneDrive (通称 OneDrive Personal) は、簡単に共有リンクを作れます。
- 共有リンクによって、リンクを知ってれば誰でも参照・編集が可能になってしまいます。
- この記事を読むと、意図せず共有されてるファイルやフォルダを知る方法がわかります。

## 背景

Microsoft 365 Personal などをサブスクリプション契約していると、オンラインストレージとして OneDrive を 1TB 利用できます。

一方、次の記事のように、オンラインストレージは個人情報のような公開を避けたい情報であっても、リンクを知っていれば誰でも閲覧できる状態となってしまうかもしれません。

https://www.itmedia.co.jp/news/articles/2312/21/news113.html

> エイチーム、Googleドライブで設定ミス　約94万人分の情報が「リンクを知っていれば誰でも閲覧できる状態」に
> 2023年12月21日 エイチームは12月21日、7日に発表した個人情報漏えいの可能性について、詳細な調査の結果を公開した。オンラインストレージ「Googleドライブ」の情報公開設定にミスがあり、顧客や取引先、退職者など93万5779人の情報を含むファイル1369件が、リンクを知っていれば誰でも閲覧できる状態だったという。

前述の ITメディアの記事は Googleドライブの事例ですが、個人用の OneDrive も同様にリンクを知っていれば誰でも閲覧や編集できる機能があります。

OneDrive でこのリンクは、あまりに簡単に作れてしまいます。
エクスプローラーで作る場合は、フォルダやファイルを右クリックし、 `OneDrive - リンクのコピー` を選ぶ...2クリックだけで、リンクを知っていれば誰でも編集できるリンクが生成されます。

個人用の OneDrive に保管している、あんなファイルや、こんなファイルが、意図せず世の中に公開されている状態になっては困りますね。

では、個人用の OneDrive で、リンクを知っていれば誰でも閲覧できる状態かどうかを確認するにはどうすればいいでしょうか。

## 解決策

個人用 OneDrive の全てのファイルやフォルダについて、あながた共有した共有したファイルやフォルダが存在しているか確認しましょう。

### 解決策1: OneDrive の Web 画面で確認する。

あなたが共有したフォルダは https://onedrive.live.com/?qt=sharedbyme から確認できます。
上記は、 https://onedrive.live.com/ にアクセスしたのち、画面左の「共有」の中の「あなたが共有」を選択することで参照できます。
ここで確認するのがもっとも簡単です。

なお、この方法では、フォルダX を共有し、フォルダX下のファイルYがあるとき、ファイルYは表示されない動作のようです。

### 解決策2: すべてのリンクについて列挙する。

Windows PowerShell と Microsoft Graph PowerShell で確認できます。
手間がかかりますが、自動で処理する場合はこの方法がよいでしょう。

特に準備は必要ありません。
例えば、 Azure Active Directory admin center でアプリケーションを新規登録する、といった操作は不要です。

基本的には API に対する操作で確認します。

Win + R で `powershell.exe` を実行し、次のコードをコピー＆ペーストすると、アクセス許可が空ではないアイテムの一覧を確認できます。

```powershell
# (初回のみ)モジュールをインストールして、インポートする。
Install-Module Microsoft.Graph -Scope CurrentUser
Import-Module Microsoft.Graph.Users

# 接続する。
$scopes = "Files.Read","Files.Read.All"
Connect-MgGraph -Scopes $scopes

# OneDrive の Root の ID を得る。
$drive = Get-MgDrive
$driveId = $drive.Id

# OneDrive のアイテムを表現するクラスを定義する。
class DriveItem {
    [string] $Parent
    [string] $Id
    [string] $Name
    [bool] $IsFolder
    DriveItem([string]$Parent, [string]$Id, [string]$Name, [bool]$IsFolder) {
        if ($Parent -match "/$") {
            throw
        }
        $this.Parent = $Parent
        $this.Id = $Id
        $this.Name = $Name
        $this.IsFolder = $IsFolder
    }
    static [DriveItem] Create([string]$Parent, [hashtable]$ValueItem) {
        $local:id = $ValueItem["Id"]
        $local:name = $ValueItem["Name"]
        $local:isFolder = $ValueItem.ContainsKey("Folder")
        return [DriveItem]::new($Parent, $id, $name, $isFolder)
    }
    [string] GetPathRelativeToRoot() {
        # Microsoft Graph REST API v1.0 の記述に合わせ、先頭の / を含まず返す。
        $separator = if ($this.Parent.Length -gt 0) { "/" } else { "" }
        $parentSeparator = $this.Parent + $separator
        return $parentSeparator + $this.Name
    }
    [DriveItem] CreateChild([hashtable]$ValueItem) {
        $local:parent = $this.GetPathRelativeToRoot()
        return [DriveItem]::Create($parent, $ValueItem)
    }
}

$wrongPermissionDriveItems = New-Object System.Collections.Generic.List[object]
$queue = New-Object System.Collections.Generic.List[object]

# Root の子要素をキューに加える。
$apiUrl = "https://graph.microsoft.com/v1.0/drives/${driveId}/root/children"
$response = Invoke-MgGraphRequest -Uri $apiUrl
$responseValue = $response.value
foreach ($valueItem in $responseValue) {
    $parent = ""
    $driveItem = [DriveItem]::Create($parent, $valueItem)
    $queue.Add($driveItem)
}

# すべての子要素を探索し、アクセス許可が空ではないアイテムを $wrongPermissionDriveItems に格納する。
$PERCENT_COMPLETE_UNKNOWN = -1
$counter = 0
while ($queue.Count -gt 0) {
    # 進捗を出力する。
    $total = $counter + $queue.Count
    Write-Progress -Activity "Search in Progress" -Status "${counter} of ${total} Complete:" -PercentComplete $PERCENT_COMPLETE_UNKNOWN

    # キューからアイテムを得る。
    $driveItem = $queue.Item(0)
    $queue.RemoveAt(0)

    # フォルダなら子要素をキューに加える。
    if ($driveItem.IsFolder) {
        $pathRelativeToRoot = $driveItem.GetPathRelativeToRoot()
        $apiUrl = "https://graph.microsoft.com/v1.0/drives/${driveId}/root:/${pathRelativeToRoot}:/children"
        $response = Invoke-MgGraphRequest -Uri $apiUrl
        $responseValue = $response.value
        foreach ($valueItem in $responseValue) {
            $driveItem2 = $driveItem.CreateChild($valueItem)
            $queue.Add($driveItem2)
        }
    }

    # アクセス許可にエントリがあれば $wrongPermissionDriveItems に加える。
    $driveItemId = $driveItem.Id
    $permission = Get-MgDriveItemPermission -DriveId $driveId -DriveItemId $driveItemId
    if ($null -ne $permission) {
        $wrongPermissionDriveItems.Add($driveItem)
    }

    $counter += 1
}

# 切断する。
Disconnect-MgGraph

# アクセス許可が空ではないアイテムの一覧を出力する。
$wrongPermissionDriveItems | Format-List
```

OneDrive 直下に次のようなファイル構造を作って実行してみます。

```text
OneDrive
    |
    +-- test-not-shared-folder
    |       |
    |       +-- shared-file1.txt (リンク作成: 有)
    |       |
    |       `-- not-shared-file1.txt
    |
    `-- test-shared-folder (リンク作成: 有)
            |
            +-- shared-file2.txt (リンク作成: 有)
            |
            `-- not-shared-file2.txt
```

すると次のようにファイル一覧が得られました。

```console
PS > $wrongPermissionDriveItems | Format-List


Parent   :
Id       : C8774D63856369A7!5807
Name     : test-shared-folder
IsFolder : True

Parent   :
Id       : C8774D63856369A7!119
Name     : 公開
IsFolder : True

Parent   : test-not-shared-folder
Id       : C8774D63856369A7!5812
Name     : shared-file1.txt
IsFolder : False

Parent   : test-shared-folder
Id       : C8774D63856369A7!5808
Name     : not-shared-file2.txt
IsFolder : False

Parent   : test-shared-folder
Id       : C8774D63856369A7!5810
Name     : shared-file2.txt
IsFolder : False
```

期待通り、共有したフォルダや、共有したフォルダ下の非共有のファイル(この出力例では `not-shared-file2.txt`)も含めて、共有したフォルダやファイルを取得できることがわかりました。

## 参考資料

- [Overview of Microsoft Graph](https://learn.microsoft.com/en-us/graph/overview?view=graph-rest-1.0)
- [Microsoft Graph PowerShell documentation](https://learn.microsoft.com/ja-jp/powershell/microsoftgraph/?view=graph-powershell-1.0)
- [The right API to use is https://docs.microsoft.com/en-us/graph/api/driveitem-list-children?view=graph-rest-1.0&tabs=http#list-children-in-the-root-of-the-current-users-drive, which unfortunately is not currently covered by the SDK.](https://github.com/microsoftgraph/msgraph-sdk-powershell/issues/813#issuecomment-1123009518)

## 編集後記

- 子供の成長記録や家族旅行旅程など、意図せず公開したくなかった。
- 自動でチェックして、通知する方法も知りたい人は、コメントいただけると嬉しいです。別の記事を作るモチベーションになります。