experimental_taintObjectReference
taintObjectReference
lets you prevent a specific object instance from being passed to a Client Component like a user
object.
experimental_taintObjectReference(message, object);
To prevent passing a key, hash or token, see taintUniqueValue
.
Reference
taintObjectReference(errMessage, object)
Call taintObjectReference
with an object to register it with React as something that should not be allowed to be passed to the Client as is:
import {experimental_taintObjectReference} from 'react';
experimental_taintObjectReference(
'Do not pass ALL environment variables to the client.',
process.env
);
Parameters
-
errMessage
: The message you want to display if the object gets passed to a Client Component. This message will the be contents of an Error that will be thrown if the object gets passed to a Client Component. -
object
: The object to be tainted.
Returns
experimental_taintObjectReference
returns undefined
.
Caveats
- Recreating or cloning a tainting object creates a new untained object which main contain sensetive data. For example, if you have a tainted
user
object,const userInfo = {name: user.name, ssn: user.ssn}
or{...user}
will create new objects which are not tainted.taintObjectReference
only protects against simple mistakes when the object is passed through to a Client Component unchanged. - Functions and class instances can be passed to
taintObjectReference
asobject
. Functions and classes are already blocked from being passed to Client Components but the React’s default error message will be replaced by what you defined inerrMessage
. - If you taint a specific instances of a Typed Array any other copies of the Typed Array will not be tainted.
Usage
Prevent user data from unintentionally reaching the client
A Client Component should never accept objects that carry sensitive data. Ideally, the data fetching functions should not expose data that the current user should not have access to. Sometimes mistakes happen during refactoring. To protect against this mistakes happening down the line we can “taint” the user object in our data API.
import {experimental_taintObjectReference} from 'react';
export async function getUser(id) {
const user = await db`SELECT * FROM users WHERE id = ${id}`;
experimental_taintObjectReference(
'Do not pass the entire user object to the client. ' +
'Instead, pick off the specific properties you need for this use case.',
user,
);
return user;
}
Now whenever anyone tries to pass this object to a Client Component, an error will be thrown with the passed in error message instead.
Deep Dive
If you’re running a Server Components environment that has access to sensitive data, you have to be careful not to pass objects straight through:
// api.js
export async function getUser(id) {
const user = await db`SELECT * FROM users WHERE id = ${id}`;
return user;
}
import { getUser } from 'api.js';
import { InfoCard } from 'components.js';
export async function Profile(props) {
const user = await getUser(props.userId);
// DO NOT DO THIS
return <InfoCard user={user} />;
}
// components.js
"use client";
export async function InfoCard({ user }) {
return <div>{user.name}</div>;
}
Ideally, the getUser
should not expose data that the current user should not have access to. To prevent passing the user
object to a Client Component down the line we can “taint” the user object:
// api.js
import {experimental_taintObjectReference} from 'react';
export async function getUser(id) {
const user = await db`SELECT * FROM users WHERE id = ${id}`;
experimental_taintObjectReference(
'Do not pass the entire user object to the client. ' +
'Instead, pick off the specific properties you need for this use case.',
user,
);
return user;
}
Now if anyone tries to pass the user
object to a Client Component, an error will be thrown with the passed in error message.