0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【GCS】WebアプリでCloudStorageの画像を安全に表示する

Posted at

やりたいこと

GoogleCloudStorageにある画像を自Webアプリで表示する。
セキュリティ的にインターネット上で公開する際は"自アプリを通してのみ"見られるようにしたい。

構成と前提

自Webアプリ フロントエンド

  • FireBaseでホスティングにて公開済み
  • Rect × TypeScript

自Webアプリ バックエンド

  • herokuで公開済み
  • Go(gin)

ファイルストレージ

  • herokuはファイルを置けないのでGoogleCloudStorageで管理している
  • GCPのプロジェクト諸々は設定済み
  • Googleのサービスアカウント作成済み

対応策

  • 署名付きURLを使ってインターネットに公開する
  • 署名付きURLの生成にはGoogleのサービスアカウントのプリンシパルを使用する

Google Cloud Storage にある画像を認証済みURLを使って Web アプリで表示する際に、Google のサービスアカウントのプリンシパルを適用する。
そのためには、署名付きURL(Signed URL)を使用する。
署名付きURLを作成することで、指定した期間内に限り、特定のリソース(画像など)に対してアクセス権を付与できる。
このURLはサービスアカウントの認証を基に生成され、他のユーザーもそのURLを使ってリソースにアクセスできます。

署名付きURLの利点

  • 認証済みユーザーだけが、限られた期間内にリソースにアクセスでききる
  • バケットやオブジェクトに対するグローバルなアクセス権を変更する必要がなく、セキュリティが向上する
  • サービスアカウントを使ってバックエンド側で署名付きURLを生成するため、ユーザーが直接認証情報を扱う必要がない(これ嬉しい)

注意点

  • 署名付きURLは、生成後の有効期間が過ぎるとアクセスできなくなる。
    • ユーザーが直接ブラウザのブックマークに入れてしまわないような構成にしてあげる配慮がいる
  • 秘密鍵の管理は慎重に行う。
    • 署名付きURLに限った話ではない。(鍵だけに)
    • サービスアカウントの認証JSONファイルは安全な場所に保管する必要がある
    • herokuでのデプロイの際には工夫が必要。

やり方

署名付きURLをGoで生成する

署名付きURLを生成するためのコードをバックエンドに追加します。storage.SignedURL関数を使用してURLを生成します。
Goバックエンド

package main

import (
    "context"
    "fmt"
    "log"
    "time"
    "cloud.google.com/go/storage"
    "google.golang.org/api/option"
)

func generateSignedURL(bucketName, objectName, credentialFile string) (string, error) {
    ctx := context.Background()
    
    // ストレージクライアントを作成
    client, err := storage.NewClient(ctx, option.WithCredentialsFile(credentialFile))
    if err != nil {
        return "", fmt.Errorf("storage.NewClient: %v", err)
    }
    defer client.Close()

    // 署名付きURLのオプションを指定
    opts := &storage.SignedURLOptions{
        GoogleAccessID: "[YOUR_SERVICE_ACCOUNT]@your-project-id.iam.gserviceaccount.com", // サービスアカウントのメールアドレス
        PrivateKey:     []byte("[YOUR_PRIVATE_KEY]"), // サービスアカウントの秘密鍵
        Method:         "GET",
        Expires:        time.Now().Add(15 * time.Minute), // URLの有効期限
    }

    // 署名付きURLの生成
    url, err := storage.SignedURL(bucketName, objectName, opts)
    if err != nil {
        return "", fmt.Errorf("storage.SignedURL: %v", err)
    }

    return url, nil
}

func main() {
    bucketName := "your-bucket-name"
    objectName := "your-object-name"
    credentialFile := "path/to/your-service-account.json"

    signedURL, err := generateSignedURL(bucketName, objectName, credentialFile)
    if err != nil {
        log.Fatalf("Failed to generate signed URL: %v", err)
    }

    fmt.Printf("Signed URL: %s\n", signedURL)
}

  • GoogleAccessID: 署名付きURLを生成するために使用するサービスアカウントのメールアドレス。
  • PrivateKey: サービスアカウントの秘密鍵。この鍵は、サービスアカウントのJSONファイル内に含まれています。
  • Expires: 署名付きURLの有効期限を指定。ここでは15分間に設定しています。

Webアプリでの画像表示

上記のコードで生成した署名付きURLをフロントエンドに渡し、そのURLを使って画像を表示します。例えば、React で画像を表示する場合は以下のようにします。

Reactフロントエンド

import React, { useState, useEffect } from 'react';

const ImageComponent = () => {
  const [imageUrl, setImageUrl] = useState('');

  useEffect(() => {
    // バックエンドから署名付きURLを取得するリクエスト
    fetch('/api/get-signed-url')
      .then(response => response.json())
      .then(data => {
        setImageUrl(data.signedUrl);
      })
      .catch(error => {
        console.error('Error fetching signed URL:', error);
      });
  }, []);

  return (
    <div>
      {imageUrl ? <img src={imageUrl} alt="Your image" /> : <p>Loading...</p>}
    </div>
  );
};

export default ImageComponent;

  • /api/get-signed-url: バックエンド側で署名付きURLを生成し、フロントエンドに返すAPIエンドポイント。
  • imageUrl: 署名付きURLを使用して画像を表示。
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?