paint-brush
開発者が機密データが誤って別のデバイスに届かないようにする方法@dougsillars1
新しい歴史

開発者が機密データが誤って別のデバイスに届かないようにする方法

Doug8m2024/12/25
Read on Terminal Reader

長すぎる; 読むには

開発者は、アプリケーションに保存されている機密データが誤って別の当事者に渡されないようにする必要があります。このチュートリアルでは、Clerk を認証レイヤーとして使用して Remix アプリを構築する方法について説明します。次に、Neon の行レベル セキュリティを使用して顧客データを保護します。
featured image - 開発者が機密データが誤って別のデバイスに届かないようにする方法
Doug HackerNoon profile picture
0-item

近所の人からの荷物を玄関で受け取ったことがありますか? (誤って開封してしまったこともありますか?) 誰かに機密性の高いボイスメールを残したことはありませんか? アプリケーション開発者として、アプリケーションに保存されている機密データが誤って別の相手に渡らないようにすることがあなたの仕事です。


顧客データを安全に保存する方法は数多くありますが、その多くは非常に複雑で実装が困難です。理想的には、機能の設計をシンプルかつ安全なままにしながら、すべての顧客データを 1 つのデータベースで保護します。


行レベル セキュリティ (RLS)は、データベース テーブル内の特定のデータ行へのアクセスを保護および制御する機能です。これは、アカウント間でのデータ漏洩を心配することなく、すべての顧客データを 1 つのデータベースに保存できる強力なツールです。ただし、RLS を正しく実装するには、ログイン詳細とデータベース権限を組み合わせる必要があるため、難しいプロセスになる可能性があります。Neon Authorize は、 OAuth プロバイダーからの認証を PostgreSQL データベースに自動的に統合することで、このプロセスを効率化します。


Neon Authorize は、既存の認証レイヤーを利用してログインしているすべてのユーザーを識別し、データベース内のすべてのデータをそのログイン認証情報に関連付けます。これにより、データベースに保存されているデータにはログインしているユーザーのみがアクセスでき、ログインしているユーザーのみがデータを見ることができるようになります。


このチュートリアルでは、Clerk を認証レイヤーとして使用して Remix アプリを構築する方法について説明します。Clerkは、人気のユーザー認証および管理ツールです。データ レイヤーとして Neon Postgres を使用し、ログインした各顧客のすべてのデータを保護するために Neon Authorize を活用します。テーブルの各行には、Clerk によって提供されるユーザー ID が指定されます。ユーザー ID で認証されたユーザーのみが、行内のデータを操作できます。


サンプル アプリケーションはシンプルで、ユーザー ID を使用してすべてのログインを RLS データベースに記録します。ページが読み込まれると、認証されたユーザーの最後の 10 件のログインが表示され、他のユーザーのデータ (同じ PostgreSQL テーブルに保存されている) は表示されません。では、始めましょう。

Remixアプリの作成


まず、以下のコード スニペットを使用して Remix アプリケーションを作成し、依存関係をインストールします。詳細な手順については、 Remix クイック スタート ガイドを参照してください。

 ##make a directory and initialise your NPM project mkdir neon-authorize-remix-clerk-app cd neon-authorize-remix-clerk-app npm init -y ## install runtime dependecies for Remix npm i @remix-run/node @remix-run/react @remix-run/serve isbot@4 react react-dom @remix-run/router drizzle-orm npm install @neondatabase/serverless npm install @clerk/remix npm i -D @remix-run/dev vite


Remix は Javascript ビルド ツールである Vite を使用するため、ルート ディレクトリにvite.config.jsを作成します。


 import { vitePlugin as remix } from "@remix-run/dev"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [remix()], });


開発を始める前に、Clerk と Neon のサービスを利用するためにアカウントを作成する必要があります。


Clerk と Neon インスタンスの作成

店員

新しいプロジェクトを作成するには、Clerk ダッシュボードにサインインします。

  1. 左側のナビゲーションで、 「API キー」を選択します。

    1. [クイック コピー] ボックスで [リミックス] を選択し、環境変数をコピーします。
    2. それらをコード内の.envファイルに貼り付けます。
  2. 左側のナビゲーションで、「 JWT テンプレート」を選択します。

    1. テンプレートを作成します(私は「 neon-remix 」という名前を付けました)。
    2. 後で使用するために、 JWKS エンドポイント URLをコピーします。

ネオン

  1. Neon コンソールにログインし、新しいプロジェクトを作成します。

  2. 左側のナビゲーション メニューから、 [承認]を選択します。

  3. 新しいプロバイダーを作成し、先ほど Clerk からコピーしたClerk JWKS URLを貼り付けます。


インスタンスを作成したら、「開始」をクリックします。サイドパネルが開き、Neon Authorize の統合を完了するための一連の手順が表示されます。

開始セットアップでは、Clerk を使用して基本的な Authorize プロジェクトをセットアップする手順が提供されます。

 1. Set up Neon Extension and Roles Privileges. Run these steps in the Dashboard. 2. Grant privileges to the roles in the neondb database. 

行レベルのセキュリティの設定

提供されているコードは、ToDo アプリ用です。Neon から提供されている ToDo アプリ用の定型コードを使用する代わりに、 login_historyテーブルを作成し、そこに RLS を設定します。Neon ダッシュボードで SQL エディターを開き、以下のコードを実行します。login_history テーブルは、各ユーザーのlogin_history時間を保存するために使用されます。


