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

データアクセスのセキュリティ

Squid が提供するさまざまなデコレーターを使用して、Squid に接続されるデータベース(組み込みの内部データベースを含む)を保護します。

各デコレーターは、データベースの異なる部分を保護するために設計されています。

特定の種類の操作を保護するためには、異なるコンテキストオブジェクトが関数に渡されます。たとえば、read 操作を保護したい場合、コンテキストオブジェクトは QueryContext 型となり、write 操作の場合は MutationContext オブジェクトが使用されます。

適切なデコレーターを使用することで、次のいずれかのデータアクセスの種類を保護できます:

  • read
  • insert
  • update
  • delete
  • write
  • all

なお、write デコレーターは insertupdatedelete 操作を含むため、データベース上のすべての種類の書き込み操作に対して包括的な保護を提供します。

@secureDatabase

Squid が提供する @secureDatabase デコレーターは、アクセスされるテーブルやコレクションに関係なく、すべてのデータベースアクセスを保護するために使用できます。このデコレーターは、データベースにアクセスするすべての操作に対して認可チェックを強制します。

たとえば、開発者は @secureDatabase デコレーターを使用して、認証されたユーザーのみがデータベースにアクセスできるようにすることができます。その場合、ユーザーが認証されているかどうかを確認する関数を定義します:

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

export class ExampleService extends SquidService {
@secureDatabase('all', 'usersDatabase')
verifyUserAuthenticated(): boolean {
return this.isAuthenticated();
}
}

usersDatabase の変更を admin プロパティを持つユーザーのみに許可するために、開発者はユーザーが認証され、かつ admin プロパティを保持しているかを確認する verifyUserAuthenticated 関数と組み合わせて @secureDatabase デコレーターを使用できます:

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

export class ExampleService extends SquidService {
@secureDatabase('write', 'usersDatabase')
verifyUserAuthenticated(context: MutationContext): boolean {
const userAuth = this.getUserAuth();
if (!userAuth) return false;
return !!userAuth.attributes['admin'];
}
}

場合によっては、クライアントが認可されているかどうかを判断するために、アクションのコンテキストにアクセスする必要があります。たとえば、次の関数は、ユーザーが認証されているかどうか、および実行しようとしている書き込み操作が insertupdate ではなく)であるかどうかを確認します:

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

export class ExampleService extends SquidService {
@secureDatabase('write', 'usersDatabase')
allowOnlyInsertsAndAuthenticated(context: MutationContext): boolean {
return this.isAuthenticated() && context.getMutationType() === 'insert';
}
}

@secureCollection

開発者は、@secureCollection デコレーターを使用してデータベース内の特定のコレクションを保護することができます。これにより、認可されたユーザーのみが特定のコレクション内のデータにアクセスおよび変更できるようになります。

たとえば、開発者は @secureCollection デコレーターを使用して、ユーザーが owner カラムに自身の userId を持つドキュメントのみを読み取れるようにすることができます。以下はそのコードスニペットです:

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

export class ExampleService extends SquidService {
@secureCollection('Items', 'read')
secureItemsRead(context: QueryContext<Item>): boolean {
const userId = this.getUserAuth()?.userId;
if (!userId) return false;
return context.isSubqueryOf('owner', '==', userId);
}
}

また、ユーザーが自身が所有する Items のみを更新、削除、または挿入できるようにしたい場合もあります:

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

export class ExampleService extends SquidService {
@secureCollection('Items', 'write')
secureItemWrite(context: MutationContext<Item>): boolean {
const userId = this.getUserAuth()?.userId;
if (!userId) return false;
const { before, after } = context.beforeAndAfterDocs;
if (before && before.owner !== userId) return false;
if (after && after.owner !== userId) return false;
return true;
}
}

ネイティブクエリのセキュリティ

ネイティブクエリ を保護するには、@secureNativeQuery() デコレーターを使用し、データベースの統合 ID を渡します。以下の例は、ユーザーが認証されているかどうかを確認する関数を定義しています:

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

export class ExampleService extends SquidService {
@secureNativeQuery('YOUR_INTEGRATION_ID')
verifyUserAuthenticated(): boolean {
return this.isAuthenticated();
}
}

この例のように、auth token attributes を使用して、より細かなアクセス制御を追加することも可能です:

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

export class ExampleService extends SquidService {
@secureNativeQuery('YOUR_INTEGRATION_ID')
verifyAdminUser(): boolean {
// Get the authenticated user's details
const userAuth = this.getUserAuth();
if (!userAuth) {
return false;
}

// Check for the admin attribute
return !!userAuth.attributes['admin'];
}
}

また、auth permissions を格納するためにコレクションを使用することもできます。対象のコレクションが Security Service function により保護されていることを確認してください。

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

export class ExampleService extends SquidService {
@secureNativeQuery('YOUR_INTEGRATION_ID')
verifyNativeQueryAccess(): Promise<boolean> {
// Get the authenticated user's ID
const userId = this.getUserAuth()?.userId;
if (!userId) {
return false;
}

// Check if the user's ID is in the collection listing users permitted to access native query
const userTableAccess = await this.squid.collection('table_access').doc(userId).snapshot();
return !!userTableAccess;
}
}

ネイティブクエリのコンテキスト

ネイティブのリレーショナルクエリまたは MongoDB クエリを実行する際、Squid バックエンドでは、クライアントが実行しようとしているネイティブクエリの種類('relational' または 'mongo')や、クエリの種類に基づいたその他の属性を示すコンテキストが利用可能です。

リレーショナルネイティブクエリ実行時に提供されるコンテキストタイプは次のとおりです:

Backend code
RelationalNativeQueryContext  {
type: 'relational';
query: string;
params: Record<string, any>;
}

このコンテキストを使用して、クライアントが実行可能なネイティブクエリの種類を制限できます。たとえば、以下のコードは、クライアントが SQUIDS(またはテーブル内の行)から、YEAR フィールドの値が 1980 より大きいドキュメントを選択する 1 種類のネイティブクエリのみを実行できるようにします。もしクライアントが異なるクエリを実行したり、1980 より前の値を照会しようとすると、クエリは失敗します。

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

export class ExampleService extends SquidService {
@secureNativeQuery('YOUR_INTEGRATION_ID')
verifyNativeQueryAccess(context: RelationalNativeQueryContext): boolean {
if (context.query !== 'SELECT * FROM SQUIDS WHERE YEAR = ${year}' || context.params.year < 1980) {
return false;
}
return true;
}
}

ネイティブ MongoDB クエリ実行時に提供されるコンテキストタイプは次のとおりです:

Backend code
MongoNativeQueryContext {
type: 'mongo';
collectionName: string;
aggregationPipeline: Array<any | undefined>;
}

このコンテキストを使用して、クライアントが Mongo aggregation pipeline に対して実行可能な集約パイプラインクエリの種類を制限できます。以下の例では、ユーザーが ORDERS コレクション内でのみ MongoDB の集約パイプラインを実行できるようにしています:

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

export class ExampleService extends SquidService {
@secureNativeQuery('YOUR_INTEGRATION_ID')
verifyNativeMongoQueryAccess(context: MongoNativeQueryContext): boolean {
if (context.collectionName !== 'ORDERS') {
return false;
}
return true;
}
}