taint 选项启用了对实验性 React API 的支持,用于标记对象和值。此功能有助于防止敏感数据意外传递给客户端。启用后,你可以使用:
experimental_taintObjectReference 来标记对象引用。experimental_taintUniqueValue 来标记唯一值。须知:激活此标志还会为
app目录启用 React 的experimental通道。
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
experimental: {
taint: true,
},
}
export default nextConfig/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
taint: true,
},
}
module.exports = nextConfig警告:不要将 taint API 作为防止向客户端暴露敏感数据的唯一机制。请参阅我们的安全建议。
taint API 允许你通过声明性地、显式地标记不允许通过服务器-客户端边界的数据来进行防御。当对象或值通过服务器-客户端边界时,React 会抛出错误。
这在以下情况下很有帮助:
建议对你的数据和 API 进行建模,以确保敏感数据不会返回到不需要它的上下文中。
experimental_taintUniqueValue 参数参考。在本例中,getUserDetails 函数返回有关给定用户的数据。我们标记用户对象引用,使其无法跨越服务器-客户端边界。例如,假设 UserCard 是一个客户端组件。
import { experimental_taintObjectReference } from 'react'
function getUserDetails(id: string): UserDetails {
const user = await db.queryUserById(id)
experimental_taintObjectReference(
'Do not use the entire user info object. Instead, select only the fields you need.',
user
)
return user
}import { experimental_taintObjectReference } from 'react'
function getUserDetails(id) {
const user = await db.queryUserById(id)
experimental_taintObjectReference(
'Do not use the entire user info object. Instead, select only the fields you need.',
user
)
return user
}我们仍然可以从已标记的 userDetails 对象访问单个字段。
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}
/>
)
}export async function ContactPage({ params }) {
const { id } = await params
const userDetails = await getUserDetails(id)
return (
<UserCard
firstName={userDetails.firstName}
lastName={userDetails.lastName}
/>
)
}现在,将整个对象传递给客户端组件将抛出错误。
export async function ContactPage({
params,
}: {
params: Promise<{ id: string }>
}) {
const userDetails = await getUserDetails(id)
// Throws an error
return <UserCard user={userDetails} />
}export async function ContactPage({ params }) {
const { id } = await params
const userDetails = await getUserDetails(id)
// Throws an error
return <UserCard user={userDetails} />
}在本例中,我们可以通过等待对 config.getConfigDetails 的调用来访问服务器配置。然而,系统配置包含我们不希望暴露给客户端的 SERVICE_API_KEY。
我们可以标记 config.SERVICE_API_KEY 值。
import { experimental_taintUniqueValue } from 'react'
function getSystemConfig(): SystemConfig {
const config = await config.getConfigDetails()
experimental_taintUniqueValue(
'Do not pass configuration tokens to the client',
config,
config.SERVICE_API_KEY
)
return config
}import { experimental_taintUniqueValue } from 'react'
function getSystemConfig() {
const config = await config.getConfigDetails()
experimental_taintUniqueValue(
'Do not pass configuration tokens to the client',
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_KEY 传递给 ClientDashboard 会抛出错误。
export async function Dashboard() {
const systemConfig = await getSystemConfig()
// Someone makes a mistake in a PR
const version = systemConfig.SERVICE_API_KEY
return <ClientDashboard version={version} />
}请注意,即使 systemConfig.SERVICE_API_KEY 被重新分配给一个新变量,将其传递给客户端组件仍然会抛出错误。
然而,从已标记的唯一值派生出的值将被暴露给客户端。
export async function Dashboard() {
const systemConfig = await getSystemConfig()
// Someone makes a mistake in a PR
const version = `version::${systemConfig.SERVICE_API_KEY}`
return <ClientDashboard version={version} />
}更好的方法是从 getSystemConfig 返回的数据中移除 SERVICE_API_KEY。