LoginSignup
9
4

Web3.Storageと暗号化技術を組み合わせて、ファイルを安全に管理する

Last updated at Posted at 2023-12-18

この記事は、株式会社うるる(ULURU) Advent Calendar 2023の18日目の記事です。

はじめに

最近、Web3.Storageというサービスに触れる機会があったので、使い方やできることをまとめつつ、暗号化技術を組み合わせてファイルを安全に管理する方法を模索します。

この記事で登場するコード群

以下Githubレポジトリにて公開しています。

概要

本記事で登場するWeb3.Storage, Filecoinストレージについて説明します。

詳細まで踏み込むと長くなってしまうので、概要の説明に留めています。
より詳しくは、添付している公式のリンクなどを参照ください。

Web3.Storage とは

Web3.Storageとは、Filecoinストレージへのアクセスを容易 & シンプルにしてくれるインタフェースサービスのことです。
開発者が裏側の仕組みを意識することなく、Filecoinストレージに対してファイルのアップロード & ダウンロードなどを行うことができます。

Filecoinストレージとは

Filecoinとは、近年登場した分散型ストレージのことです。
大きな特徴として、全世界に存在するデジタルデバイスの空き容量をストレージとして利用する ということがあります。

世界中の空きデバイスにファイルを分散させる仕組みは、IPFSというP2P通信のプロトコルを用いて実現されています。
Filecoinでは、自分の持つ空きデバイス容量の一部を貸し出し、報酬を得るような経済モデルを構築したことで、空きストレージが共有され続けるような仕組みになっています。

イメージとしては、Airbnbのファイル版 といった感じです。

Web3.Storageを使ってみる

最近、大きなアップデートが入ったそうで、過去に自分が触った時点と仕様が結構変わっていました・・・:cry:
本記事ではアップデート後のWeb3.Storageで動かしてみたいので、まずはアカウント設定から、ファイルのアップロード・ダウンロードなど基本操作を試してみます。

アカウント & スペース作成

公式サイトの案内に沿って、アカウント & スペースを作成します。
UI上は、スペースに対して、ファイルをアップロード & 管理するような動きになります。
また、各スペースにはDIDという一意なIDが割り振られており、プログラムからアクセスする際などはこの値を使うことになります。

スクリーンショット-2023-12-17-14-03-05.png

ファイルをアップロードしてみる

JavaScript クライアントが提供されているので、これを使ってみます。
まずは、プログラムからアクセス可能にするための、認証用コードが必要になります。

import { create } from '@web3-storage/w3up-client';
import dotenv from 'dotenv';
dotenv.config();

const client = await create();
const account = await client.login(process.env.LOGIN_ADDRESS);
const did = process.env.DID;
await account.provision(did);

GUI上で、メールアドレスにてログイン & 対象スペースを選択 という動きをプログラムで実施しているイメージです。
DIDは、スペース作成時に表示される値を使いましょう。
試してないですが、毎回ログインするのではなく、「代表者の権限を委任する」ということもできるみたいです。参考

認証が通過したら、ファイルをアップロードするコードを記述します。

import { create } from '@web3-storage/w3up-client';
import { filesFromPaths } from 'files-from-path';
import dotenv from 'dotenv';
dotenv.config();

const client = await create();
// ... 認証コード
const file = await filesFromPaths(['sample_images/sample-image.png']);
const cid = await client.uploadFile(file[0]);
console.log('cid', cid);

コードの全体像はこんな感じ。

サンプルコード
import { create } from '@web3-storage/w3up-client';
import { filesFromPaths } from 'files-from-path';
import dotenv from 'dotenv';
dotenv.config();

async function upload()
{
  const client = await getClient();
  const file = await filesFromPaths(['sample_images/sample-image.png']);
  const cid = await client.uploadFile(file[0]);
  console.log('cid', cid);
}

async function getClient()
{
  const client = await create();
  const account = await client.login(process.env.LOGIN_ADDRESS);
  const did = process.env.DID;
  await account.provision(did);
  await client.setCurrentSpace(did);
  return client;
}

