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
。
注意
- Deriving new values from tainted values can compromise tainting protection. New values created by uppercasing tainted values, concatenating tainted string values into a larger string, converting tainted values to base64, substringing tainted values, and other similar transformations are not tainted unless you explicitly call
taintUniqueValue
on these newly created values. - Do not use
taintUniqueValue
to protect low-entropy values such as PIN codes or phone numbers. If any value in a request is controlled by an attacker, they could infer which value is tainted by enumerating all possible values of the secret.
用法
防止将令牌传递给客户端组件
为了确保敏感信息(如密码、会话令牌或其他唯一值)不会被意外地传递给客户端组件,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) {
// 不要这样做
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
时定义的。