login_historyには、id、user_id、login_at の 3 つの列しかないことに注意してください。最後の 2 つの列には、アプリケーションでの最新のログインが表示されます。


 CREATE TABLE login_history ( id bigint generated by default as identity primary key, user_id text not null default (auth.user_id()), login_at timestamp not null default now() ); -- 1st enable row level security for your table ALTER TABLE login_history ENABLE ROW LEVEL SECURITY; -- 2nd create policies for your table CREATE POLICY "Individuals can add login." ON login_history FOR INSERT TO authenticated WITH CHECK ((select auth.user_id()) = user_id); CREATE POLICY "Individuals can view their own logins. " ON login_history FOR SELECT TO authenticated USING ((select auth.user_id()) = user_id);


提供された環境変数を.envに追加します



これらのセットアップ手順が完了すると、 .envは 4 つの変数 (Clerk から 2 つ、Neon から 2 つ) が含まれるようになります。

 CLERK_PUBLISHABLE_KEY=pk_test_.... CLERK_SECRET_KEY=sk_test_... # Database owner connection string DATABASE_URL='postgresql://neondb_owner:...' # Neon "authenticated" role connection string DATABASE_AUTHENTICATED_URL='postgresql://authenticated@ep-...


Remix アプリの構築

これで、アプリケーションをビルドする準備が整いました。完全なコードはGitHubで入手できますが、最も重要な機能をここで紹介します。アプリケーションのコアはapp/routes/_index.tsxにあります。


 export const loader: LoaderFunction = async (args) => { const { userId, getToken } = await getAuth(args); if (!userId) { return redirect("/sign-in"); } const authToken = await getToken(); console.log(userId); if (!authToken) { return null; } const DATABASE_AUTHENTICATED_URL= process.env.NEXT_PUBLIC_DATABASE_AUTHENTICATED_URL; try { const sql = neon(DATABASE_AUTHENTICATED_URL ?? '', { authToken, }); const loginResponse = await sql(`INSERT INTO login_history ("user_id") VALUES ($1) RETURNING *`,[userId]); // Retrieve last 10 logins const last10LoginsResponse = await sql(`SELECT * FROM login_history WHERE user_id = $1 ORDER BY login_at DESC LIMIT 10`, [userId]); console.log(`loginResponse: ${JSON.stringify(loginResponse)}`); return last10LoginsResponse as Array<LoginHistory>; } catch (error) { console.error(`Error inserting into login_history table: ${error.message}`); console.error(`Error details: ${JSON.stringify(error)}`); throw error; } }

_index.tsxファイルのLoaderFunctionは、クライアントにページをレンダリングする前にサーバー上でタスクを完了します。このアプリケーションでは、ローダーがアプリの重い処理の多くを実行します。


この関数は、まずユーザーがログインしていないかどうかを確認し、次にユーザーを/sign-inページにリダイレクトします。サインイン ページは、Google ログインやメール ログインなど、さまざまなログイン タイプを受け入れるように Clerk ダッシュボードで設定できます。


サインイン ページを作成するには、Clerk ダッシュボードに移動し、プロジェクトに必要なログイン方法を設定します。


ユーザーがログインしている場合、関数は Clerk からuserIdauthToken取得します。これらの値は、ユーザーがログインしていることを確認するために不可欠であり、その後、 userId使用してデータベースの各行にデータを入力できます。


RLS で保護されたデータベースに変更を加えるには、環境変数からDATABASE_AUTHENTCATED_URLを取得する必要があります。

RLS セキュリティを実装するためのコア ロジックはLoaderFunction内にあります。SQL Neon インスタンスは、環境変数と認証トークンを使用して初期化されます。 loginResponse関数は SQL 呼び出しを行い、user_id (および現在の時刻) を PostgreSQL データベースに挿入します。その後、 last10LoginsResponse関数は DB に対して最新の 10 件のログインを照会します。


最後に、ローダー関数からlast10LoginsResponseが返されます。


_index.tsxファイルのIndex()関数は、以下のスニペットに示すようにページのレイアウトをレンダリングします。


 export default function Index() { const logins = useLoaderData(); return ( <div> <h1>Signed in</h1> <p>You are signed in!</p> <p> <UserButton /></p> <div> <h1>Recent Logins</h1> {logins?.map((logins) => ( <li key={logins.id}> {logins.user_id} login at: {logins.login_at} </li> ))} </div> <p>< SignOutButton > Sign Out</ SignOutButton ></p> </div> ); }

上記のコードはLoaderFunctionから応答を取得します。この応答には、最新の 10 件のログイン エントリが含まれています。この応答により、ユーザーがサインインしていることを通知し、最新の 10 件のログインをリストし、次に示すように [サインアウト] ボタンを表示するページが作成されます。

応答が返されました


この例では、ログインしたユーザーのログイン データのみが表示されることを明確に示すために、 user_idも表示されます。

シークレット ウィンドウを使用すると、2 番目の Google アカウントでログインし、さまざまなユーザーのデータを並べて表示できます。



ログイン時間は重複しますが、データベースで行レベルのセキュリティを使用することで、アカウント間でのデータ漏洩を防ぐことができます。行は、認証されたユーザーに対してのみ抽出および表示できます。


結論

データのプライバシー保護は重要なユースケースです。アプリケーションには個人情報が保存されることが多いため、適切な人がデータを保持できるように保護する必要があります。消費者は GDPR などの法的保護をますます強化しており、Neon Authorize などのツールを使用すると、行レベルのセキュリティを簡単に実装して顧客のデータを保護できます。


この記事では、Neon データベースで行レベル セキュリティを有効にするために必要な手順について説明しました。顧客のデータに RLS を使用すると、ログインしたユーザーだけが自分のデータを抽出するための資格情報を持つようになります。


今すぐ Neon を使用して、アプリに行レイヤー セキュリティを追加しましょう。