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 буде передано клієнтському компоненту.

  • lifetime: Будь-який об'єкт, який вказує, як довго значення має бути зіпсованим. Значення буде заблоковано для надсилання до будь-якого клієнтського компонента, доки цей об'єкт існує. Наприклад, передача globalThis заблокує значення на весь час існування програми. lifetime зазвичай є об'єктом, властивості якого містять value.

  • value: Рядок, bigint або типізований масив. 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. Якщо цей об'єкт буде збережено у глобальному кеші або буде доступно іншим запитом, то маркер сеансу залишиться зіпсованим.

Не покладайтеся лише на псування для безпеки. Псування значення не блокує всі можливі похідні значення. Наприклад, створення нового значення шляхом використання верхнього регістру зіпсованого рядка не призведе до псування нового значення.

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 використовується для створення нового значення uppercasePassword шляхом виклику методу toUpperCase у password. Новостворений uppercasePassword не пошкоджено.

Інші подібні способи отримання нових значень із зіпсованих значень, такі як об'єднання у більший рядок, перетворення у base64 або повернення підрядка, створюють незіпсовані значення.

Псування захищає лише від простих помилок, таких як явна передача клієнту секретних значень. Помилки у виклику taintUniqueValue, такі як використання глобального сховища за межами React, без відповідного об'єкту життєвого циклу, можуть призвести до того, що зіпсоване значення стане незаплямованим. Псування - це рівень захисту; безпечний застосунок матиме декілька рівнів захисту, добре розроблені API та патерни ізоляції.

Використання server-only та taintUniqueValue для запобігання витоку секретів

Якщо ви використовуєте середовище серверних компонентів, яке має доступ до приватних ключів або паролів, таких як паролі до баз даних, вам слід бути обережними і не передавати їх клієнтським компонентам.

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.