メインコンテンツまでスキップ

Secrets(シークレット)

API key、パスワード、証明書などの機密データを安全に保存・管理します。

Secrets を使う理由

アプリケーションには機密性の高い認証情報が必要です。サードパーティサービス用の API key、データベースのパスワード、認証トークンなどです。これらをコードにハードコードするのはセキュリティ上のリスクであり、環境変数だけではローテーションやアクセス制御を提供できません。

Squid secrets を使うと、認証情報を安全に保存し、実行時にバックエンドコードからアクセスできます。

Backend code
@executable()
async sendEmail(to: string, subject: string, body: string): Promise<void> {
// Access the secret securely without exposing it to the client
const apiKey = this.secrets['SENDGRID_API_KEY'];
await sendgrid.send({ to, subject, body, apiKey });
}

ハードコードされた値は不要です。認証情報を露出しません。Secrets は実行時に注入されます。

概要

Squid secrets は、機密データのための安全な key-value ストアを提供します。Secrets は保存時に暗号化され、パフォーマンスのためにキャッシュされ、実行時に this.secrets を介してバックエンドコードからアクセスできます。

Squid は 2 種類の secrets をサポートしています。

TypeDescriptionValueExample use case
Custom secrets外部システム由来の認証情報向けに、ユーザーが定義する key-value ペア値はユーザーが指定SendGrid API key や DB パスワードの保存
API keysプラットフォームと認証するための Squid 管理のアプリケーションキーSquid が自動生成バックエンドの Squid への認証

Secrets を使う場面

  • サードパーティ API key や認証トークンを安全に保存する
  • データベース認証情報を管理する
  • schedulers を使ってスケジュールに従い API key をプログラム的にローテーションする
  • クライアントに認証情報を露出せずに executables 経由で安全な API 呼び出しを行う

仕組み

  1. Squid Console から、または Client SDK を通じてプログラム的に secrets を作成する
  2. Squid が secrets を暗号化して安全に保存する
  3. バックエンドサービスで this.secrets['SECRET_NAME'] を通じて secrets にアクセスする
  4. プログラムによる管理(ローテーション、動的作成)には squid.admin().secrets() を使用する

クイックスタート

前提条件

  • バックエンドプロジェクトを含む Squid アプリケーション
  • @squidcloud/backend パッケージがNPMからインストールされていること

Step 1: Squid Console で secret を追加する

Squid Console で対象アプリケーションに移動し、Secrets に進んで、key を MY_API_SECRET、値を任意の値として新しい secret を追加します。

Step 2: バックエンドから secret にアクセスする

Backend code
import { executable, SquidService } from '@squidcloud/backend';

export class ExampleService extends SquidService {
@executable()
async getProtectedData(): Promise<string> {
const apiKey = this.secrets['MY_API_SECRET'];

const response = await fetch('https://api.example.com/data', {
headers: { Authorization: `Bearer ${apiKey}` },
});

return (await response.json()) as string;
}
}

Step 3: バックエンドをデプロイする

実行時に secret が利用できるように、バックエンドをデプロイします。バックエンドディレクトリで次のコマンドを実行してください。

squid deploy

デプロイの詳細は、deploying your backend を参照してください。

Step 4: クライアントから呼び出す

Client code
const data = await squid.executeFunction('getProtectedData');
console.log(data);

認証と設定

Caution

Client SDK を介したプログラムによる secret 管理では、apiKey オプションを使用してアプリケーションの API key でクライアントを初期化する必要があります。ユーザー向けアプリケーションから 絶対に これを行わないでください。secret 管理は Squid Backend などの安全な環境でのみ実行してください。

secrets をプログラム的に管理するには、API key を使って Squid client を初期化します。

import { Squid } from '@squidcloud/client';

const squid = new Squid({
appId: 'YOUR_APP_ID',
region: 'us-east-1.aws',
apiKey: 'YOUR_API_KEY', // Required for programmatic secret management
});

API key がない場合、secrets の管理を試みると UNAUTHORIZED エラーになります。

コアコンセプト

Secret entry

すべての secret 操作は SecretEntry オブジェクトを返します。

interface SecretEntry {
key: string; // The secret name
value: string; // The secret value (always stored as a string)
lastUpdated: number; // Timestamp in milliseconds since epoch
}

upsert メソッドは stringnumberboolean を受け取れますが、すべての値は文字列として保存され、文字列として返されます。

Custom secrets と API keys の違い

Squid では、用途が異なりライフサイクル要件も異なるため、secrets を 2 つのカテゴリに分けています。

Custom secrets はユーザーが制御する値を保存します。サードパーティの API key、データベースパスワード、OAuth トークンなど、外部システム由来の認証情報に使用します。値はユーザーが設定し、認証情報が変更された際の更新もユーザーの責任で行います。