await upload()
  .catch(console.error)
  .finally(() => process.exit())

動かしてみると、Web3.Storageのスペース上にファイルがアップロードされていることが確認できるかと思います。

❯❯❯ make upload
node js/upload.js
cid CID(bafkreiciko4vlpseyuzwhz5h3t5mv5t4ejwrri35vkqpdvtgb654ux7pei)

スクリーンショット-2023-12-17-14-28-36.png

スクリーンショット-2023-12-17-14-28-51.png

CIDというのは、アップロードされたファイルのコンテンツ識別子のことです。

ファイルをブラウザ上で表示 & ダウンロードしてみる

URL箇所に表示されている、IPFSゲートウェイURLを叩くことで、ブラウザ上でファイルを表示することができます。

スクリーンショット 2023-12-17 14.37.59.png

ダウンロードについては、JSクライアントでは今時点で実装が提供されてなさそうだったので、curlコマンドなどで実施することになりそうです。

Web3.Storageと暗号化技術を組み合わせる

ここからが本題です。
Web3.Storageと暗号化技術を組み合わせることで、安全にファイルを保存する方法を模索してみます。

公式ドキュメントでも注意喚起されていますが、アップロードしたファイルについては、CID(コンテンツ識別子)を知っている人であれば誰でもアクセス可能です。
そのため、例えば機密情報を保存したい場合、閲覧可能者をコントロールする工夫が必要になります。

Public Data 🌎
All data uploaded to w3up is available to anyone who requests it using the correct CID. Do not store any private or sensitive information in an unencrypted form using w3up.

そこで今回は、PGP(Pretty Good Privacy)の仕組みを用いて、ファイルを暗号化した状態でWeb3.Storageにアップロード & 秘密鍵を保有しているユーザのみファイルを復元して閲覧可能 という動きを実装してみます。

鍵の準備

まずは、暗号化に使う公開鍵 & 秘密鍵を作成 & importします。
鍵自体は、gpgコマンドで作成しても、JavaScriptのopenpgpパケージで作成しても構いません。
JavaScriptで実施するなら以下の感じです。

サンプルコード
import * as openpgp from 'openpgp';
import fs from 'fs';

const { privateKey, publicKey } = await openpgp.generateKey({
  type: 'ecc',
  curve: 'curve25519',
  userIDs: [{
    name: process.env.GPG_USER_NAME,
    email: process.env.GPG_USER_EMAIL
  }],
  format: 'armored'
});

console.log(privateKey);
console.log(publicKey);
fs.writeFileSync('private-key.key', privateKey);

鍵作成後は、公開鍵情報はpublic_keys/publicKey1.ascに記載します。
秘密鍵情報は、復号操作を可能にしたいPC上でgpg --importコマンドを実施してimportしましょう。

ファイルを暗号化してアップロード

ファイルの暗号化にも、JavaScriptのopenpgpパケージを使います。
暗号化箇所のコードは以下です。

import openpgp from 'openpgp';
import fs from 'fs';
import dotenv from 'dotenv';
dotenv.config();

// Public keys
const publicKeysArmored = [
  fs.readFileSync(process.env.PUBLIC_KEY1_PATH, 'utf8'),
  fs.readFileSync(process.env.PUBLIC_KEY2_PATH, 'utf8'),
];
const publicKeys = await Promise.all(
  publicKeysArmored.map((armoredKey) => openpgp.readKey({ armoredKey }))
);

// File data
const fileData = fs.readFileSync('sample_images/sample-image.png');
const fileMessage = await openpgp.createMessage({ binary: fileData });

const encrypted = await openpgp.encrypt({
  message: fileMessage,
  encryptionKeys: publicKeys,
  format: "binary",
});

publicKeysArmored箇所で複数の公開鍵を指定することで、他のユーザが暗号化したファイルについても、自分の保有する秘密鍵を用いて復号することができます。

このようにすることで、「同一グループ間でのみファイルの閲覧可能」といった、グループ認証の機構が実現可能になります。

コードの全体像は以下のようになります。

