experimental_taintUniqueValue

开发中

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

您可以尝试将 React 包升级到最新的实验版本

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

React 的实验版本可能包含 bug。请勿在生产环境中使用。

此 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 应为此封装该值的的对象。这确保了被污染的值在其封装对象的整个生命周期内都受到保护。

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 通过对 password 调用 toUpperCase 方法来创建一个新值 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 时定义的消息。