React taintUniqueValue防止敏感数据传递到客户端
在 React Server Components 和 Client Components 的交互过程中,服务器端的数据往往会自动序列化并传递给客户端。如果数据对象中混入了敏感信息(如 API 密钥、用户令牌、个人隐私等),这些信息可能会意外泄露到浏览器中。experimental_taintUniqueValue(或 taintUniqueValue)是 React 提供的一种防御机制,用于在运行时标记敏感数据,阻止其跨越服务器与客户端的边界。
以下指南将演示如何配置并使用此功能来锁定敏感数据。
1. 创建模拟的敏感数据源
首先,需要在服务器组件中准备一个包含敏感信息的对象。假设这是一个从数据库获取的用户对象,其中包含不应泄露的 authToken。
定义 data 对象,使其同时包含公开信息(username)和敏感信息(secretKey)。
// app/server-action.ts
'use server';
import { experimental_taintUniqueValue } from 'react';
export async function getUserData() {
// 模拟从数据库获取的数据
const userData = {
id: 'user_123',
username: 'alice_dev',
email: 'alice@example.com',
// 这是一个敏感的 API 密钥,绝不能发送到客户端
secretKey: 'sk_live_51MzQ...'
};
return userData;
}
2. 标记敏感值为“污点”
使用 experimental_taintUniqueValue 对敏感字段进行标记。一旦标记,React 将会追踪这个特定的值。如果尝试将包含此值的对象传递给客户端组件,React 会抛出错误。
引入 experimental_taintUniqueValue 函数。
调用 该函数,传入原因、敏感值和错误提示信息。
// app/server-action.ts (修改 getUserData 函数)
export async function getUserData() {
const userData = {
id: 'user_123',
username: 'alice_dev',
email: 'alice@example.com',
secretKey: 'sk_live_51MzQ...'
};
// 标记 secretKey 为敏感数据
experimental_taintUniqueValue(
'Do not pass the secret API key to the client.', // 原因
userData.secretKey, // 敏感值
'SecretKey is leaked in getUserData', // 调试信息
);
return userData;
}
3. 验证拦截效果(错误演示)
为了验证 taintUniqueValue 是否生效,尝试直接将包含 secretKey 的 userData 对象传递给一个客户端组件。React 应该会检测到被污染的值并阻止渲染。
创建 一个客户端组件 ProfileClient,接收 user 数据。
// components/ProfileClient.tsx
'use client';
export default function ProfileClient({ user }: { user: any }) {
return (
<div>
<h1>Profile: {user.username}</h1>
{/* 如果这里渲染了 secretKey,说明防御失效 */}
{/* <p>Key: {user.secretKey}</p> */}
</div>
);
}
修改 页面组件,获取数据并传递给 ProfileClient。
// app/page.tsx
import { getUserData } from './server-action';
import ProfileClient from './components/ProfileClient';
export default async function Page() {
const user = await getUserData();
// 尝试直接传递整个 user 对象
// React 会检测到 user.secretKey 已被“污染”,并在此处抛出错误
return <ProfileClient user={user} />;
}
此时运行应用,React 会报错,提示试图将一个被“污染”的值传递给客户端。
4. 清理数据并正确传递
由于 React 阻止了整个对象的传递(因为它包含了被污染的属性),必须将敏感数据从对象中剔除,仅保留客户端需要的公开数据,才能正常渲染。
修改 getUserData 函数,在返回数据前解构 并过滤 掉敏感字段。
// app/server-action.ts (最终版)
export async function getUserData() {
const userData = {
id: 'user_123',
username: 'alice_dev',
email: 'alice@example.com',
secretKey: 'sk_live_51MzQ...'
};
experimental_taintUniqueValue(
'Do not pass the secret API key to the client.',
userData.secretKey,
'SecretKey is leaked in getUserData',
);
// 创建一个不包含 secretKey 的新对象
const { secretKey, ...safeUserData } = userData;
// 仅返回安全的数据
return safeUserData;
}
验证 结果。现在 Page.tsx 中的 <ProfileClient user={user} /> 将接收到一个干净的对象,页面可以正常加载,且 secretKey 绝不会出现在浏览器的网络请求或 JavaScript 包中。
5. 针对唯一值的保护特性
experimental_taintUniqueValue 不仅检查值的内容,还会检查值的引用。如果你复制了这个值(例如 const myKey = user.secretKey),新复制的值也会被视为受污染。这对于处理可能被多次引用的敏感对象(如 Error 对象中可能包含请求上下文)非常有用。
测试 引用追踪:假设敏感信息被意外复制到了另一个变量中。
const errorLog = {
message: 'Login failed',
// 即使是不同的属性名,只要值引用了 userData.secretKey,也会被拦截
leakedToken: userData.secretKey
};
experimental_taintUniqueValue(
'Do not pass token in error logs',
userData.secretKey,
'Token leaked in errorLog'
);
// 如果尝试将 errorLog 传给客户端,同样会报错
这确保了无论敏感数据在对象结构中被移动到哪里,只要其引用被追踪,就无法逃逸到客户端。

暂无评论,快来抢沙发吧!