Logo
BlogCategoriesChannels

React's most dangerous feature

Discover the risks and best practices for using 'use server' in React applications.

Theo - t3․ggTheo - t3․ggSeptember 5, 2024

This article was AI-generated based on this episode

What is 'use server' in React?

'Use server' in React directs functions to run on the server instead of the client. It's fundamental for server-side rendering, ensuring that certain operations don't expose sensitive data on the client-side. This directive, used within Next.js, significantly enhances security by compartmentalizing the execution of server-only functions.

Here's a breakdown of its role:

  • Server-side execution: Ensures functions run on the server, not the client.
  • End-point creation: Each function marked with 'use server' becomes an endpoint.
  • Security enhancement: Keeps sensitive operations away from the client-side.

In essence, 'use server' helps maintain a strict separation between server and client tasks, bolstering security, as elaborated in Next.js use server.

Why is 'use server' potentially dangerous?

Although powerful, use server in React carries significant risks if not correctly implemented. Here's why:

  • Data Leakage: Functions marked with use server expose themselves as endpoints. This can lead to unintended access to sensitive data.
  • Unintended Exposure: Even unused functions within a file tagged with use server become accessible. If an internal function is mistakenly exported, sensitive information might be exposed.

The risks revolve around server-side functions in React, which can be inadvertently exposed to users if authentication checks are missed. Therefore, it's crucial to understand the dangers and implement React security best practices. More insights into how 'use server' works in Next.js can help mitigate some of these risks and maintain secure server actions.

For further reading on securely using server-side components, check out Next.js security tips in related Next.js use server discussions.

How does 'use server' handle data and context?

The use server directive in React simplifies the handling of data and context by embedding necessary values within the server-side functions. When dealing with dynamic data, like random numbers, use server can embed the specific values into the form, ensuring consistency and accuracy when functions are called.

Example:

Here's an example to illustrate this:

// Component rendering with a random number
const randomNumber = Math.random() * 100;

// Server-side function with `use server`
async function handleSubmit() {
  useServer();
  console.log(`Server received: ${randomNumber}`);
}

In this case, the random number is generated on the server and embedded in the form's submission context. When the form is submitted, the correct number is logged, maintaining context.

Hidden Data:

When handling lists or forms with item-specific actions (like deleting an item), use server helps by embedding item IDs as hidden data fields in the form. This allows for concise action execution, ensuring the correct context:

// List item rendering
items.map(item => (
  <form action={async () => {
    useServer();
    console.log(`Deleting item with ID: ${item.id}`);
  }}>
    <button type="submit">Delete</button>
  </form>
));

// Output: Correct item ID on server-side

In essence, use server abstracts the intricacies of embedding and maintaining context, enabling developers to focus on React security best practices without losing the integrity of data. Learn more about ensuring safe data handling in Next.js security tips.

What are the best practices for using 'use server'?

To safely implement 'use server' in React, follow these recommended best practices:

  • Authenticate all actions: Ensure that each server-side function includes authentication checks to validate user permissions.

    import { headers } from 'next/server';
    export async function secureAction() {
      const userHeader = headers().get('secured-user');
      if (userHeader !== 'expected-value') throw new Error('Not authorized');
    }
    
  • Avoid unnecessary exports: Only export functions that are essential. Avoid exporting internal functions that handle sensitive data.

  • Create a dedicated data folder: Organize server-side functions in a designated folder. This helps signify that these functions need careful handling and review.

  • Ensure thorough code reviews: Mandate code reviews for files containing use server. Check for proper authentication and validation in all exported functions.

  • Use ESLint plugins: Employ ESLint plugins to disallow inappropriate use of use server at the top level, preventing unintended exposures.

  • Incorporate header-based security checks: Implement checks using headers to verify user credentials before allowing execution of server-side actions.

  • Treat use server files as API endpoints: Acknowledge that server functions tagged with use server are potential endpoints. Apply the same security scrutiny as you would for other APIs.

By adhering to these best practices, you can significantly enhance the security and robustness of your server-side functions in React. For more Next.js security tips, ensure you're utilizing the latest safe practices.

How to secure 'use server' functions?

Securing use server functions in React is crucial to prevent unauthorized access and data leakage. Here’s how to do it effectively:

Authentication with Headers

You can use headers to authenticate users before allowing server-side actions. This ensures only authorized users can execute sensitive operations.

import { headers } from 'next/server';

export async function secureAction() {
  useServer();
  
  const userHeader = headers().get('secured-user');
  
  if (userHeader !== 'expected-value') {
    throw new Error('Not authorized');
  }

  // Proceed with your server-side logic here
}

Ensure Proper Exporting

Avoid exporting functions that handle sensitive data unless absolutely necessary. Keeping internal functions unexported reduces the risk of exposure.

Use ESLint Plugins

Leverage ESLint plugins to prevent the top-level use of use server, which can unintentionally expose functions as endpoints. For example, the ESLint plugin by CJ disallows inappropriate use server usage.

Dedicated Data Folders and Code Reviews

Organize server-side functions into a dedicated folder. This signals to developers and code reviewers that these files require careful handling. Ensure thorough code reviews focusing on proper authentication and validation for all exported functions.

By incorporating these practices, you can fortify your server-side functions in React, ensuring they remain secure and dependable.

For more detailed insights, you can check out Next.js security tips.

Can 'use server' be integrated with tRPC?

Integrating use server with tRPC can significantly enhance security and standardization for your server-side actions. tRPC offers a robust way to validate inputs and ensure that server logic remains secure and reliable.

Example:

First, define a server action procedure using tRPC:

import { initTRPC } from '@trpc/server';

const t = initTRPC();

export const createPost = t.procedure
  .input({
    title: 'string',
  })
  .mutation(async ({ input }) => {
    console.log(`Creating post with title: ${input.title}`);
    return { success: true };
  });

Next, bind this procedure to a form in your React component:

function CreatePostForm() {
  const createPostAction = trpc.createPost.useMutation();

  const handleSubmit = (event) => {
    event.preventDefault();
    const title = event.target.elements.title.value;
    createPostAction.mutate({ title });
  };

  return (
    <form onSubmit={handleSubmit}>
      <input name="title" type="text" required />
      <button type="submit">Create Post</button>
    </form>
  );
}

Benefits of using tRPC with server actions:

  • Validation: Automatically validates inputs.
  • Security: Ensures server-side code is executed securely.

For more details on improving security with server components, check out Next.js security tips.

FAQs

Loading related articles...