taintUniqueValue
允许您防止将唯一值(例如密码、密钥或令牌)传递到客户端组件。
taintUniqueValue(errMessage, lifetime, value)
要防止传递包含敏感数据的对象,请参阅 taintObjectReference
。
参考
taintUniqueValue(message, lifetime, value)
调用 taintUniqueValue
并传入密码、令牌、密钥或哈希值,将其注册到 React 中,使其不被允许原样传递到客户端。
import {experimental_taintUniqueValue} from 'react';
experimental_taintUniqueValue(
'Do not pass secret keys to the client.',
process,
process.env.SECRET_KEY
);
参数
-
message
: 如果将value
传递给客户端组件,则要显示的消息。如果将value
传递给客户端组件,则此消息将显示为抛出的错误的一部分。 -
lifetime
: 指示应污染value
多长时间的任何对象。在此对象仍然存在时,value
将被阻止发送到任何客户端组件。例如,传递globalThis
会阻止应用程序生命周期内的值。lifetime
通常是一个其属性包含value
的对象。 -
value
: 字符串、BigInt 或 TypedArray。value
必须是具有高熵的唯一字符或字节序列,例如加密令牌、私钥、哈希或长密码。value
将被阻止发送到任何客户端组件。
返回值
experimental_taintUniqueValue
返回 undefined
。
注意事项
- 从被污染的值派生新值可能会影响污染保护。通过将被污染的值转换为大写、将被污染的字符串值连接成更大的字符串、将被污染的值转换为base64、提取被污染值的子字符串以及其他类似的转换创建的新值不会被污染,除非您显式调用
taintUniqueValue
对这些新创建的值进行处理。 - 不要使用
taintUniqueValue
来保护低熵值,例如PIN码或电话号码。如果请求中的任何值都受攻击者控制,他们可以通过枚举秘密的所有可能值来推断哪个值被污染。
使用方法
防止令牌传递到客户端组件
为确保敏感信息(如密码、会话令牌或其他唯一值)不会意外地传递到客户端组件,taintUniqueValue
函数提供了一层保护。当值被污染时,任何尝试将其传递到客户端组件的操作都会导致错误。
lifetime
参数定义了值保持污染的持续时间。对于应该无限期保持污染的值,可以使用 globalThis
或 process
等对象作为 lifetime
参数。这些对象的生存期涵盖了应用程序执行的整个持续时间。
import {experimental_taintUniqueValue} from 'react';
experimental_taintUniqueValue(
'Do not pass a user password to the client.',
globalThis,
process.env.SECRET_KEY
);
如果被污染值的生存期与某个对象绑定,则 lifetime
应为此封装该值的的对象。这确保了被污染的值在其封装对象的整个生命周期内都受到保护。
import {experimental_taintUniqueValue} from 'react';
export async function getUser(id) {
const user = await db`SELECT * FROM users WHERE id = ${id}`;
experimental_taintUniqueValue(
'Do not pass a user session token to the client.',
user,
user.session.token
);
return user;
}
在此示例中,user
对象用作 lifetime
参数。如果此对象存储在全局缓存中或可被其他请求访问,则会话令牌将保持污染状态。
深入探讨
如果您正在运行一个可以访问私钥或密码(例如数据库密码)的服务器组件环境,则必须小心不要将其传递给客户端组件。
export async function Dashboard(props) {
// DO NOT DO THIS
return <Overview password={process.env.API_PASSWORD} />;
}
"use client";
import {useEffect} from '...'
export async function Overview({ password }) {
useEffect(() => {
const headers = { Authorization: password };
fetch(url, { headers }).then(...);
}, [password]);
...
}
此示例会将秘密API令牌泄漏给客户端。如果此API令牌可用于访问此特定用户无权访问的数据,则可能导致数据泄露。
理想情况下,此类秘密会被抽象到单个帮助器文件中,该文件只能由服务器上的受信任数据实用程序导入。该帮助器甚至可以使用 server-only
进行标记,以确保此文件不会在客户端导入。
import "server-only";
export function fetchAPI(url) {
const headers = { Authorization: process.env.API_PASSWORD };
return fetch(url, { headers });
}
有时在重构过程中会发生错误,并非所有同事都可能知道这一点。为了防止将来发生此类错误,我们可以“污染”实际密码。
import "server-only";
import {experimental_taintUniqueValue} from 'react';
experimental_taintUniqueValue(
'Do not pass the API token password to the client. ' +
'Instead do all fetches on the server.'
process,
process.env.API_PASSWORD
);
现在,无论何时有人尝试将此密码传递到客户端组件,或使用服务器函数将密码发送到客户端组件,都会抛出错误,并显示您在调用 taintUniqueValue
时定义的消息。