Menu

taint

使用方法

taintオプションは、オブジェクトと値をタイント化するための実験的なReact APIのサポートを有効にします。この機能は、機密データが誤ってクライアントに渡されるのを防ぐのに役立ちます。有効にすると、以下を使用できます:

補足:このフラグを有効にすると、appディレクトリに対するReactのexperimentalチャネルも有効になります。

next.config.ts
TypeScript
import type { NextConfig } from 'next'
 
const nextConfig: NextConfig = {
  experimental: {
    taint: true,
  },
}
 
export default nextConfig

警告:機密データをクライアントに公開するのを防ぐ唯一のメカニズムとしてtaint APIに依存しないでください。セキュリティに関する推奨事項を参照してください。

taint APIを使用することで、Server-Clientの境界を越えて渡すことが許可されていないデータを、宣言的かつ明示的にマークしておくことで、防御的に対応できます。オブジェクトまたは値がServer-Clientの境界を越えて渡される場合、Reactがエラーをスローします。

これは以下の場合に役立ちます:

  • データを読み取るメソッドがユーザーの制御外にある場合
  • ユーザーが定義していない機密データ形状を扱う必要がある場合
  • Server Componentのレンダリング中に機密データがアクセスされる場合

データをモデル化し、機密データが不要なコンテキストに返されないようにすることをお勧めします。

注意事項

  • タイント化は参照によってオブジェクトを追跡することのみができます。オブジェクトをコピーすると、タイント化されていないバージョンが作成され、APIで提供されたすべての保証が失われます。コピーをタイント化する必要があります。
  • タイント化は、タイント化された値から派生したデータを追跡することができません。派生した値もタイント化する必要があります。
  • 値は、参照の有効期間がスコープ内にある限り、タイント化されたままです。詳細については、experimental_taintUniqueValueパラメータリファレンスを参照してください。

オブジェクト参照のタイント化

この場合、getUserDetails関数は特定のユーザーに関するデータを返します。ユーザーオブジェクト参照をタイント化し、Server-Clientの境界を越えられないようにします。例えば、UserCardがClient Componentである場合を想定します。

import { experimental_taintObjectReference } from 'react'
 
function getUserDetails(id: string): UserDetails {
  const user = await db.queryUserById(id)
 
  experimental_taintObjectReference(
    'ユーザー情報オブジェクト全体は使用しないでください。代わりに、必要なフィールドのみを選択してください。',
    user
  )
 
  return user
}
import { experimental_taintObjectReference } from 'react'
 
function getUserDetails(id) {
  const user = await db.queryUserById(id)
 
  experimental_taintObjectReference(
    'ユーザー情報オブジェクト全体は使用しないでください。代わりに、必要なフィールドのみを選択してください。',
    user
  )
 
  return user
}

タイント化されたuserDetailsオブジェクトの個別フィールドには引き続きアクセスできます。

app/contact/page.tsx
TypeScript
export async function ContactPage({
  params,
}: {
  params: Promise<{ id: string }>
}) {
  const { id } = await params
  const userDetails = await getUserDetails(id)
 
  return (
    <UserCard
      firstName={userDetails.firstName}
      lastName={userDetails.lastName}
    />
  )
}

これで、オブジェクト全体をClient Componentに渡すとエラーがスローされます。

export async function ContactPage({
  params,
}: {
  params: Promise<{ id: string }>
}) {
  const userDetails = await getUserDetails(id)
 
  // エラーがスローされます
  return <UserCard user={userDetails} />
}
export async function ContactPage({ params }) {
  const { id } = await params
  const userDetails = await getUserDetails(id)
 
  // エラーがスローされます
  return <UserCard user={userDetails} />
}

一意の値のタイント化

この場合、config.getConfigDetailsへの呼び出しをawaitすることで、サーバー設定にアクセスできます。しかし、システム設定には、クライアントに公開したくないSERVICE_API_KEYが含まれています。

config.SERVICE_API_KEY値をタイント化できます。

import { experimental_taintUniqueValue } from 'react'
 
function getSystemConfig(): SystemConfig {
  const config = await config.getConfigDetails()
 
  experimental_taintUniqueValue(
    'クライアントに設定トークンを渡さないでください',
    config,
    config.SERVICE_API_KEY
  )
 
  return config
}
import { experimental_taintUniqueValue } from 'react'
 
function getSystemConfig() {
  const config = await config.getConfigDetails()
 
  experimental_taintUniqueValue(
    'クライアントに設定トークンを渡さないでください',
    config,
    config.SERVICE_API_KEY
  )
 
  return config
}

systemConfigオブジェクトの他のプロパティには引き続きアクセスできます。

export async function Dashboard() {
  const systemConfig = await getSystemConfig()
 
  return <ClientDashboard version={systemConfig.SERVICE_API_VERSION} />
}

しかし、SERVICE_API_KEYClientDashboardに渡すとエラーがスローされます。

export async function Dashboard() {
  const systemConfig = await getSystemConfig()
  // 誰かがPRで間違えた
  const version = systemConfig.SERVICE_API_KEY
 
  return <ClientDashboard version={version} />
}

systemConfig.SERVICE_API_KEYが新しい変数に再割り当てされた場合でも、Client Componentに渡すとエラーがスローされることに注意してください。

一方、タイント化された一意の値から派生した値は、クライアントに公開されます。

export async function Dashboard() {
  const systemConfig = await getSystemConfig()
  // 誰かがPRで間違えた
  const version = `version::${systemConfig.SERVICE_API_KEY}`
 
  return <ClientDashboard version={version} />
}

より良いアプローチは、getSystemConfigから返されるデータからSERVICE_API_KEYを削除することです。