Githubのプライベートリポジトリのissueやプルリクに添付した画像はどこまで公開されるのか？

## 結論だけ気になる方向けまとめ (前書き)
プライベートなリポジトリのissueやプルリクのコメント欄にドラッグ&ドロップした画像の振る舞いについては下記のようでした。
 
1. プライベートリポジトリに添付した画像は、画像のリンク(JWT込み)を取得して以降５分間なら誰でも見ることができる(全公開)
    - 画像の一時的なパスと、X-Amz-Signatureが複数ありそうなので、推測で当てるのは比較的難しそう。

2. issueやプルリクのコメント欄などに、画像ファイルをドラッグ&ドロップした時にマークダウン形式で自動で作成されるリンクは、その画像を貼った本人しか開けない。
    - 本人はGithubに認証済みな状態でそれを開くと画像のリンクにリダイレクトされる。

これらを踏まえると、今でもうっかりJWT付きのリンクをコピペしてしまうリスクがあるので、
Privateなリポジトリであっても、"無闇に" 個人情報など機微情報を含んだ画像は
アップロードしないのが望ましいのではないかと思います。

また、個人で添付した画像のリンクを管理する場合は、画像ファイルをドラッグ&ドロップした時にマークダウン形式で自動で作成されるリンクの方は有効期限を持っていないリンクになりますので、こちらを管理しておくのが望ましいのではないかと思います。


