experimental_taintUniqueValue

正在建设中

此 API 仍处于实验阶段,尚未在 React 的稳定版本中提供。

您可以尝试将 React 软件包升级到最新的实验版本以使用它

  • react@experimental
  • react-dom@experimental
  • eslint-plugin-react-hooks@experimental

React 实验版本可能包含错误。请勿在生产环境中使用它们。

此 API 只能在 React 服务器组件 中使用。

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 参数定义了值保持污染状态的持续时间。对于应该无限期保持污染状态的值,可以使用 globalThisprocess 等对象作为 lifetime 参数。这些对象的寿命涵盖了应用程序执行的整个过程。

import {experimental_taintUniqueValue} from 'react';

experimental_taintUniqueValue(
'Do not pass a user password to the client.',
globalThis,
process.env.SECRET_KEY
);

如果受污染值的寿命与某个对象绑定,则 lifetime 应该是封装该值的 object。这确保了受污染的值在封装对象的整个生命周期内都受到保护。

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 参数。如果此对象存储在全局缓存中或可被另一个请求访问,则会话令牌将保持污染状态。

陷阱

不要仅仅依靠污染来保证安全。污染一个值并不能阻止所有可能的派生值。例如,通过将受污染的字符串转换为大写来创建新值不会污染新值。

import {experimental_taintUniqueValue} from 'react';

const password = 'correct horse battery staple';

experimental_taintUniqueValue(
'Do not pass the password to the client.',
globalThis,
password
);

const uppercasePassword = password.toUpperCase() // `uppercasePassword` is not tainted

在此示例中,常量 password 被污染。然后,通过在 password 上调用 toUpperCase 方法,使用 password 创建一个新值 uppercasePassword。新创建的 uppercasePassword 未被污染。

其他从受污染的值派生新值的类似方法(例如将其连接成更大的字符串、将其转换为 base64 或返回子字符串)会创建未污染的值。

污染只防止简单的错误,例如将秘密值显式传递给客户端。调用 taintUniqueValue 时的错误(例如在 React 之外使用全局存储,而没有相应的生存期对象)会导致受污染的值变为未污染。污染是一层保护;一个安全的应用程序将具有多层保护、精心设计的 API 和隔离模式。

深入探讨

使用 server-onlytaintUniqueValue 防止泄露密钥

如果您运行的服务器组件环境可以访问私钥或密码(例如数据库密码),则必须小心不要将其传递到客户端组件。

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 时定义的消息。