API keys は、Squid が生成・管理する認証キーです。アプリケーションがリクエストを認証するための安全で一意なキー(例: バックエンドを Squid プラットフォームに対して認証する)として使用します。値は選べず、Squid が作成します。upsert を再度呼び出すことでローテーション(新しい値の生成)できます。

FeatureCustom secretsAPI keys
Value source値はユーザーが指定Squid が自動生成
Use caseサードパーティ認証情報、パスワード、トークンアプリケーション認証キー
Create/Updatesecrets.upsert(key, value)secrets.apiKeys.upsert(key)
Batch operationsupsertMany, deleteMany利用不可
Force deleteサポートあり(force パラメータ)サポートなし

要点: すでに値を持つ認証情報を安全に保存したい場合は custom secret を使います。Squid にキーを生成・管理させたい場合は API key を使います。

バックエンドからのアクセス

Squid のバックエンドサービスでは、secrets は this.secrets を通じて読み取り専用の key-value マップとして利用できます。

Backend code
// Access the value directly by key
const apiKey = this.secrets['MY_API_KEY'];
const dbPassword = this.secrets['DB_PASSWORD'];

このマップは実行時に自動的に生成されます。バックエンドから secrets を読み取るために API key や特別な設定は不要です。

Force delete

custom secret を削除する際、force パラメータを渡せます。デフォルトでは forcefalse で、secret が connector で使用中の場合は削除が失敗します。これにより、誤って connector 設定を壊してしまうことを防げます。

const secrets = squid.admin().secrets();

// Default: fails if 'DB_PASSWORD' is used by a connector
await secrets.delete('DB_PASSWORD');

// Force delete regardless of usage
await secrets.delete('DB_PASSWORD', true);

Custom Secrets

プログラムによる secret 管理はすべて squid.admin().secrets() クライアントを使用します。

const secrets = squid.admin().secrets();

Secret を取得する

名前で単一の secret を取得します。secret が存在しない場合は SecretEntry ではなく undefined を返します。

const secrets = squid.admin().secrets();

const entry = await secrets.get('SECRET_NAME');
if (entry) {
console.log(entry.key); // 'SECRET_NAME'
console.log(entry.value); // 'your_value'
console.log(entry.lastUpdated); // 1692306991724
}

すべての secrets を取得する

すべての custom secrets を、secret 名をキーとする SecretEntry のマップとして取得します。

const secrets = squid.admin().secrets();

const allSecrets = await secrets.getAll();
// {
// 'SECRET_NAME': { key: 'SECRET_NAME', value: 'your_value', lastUpdated: 1692306991724 },
// 'OTHER_SECRET': { key: 'OTHER_SECRET', value: 'other_value', lastUpdated: 1692306991725 }
// }

Secret を作成または更新する

upsert を使って新しい secret を作成するか、既存の secret を更新します。stringnumberboolean を受け取れますが、すべての値は文字列として保存され、文字列として返されます。

const secrets = squid.admin().secrets();

const entry = await secrets.upsert('SECRET_NAME', 'your_new_value');
// { key: 'SECRET_NAME', value: 'your_new_value', lastUpdated: 1692306991724 }

複数の secret を一括で作成または更新するには upsertMany を使用します。

const secrets = squid.admin().secrets();

const entries = await secrets.upsertMany([
{ key: 'API_KEY_1', value: 'key-value-1' },
{ key: 'API_KEY_2', value: 'key-value-2' },
]);
// Returns an array of SecretEntry objects

Secret を削除する

名前で単一の secret を削除します。

const secrets = squid.admin().secrets();
await secrets.delete('SECRET_NAME');

複数の secret を一括削除します。

const secrets = squid.admin().secrets();
await secrets.deleteMany(['SECRET_NAME', 'OTHER_SECRET']);

connector により使用中の secrets を強制削除するには、第 2 引数に true を渡します。

const secrets = squid.admin().secrets();

// Force delete even if used by a connector
await secrets.delete('SECRET_NAME', true);
await secrets.deleteMany(['SECRET_NAME', 'OTHER_SECRET'], true);

API Keys

Squid API keys は secret クライアントの apiKeys プロパティから管理します。custom secrets と異なり、キーの値は Squid が自動生成します。

const apiKeys = squid.admin().secrets().apiKeys;

API key を取得する

名前で API key を取得します。キーが存在しない場合は SecretEntry ではなく undefined を返します。

const apiKeys = squid.admin().secrets().apiKeys;

const entry = await apiKeys.get('API_KEY_NAME');
if (entry) {
console.log(entry.value); // 'a123b456-cd78-9e90-f123-gh45i678j901'
}

すべての API keys を取得する

すべての API keys を SecretEntry オブジェクトのマップとして取得します。