## 背景
5月9日以前までのGithubではプライベートリポジトリにそのまま画像をドラッグ＆ドロップした場合は、Github内のCamoで管理され[匿名化されたURL](https://docs.github.com/ja/authentication/keeping-your-account-and-data-secure/about-anonymized-urls)(少し長くてわかりづらい)が吐き出される形でした。

しかし、このURL下記の公式ドキュメントの引用部にもあるように、URLは匿名化はされていましたがURLさえわかってしまえば誰でもアクセスできるという問題がありました。

> 匿名化された URL を受け取ったユーザーは、直接であれ、間接であれ、画像や動画を見ることができます。 機密のメディア ファイルを非公開のままにしておくには、Camo を使用するのではなく、認証が必要なプライベート ネットワークまたはサーバーに制限します。

しかしプライベートネットワークの画像については、下記の記載もあるように表示できないという別の問題が発生してしまいます。

> プライベートネットワークでの画像の表示
> 画像がプライベートネットワークや、認証を要求するサーバから提供されている場合、GitHubでは表示できません。 実際のところ、その画像はユーザにサーバへのログインを求めなければ表示されません。
> この問題を修正するには、その画像をパブリックにアクセスできるサービスに移してください。

## 2023/05/09の仕様変更
この絶妙に使いづらい状況があったのですが、5月の9日 Githubから下記のような[お知らせ](https://github.blog/changelog/2023-05-09-more-secure-private-attachments/)により大きく改善されました。

> Previously, all attached (drag-and-dropped) images and videos on GitHub Issues, Pull Requests, Discussions, and wikis were available to view without authentication if you knew their direct URL. Now, future attachments associated with private repositories can only be viewed after logging in. This doesn’t apply retroactively to existing attachments, which are obfuscated by having a long, unguessable URL.

DeepLで意訳すると下記になります

- これまで、GitHubのIssues、Pull Requests、Discussions、Wikiに添付された（ドラッグ＆ドロップした）画像や動画は、直接のURLを知っていれば、認証なしで閲覧することが可能でした。
- 現在、プライベートリポジトリに関連する将来の添付ファイルは、ログイン後にのみ閲覧できるようになっています。
- これは既存の添付ファイルには遡及して適用されず、推測不可能な長いURLを持つことで難読化されています。

## 実際に試してみる

### 1. Privateなリポジトリの作成
ではまずは Github でプライベートなリポジトリを作成してみます。
今回は下記のようなリポジトリを作ってみました。

|フィールド|値|
|:-----|:--|
|リポジトリ名|https://github.com/shigimahayato/test|
|visibility|private|

### 2. issueの作成 
次にissueを作成します。

![スクリーンショット 2023-06-07 19.32.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/210923/809c80c6-4f20-a2ed-b21a-67856363048f.png)

### 3. issueのコメント欄へ画像の添付
実際に画像を貼ってみます。今回貼る画像は下記の画像とします。

![hoge.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/210923/501edc1d-08e6-5dcb-f4f7-fc0a626de63d.png)

コメント欄へ画像をドラッグ&ドロップしてみたところ、下記のような文字列が生成されました。
URL自体のリンクは `https://github.com/shigimahayato/test/assets/135826791/8861bc58-74ff-4c7e-8a80-dcfbb6b8adb0` になっていそうです。
```
![hoge](https://github.com/shigimahayato/test/assets/135826791/8861bc58-74ff-4c7e-8a80-dcfbb6b8adb0)
```

以前の物と比較すると下記のような違いがあります。

|フィールド|値|例|
|:-----|:--|:--|
|5/9以降の画像|https://github.com/#{ユーザー名}/private-repository/assets/#{ユーザー固有のID}/#{ファイルのID}|https://github.com/shigimahayato/test/assets/135826791/8861bc58-74ff-4c7e-8a80-dcfbb6b8adb0|
|5/9以前の画像|https://user-images.githubusercontent.com/#{ユーザー固有のID}/#{ファイルのID}.png|https://user-images.githubusercontent.com/135826791/103759757-8e04fb00-5057-11eb-9449-b7beb522a69d.png <br>※一例のため表示不可|

### 4. 画像のマークダウンに記載のURLの動きの確認
では現在の `https://github.com~` の情報から検証していきます。
まずは下記のようにissueに添付した画像をクリックしてみます。

![スクリーンショット 2023-06-07 19.35.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/210923/0b244f90-41e7-5002-75aa-8a5a61f4e715.png)

すると下記のような画像を表示する画面に切り替わり、URLも下記の値になっていました。issueのページに戻り、画像を右クリックして画像のリンクを取得しても下記と同じようなリンクが得られました。

|URL|
|:------|
|https://private-user-images.githubusercontent.com/135826791/244019865-8861bc58-74ff-4c7e-8a80-dcfbb6b8adb0.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJrZXkxIiwiZXhwIjoxNjg2MTM0MzUxLCJuYmYiOjE2ODYxMzQwNTEsInBhdGgiOiIvMTM1ODI2NzkxLzI0NDAxOTg2NS04ODYxYmM1OC03NGZmLTRjN2UtOGE4MC1kY2ZiYjZiOGFkYjAucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQUlXTkpZQVg0Q1NWRUg1M0ElMkYyMDIzMDYwNyUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyMzA2MDdUMTAzNDExWiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9MzU0NzkwZWE4ZDgzNTk3NTAzMTg2NDlhMjFjNzYxNDViMzI4ZjY2YjliYWMwMjkzYzhhNDZkOTNlMmIzNGY0YiZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QifQ.TxPOL7a96Pcry4WKlkKgU6MdQiAP92BkCR9UEkotWmI|


では画像のマークダウンに記載のURLである、`https://github.com/shigimahayato/test/assets/135826791/8861bc58-74ff-4c7e-8a80-dcfbb6b8adb0` をブラウザのURL欄に直接打ち込んでみます。

すると下記のように画像をクリックした時のURLと同じ場所にstatus code 302 でリダイレクトしていることがわかります。

![スクリーンショット 2023-06-07 20.07.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/210923/57265036-3e5c-54c6-15de-c153b40fdf5c.png)

※ 一部値は伏せています。

### 5. リンクに含まれていたJWTにはどのような情報が載っているか
では先程の `https://private-user-images.githubusercontent.com/135826791/244019865-8861bc58-74ff-4c7e-8a80-dcfbb6b8adb0.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJrZXkxIiwiZXhwIjoxNjg2MTM0MzUxLCJuYmYiOjE2ODYxMzQwNTEsInBhdGgiOiIvMTM1ODI2NzkxLzI0NDAxOTg2NS04ODYxYmM1OC03NGZmLTRjN2UtOGE4MC1kY2ZiYjZiOGFkYjAucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQUlXTkpZQVg0Q1NWRUg1M0ElMkYyMDIzMDYwNyUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyMzA2MDdUMTAzNDExWiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9MzU0NzkwZWE4ZDgzNTk3NTAzMTg2NDlhMjFjNzYxNDViMzI4ZjY2YjliYWMwMjkzYzhhNDZkOTNlMmIzNGY0YiZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QifQ.TxPOL7a96Pcry4WKlkKgU6MdQiAP92BkCR9UEkotWmI` というリダイレクト後のリンクのJWTを [jwt.io](https://jwt.io/) を用いて確認していきます。

解いてみると下記のような値になっていることがわかりました。

HEADER
```
{
  "alg": "HS256",
  "typ": "JWT"
}
```

PAYLOAD
```
{
  "key": "key1",
  "exp": 1686134351,
  "nbf": 1686134051,
  "path": "/135826791/244019865-8861bc58-74ff-4c7e-8a80-dcfbb6b8adb0.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIWNJYAX4CSVEH53A%2F20230607%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20230607T103411Z&X-Amz-Expires=300&X-Amz-Signature=354790ea8d8359750318649a21c76145b328f66b9bac0293c8a46d93e2b34f4b&X-Amz-SignedHeaders=host"
}
```
※ 一部値は伏せています。

これを見るとpathに含まれている、 X-Amz-Expires の値が300であるということと、expクレームとnbfクレームの指すUNIXタイムスタンプの差が丁度５分なのもあり、有効期限は５分で切れるように見えます。

|フィールド|値(UNIXタイムスタンプ)|日時(東京)|
|:------|:-----------------|:--------|
|exp|1686134351|2023/06/07 19:39:11|
|nbf|1686134051|2023/06/07 19:34:11|

### 6. 各種URLは誰が見ることができるか
ではそれぞれのユーザーで各URLが見えるかどうかを判定していきます。

対象のURLは下記の2つとします。
1. ドラッグドロップ時のマークダウンに記載される URL "https://github.com/hogehoge/private-repository/assets/(以下略)" ※以降　"画像のマークダウンに記載のURL" と表記
2. リダイレクト後のURL "https://private-user-images.githubusercontent.com/(以下略)" ※ 以降 "リダイレクト後のURL" と表記

|対象|この記事内での略称|URL|
|:-------------|:---|:--|
|ドラッグドロップ時のマークダウンに記載される URL|画像のマークダウンに記載のURL|https://github.com/hogehoge/private-repository/assets/(以下略)|
|リダイレクト後のURL|リダイレクト後のURL|https://private-user-images.githubusercontent.com/(以下略)|

また、その他の条件として、リダイレクト後のURLは５分でexpireが切れそうなので６分後に、どのように表示されるかを見てみようかと思っています。

それぞれの条件をまとめると下記になります。

|ユーザー|URL|その他条件|見れるかどうか|
|:-----|:---|:-------|:----|
|画像を貼ったユーザー|画像のマークダウンに記載のURL|特になし|◯|
|画像を貼ったユーザー|リダイレクト後のURL|特になし|◯|
|画像を貼ったユーザー|リダイレクト後のURL|URL取得の5分後|☓|
|無関係のGithubユーザー|画像のマークダウンに記載のURL|特になし|☓|
|無関係のGithubユーザー|リダイレクト後のURL|特になし|◯|
|無関係のGithubユーザー|リダイレクト後のURL|URL取得の５分後|☓|
|ユーザーなし(github未ログイン)|画像のマークダウンに記載のURL|特になし|☓|
|ユーザーなし(github未ログイン)|リダイレクト後のURL|特になし|◯|
|ユーザーなし(github未ログイン)|リダイレクト後のURL|URL取得の5分後|☓|

見れない場合は下記のように、HTTP Status code 404が返ってきているようでした。
![スクリーンショット 2023-06-08 15.48.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/210923/aa6e3b98-b4c5-76f8-b913-b44810efb9da.png)

## まとめ
プライベートなリポジトリのissueやプルリクのコメント欄にドラッグ&ドロップした画像の振る舞いについては下記のようでした。

1. プライベートリポジトリに添付した画像は、画像のリンク(JWT込み)を取得して以降５分間なら誰でも見ることができる(全公開)
    - 画像の一時的なパスと、X-Amz-Signatureが複数ありそうなので、推測で当てるのは比較的難しそう。

2. issueやプルリクのコメント欄などに、画像ファイルをドラッグ&ドロップした時にマークダウン形式で自動で作成されるリンクは、その画像を貼った本人しか開けない。
    - 本人はGithubに認証済みな状態でそれを開くと画像のリンクにリダイレクトされる。

これらを踏まえると、今でもうっかりJWT付きのリンクをコピペしてしまうリスクがあるので、
Privateなリポジトリであっても、"無闇に" 個人情報など機微情報を含んだ画像は
アップロードしないのが望ましいのではないかと思います。

また、個人で添付した画像のリンクを管理する場合は、画像ファイルをドラッグ&ドロップした時にマークダウン形式で自動で作成されるリンクの方は有効期限を持っていないリンクになりますので、こちらを管理しておくのが望ましいのではないかと思います。