サンプルコード
import { create } from '@web3-storage/w3up-client';
import { filesFromPaths } from 'files-from-path';
import openpgp from 'openpgp';
import fs from 'fs';
import dotenv from 'dotenv';
dotenv.config();

async function upload()
{
  const client = await getClient();

  // encrypt file
  const encrypted = await encryptFile();
  const tempFilePath = 'tmp-sample-image.png';
  fs.writeFileSync(tempFilePath, new Uint8Array(encrypted));

  // upload encrypted file
  const encryptedFile = await filesFromPaths([tempFilePath]);
  const cid = await client.uploadFile(encryptedFile[0]);
  console.log('cid', cid);

  // clean up tmp file
  fs.unlinkSync(tempFilePath);
}

async function getClient()
{
  const client = await create();
  const account = await client.login(process.env.LOGIN_ADDRESS);
  const did = process.env.DID;
  await account.provision(did);
  await client.setCurrentSpace(did);
  return client;
}

async function encryptFile() {
  // Public keys
  const publicKeysArmored = [
    fs.readFileSync(process.env.PUBLIC_KEY1_PATH, 'utf8'),
    fs.readFileSync(process.env.PUBLIC_KEY2_PATH, 'utf8'),
  ];
  const publicKeys = await Promise.all(
    publicKeysArmored.map((armoredKey) => openpgp.readKey({ armoredKey }))
  );

  // File data
  const fileData = fs.readFileSync('sample_images/sample-image.png');
  const fileMessage = await openpgp.createMessage({ binary: fileData });

  const encrypted = await openpgp.encrypt({
    message: fileMessage,
    encryptionKeys: publicKeys,
    format: "binary",
  });

  return encrypted;
}

await upload()
  .catch(console.error)
  .finally(() => process.exit())

動作デモ

上記コードを実際に動かしてみます。
まずは、ファイルを暗号化した上でWeb3.Storageにアップロードします。

❯❯❯ make encrypt-upload
node js/encrypt-upload.js
cid CID(bafkreiajim6gql4tziycfsn3m2c2rs4pkxifndj2re3jy5awaaxdmokkxi)

スクリーンショット-2023-12-17-15-12-11.png

ファイルが暗号化されているため、そのままの状態ではブラウザで閲覧できない & ダウンロードしても開けないことを確認しましょう。

スクリーンショット 2023-12-17 15.13.26.png

❯❯❯ make download URL=https://bafkreiajim6gql4tziycfsn3m2c2rs4pkxifndj2re3jy5awaaxdmokkxi.ipfs.w3s.link DOWNLOAD_FILE_NAME=encripted-sample-image.png
sh cmd/download.sh https://bafkreiajim6gql4tziycfsn3m2c2rs4pkxifndj2re3jy5awaaxdmokkxi.ipfs.w3s.link encripted-sample-image.png
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  3242  100  3242    0     0   1582      0  0:00:02  0:00:02 --:--:--  1586

スクリーンショット 2023-12-17 15.15.10.png

次に、秘密鍵をimportしたPC上で、ファイルを復元してみます。
復元は、gpg --decryptコマンドを実施して行います。

❯❯❯ make decrypt ENCRIPTED_FILE_NAME=encripted-sample-image.png DECRYPTED_FILE_NAME=decripted-sample-image.png
sh cmd/decrypt.sh encripted-sample-image.png decripted-sample-image.png
gpg: ECDH鍵, ID 89E54450FC5F9CD4で暗号化されました
gpg: cv25519鍵, ID DAF2F738BAAD9868, 日付2023-12-16に暗号化されました

すると、暗号化されたファイルが復号され、PC上でアクセス可能になりました!

スクリーンショット 2023-12-17 15.18.15.png

まとめ

Web3.Storageと暗号化技術を組み合わせることで、ファイルへのアクセスをコントロールし、安全に管理する方法を模索してみました。

ただし、より一般化するためには、gpgコマンドを用いて復号する箇所などを抽象化し、画像アップロード・ダウンロード操作の過程でよりシームレスに実施できるようにする必要がありそうです。

9
4
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
9
4