const apiKeys = squid.admin().secrets().apiKeys;

const allKeys = await apiKeys.getAll();
// {
// 'API_KEY_NAME': {
// key: 'API_KEY_NAME',
// value: 'a123b456-cd78-9e90-f123-gh45i678j901',
// lastUpdated: 1692306991724
// }
// }

API key を作成またはローテーションする

キー名を指定して upsert を呼び出します。新しい値は Squid が自動生成します。

const apiKeys = squid.admin().secrets().apiKeys;

const entry = await apiKeys.upsert('API_KEY_NAME');
console.log(entry.value); // New auto-generated key value

API key を削除する

const apiKeys = squid.admin().secrets().apiKeys;
await apiKeys.delete('API_KEY_NAME');

エラーハンドリング

よくあるエラー

ErrorCauseSolution
UNAUTHORIZED有効な API key で client が初期化されていないclient 初期化時に apiKey オプションで API key を渡す
Secret in useforce=false で connector が使用中の secret を削除しようとした先に connector 依存を解除するか、forcetrue を渡す
undefined resultsecret が存在しないkey 名を確認する。存在しない場合 getundefined を返す
Request timeoutサーバーが操作のためのロックを取得できなかった少し待ってから再試行する

エラーの扱い方

const secrets = squid.admin().secrets();

try {
await secrets.upsert('MY_SECRET', 'new-value');
} catch (error) {
if (error.message === 'UNAUTHORIZED') {
console.error('API key is missing or invalid');
} else {
console.error('Failed to update secret:', error.message);
}
}

ベストプラクティス

セキュリティ

  1. クライアントで secrets を絶対に露出しない。 secrets へのアクセスはフロントエンドコードではなく、executables やその他のバックエンドコードで行ってください。
  2. API key の使用を制限する。 apiKey を指定して Squid client を初期化するのは、Squid Backend などの安全なサーバーサイド環境でのみ行ってください。
  3. サードパーティサービスには connectors を使う。 Squid の connectors は認証情報の注入を自動で行うため、手動で secrets を管理する必要性を減らせます。

ローテーション

  1. スケジュールで secrets をローテーションする。 schedulers を使用して API keys や認証情報を定期的にローテーションします。
  2. ローテーション前に lastUpdated を確認する。 secret の経過時間を先に確認し、不要なローテーションを避けます。

運用

  1. 一括変更にはバッチメソッドを使う。 複数の secrets を管理する場合、個別呼び出しより upsertManydeleteMany の方が効率的です。
  2. デフォルトで force delete を避ける。 デフォルト動作(force=false)は、connector 設定を誤って壊すことを防ぎます。

コード例

スケジュールで API key をローテーションする

secret 管理と scheduler を組み合わせて、API keys を自動でローテーションします。

Backend code
import { CronExpression, scheduler, SquidService } from '@squidcloud/backend';

export class KeyRotationService extends SquidService {
@scheduler('rotate-api-key', CronExpression.EVERY_DAY_AT_MIDNIGHT)
async rotateApiKey(): Promise<void> {
const entry = await this.squid.admin().secrets().apiKeys.get('MY_API_KEY');
if (!entry) return;

// Rotate if the key is over 30 days old
const thirtyDaysMs = 30 * 24 * 60 * 60 * 1000;
if (entry.lastUpdated < Date.now() - thirtyDaysMs) {
await this.squid.admin().secrets().apiKeys.upsert('MY_API_KEY');
console.log('API key rotated successfully');
}
}
}

サードパーティ API を安全に呼び出す

executable を使用して API keys をサーバー側に保持したまま、クライアントが呼び出しをトリガーできるようにします。

Backend code
import { executable, SquidService } from '@squidcloud/backend';

interface EmailRequest {
to: string;
subject: string;
body: string;
}

export class EmailService extends SquidService {
@executable()
async sendEmail(request: EmailRequest): Promise<{ success: boolean }> {
const apiKey = this.secrets['SENDGRID_API_KEY'];

const response = await fetch('https://api.sendgrid.com/v3/mail/send', {
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
personalizations: [{ to: [{ email: request.to }] }],
from: { email: 'noreply@example.com' },
subject: request.subject,
content: [{ type: 'text/plain', value: request.body }],
}),
});

if (!response.ok) {
throw new Error(`Email send failed: ${response.status}`);
}

return { success: true };
}
}
Client code
const result = await squid.executeFunction('sendEmail', {
to: 'user@example.com',
subject: 'Welcome!',
body: 'Thanks for signing up.',
});

関連項目

  • Executables - クライアントからバックエンド関数を呼び出す
  • Schedulers - スケジュールに従ってコードを実行する
  • Connectors - 管理された認証情報でサードパーティサービスに接続する