React's most dangerous feature

Theo - t3β€€gg
3 Sept 202426:37

Summary

TLDRThe video script discusses the security implications of using top-level 'use server' in Next.js applications, highlighting the risk of unintentional data exposure through inadvertently created endpoints. It emphasizes the importance of understanding 'use server' and 'use client' functions, and the need for authentication checks on server-side functions exposed to clients. The script advocates for careful code reviews, especially when dealing with server actions, and suggests strategies like using a 'data' folder for actions and enforcing code owner rules for reviews. It also touches on the use of server actions with tRPC for added security and ergonomics.

Takeaways

  • πŸ”’ Using `use server` in Next.js can expose functions as endpoints, which can lead to accidental data leaks if not handled properly.
  • ⚠️ Accidental export of functions can create unintended endpoints, potentially leading to security vulnerabilities.
  • πŸ›  It's crucial to deeply understand the tools and frameworks used, as their implications might not be immediately obvious.
  • πŸ”„ `use server` allows client-side code to invoke server-side functions, blurring the line between traditional client-server interactions.
  • πŸ“š Educating developers on the implications of `use server` is important to prevent misuse and ensure security.
  • πŸ”— When using `use server`, any exported function within its scope becomes accessible as an API endpoint, increasing the attack surface.
  • 🚫 It's recommended to avoid exporting sensitive functions and to implement security checks on all exported functions.
  • πŸ”‘ Authentication and permission checks are essential for any server-side function exposed to the client via `use server`.
  • πŸ›‘οΈ Code reviews should be rigorous when it comes to files using `use server`, ensuring that all endpoints are secure and intentionally exposed.
  • πŸ’‘ Using `use server` with tools like tRPC can provide a hybrid solution that combines the benefits of server actions with robust authentication mechanisms.

Q & A

  • What is the main concern raised about using top-level `use server` in Next.js applications?

    -The main concern is that top-level `use server` can inadvertently create endpoints for all exported functions, which might lead to accidental data exposure if not handled properly.

  • Why might developers unintentionally expose sensitive data with `use server`?

    -Developers might unintentionally expose sensitive data if they export a function that was intended for internal use without realizing that `use server` treats all exports as endpoints accessible by the client.

  • What is the recommended practice to prevent accidental exposure of sensitive functions with `use server`?

    -The recommended practice is to ensure that any function marked with `use server` that should not be exposed has an authentication check in place and is not exported directly.

  • How does `use server` work differently from `use client`?

    -`use server` exposes functions as server-side endpoints that can be accessed by the client, whereas `use client` is used for client-side logic and does not expose functions as endpoints.

  • What is the significance of the `async` keyword in the context of `use server`?

    -The `async` keyword is used to define asynchronous functions that can perform server-side operations and are exposed as endpoints via `use server`.

  • Why is it important to understand the context in which `use server` is used?

    -Understanding the context is crucial because `use server` embeds the server-side function's context into the client-side form, allowing it to access data that is available at the time of rendering.

  • How can `use server` be used to create a delete button that is secure and maintains clean architecture?

    -By using `use server`, a delete button can be created with its own behavior, passing the necessary context (like an item ID) to the server-side function, ensuring that the function can be authenticated and secure.

  • What is the potential issue with exporting functions from a file marked with `use server`?

    -The potential issue is that any function exported from a `use server` marked file, even if not directly used in the client-side form, is exposed as an endpoint and can be accessed by the client.

  • How can developers ensure that only intended actions are exposed as endpoints in a Next.js application?

    -Developers can ensure that only intended actions are exposed by conducting thorough code reviews, using lint rules to ban top-level `use server`, and placing actions in a dedicated folder with strict review guidelines.

  • What is the role of code reviews in preventing security issues related to `use server`?

    -Code reviews play a critical role in ensuring that all functions exposed via `use server` are properly authenticated and that sensitive data is not inadvertently exposed.

Outlines

00:00

🚨 Security Risks of Top Level useServer in Next.js

The paragraph discusses the potential security risks associated with using top-level useServer in Next.js applications. It emphasizes the importance of understanding how useServer works, as it can inadvertently expose server-side functions as endpoints if not used carefully. The speaker agrees with concerns raised about data leakage and suggests a deep understanding of the tools is necessary. The paragraph includes a demonstration of how useServer can embed data from the server into client-side forms and how this can be both powerful and risky if not handled correctly.

05:02

πŸ”’ Best Practices for useServer in Next.js

This section highlights best practices for using useServer in Next.js applications to maintain security. It stresses the need for authentication checks on any exported functions that are marked with useServer. The speaker recommends exporting a separate function that internally calls the sensitive function after performing security checks. The paragraph also discusses the importance of code reviews to ensure that only intended functions are exposed as endpoints and suggests using internal functions that are not exported to avoid accidental exposure.

10:02

πŸ›  The Unintuitive Nature of useServer Exports

The paragraph delves into the unintuitive aspects of exporting functions with useServer, explaining that even functions not explicitly used can be exposed as endpoints if they are part of the import tree for a route. It demonstrates how the Next.js compiler treats useServer-tagged files as a collection of endpoints and how this can lead to accidental exposure of sensitive data. The speaker advises on the importance of understanding the compiler's behavior and conducting thorough code reviews to prevent such issues.

15:03

πŸ”‘ Implementing Access Controls with useServer

This section focuses on implementing access controls when using useServer. It discusses the importance of verifying user permissions before allowing access to server-side functions exposed by useServer. The speaker provides an example of how to check for a secure user header to ensure that only authorized users can execute certain actions. The paragraph also touches on the broader topic of access control in web development and how useServer requires developers to think more critically about securing their endpoints.

20:05

πŸ€” The Responsibility of Developers Using useServer

The paragraph emphasizes the responsibility of developers when using useServer, equating the exposure of functions as endpoints to the creation of API endpoints that require careful management and authentication. It points out that web developers may not be accustomed to handling access control for API endpoints and suggests that a shift in mindset is necessary. The speaker also mentions the importance of code reviews and the establishment of secure patterns for data exposure on the server side.

25:06

πŸ›‘οΈ Solutions and Conclusions on useServer Security

In the final paragraph, the speaker concludes with solutions and recommendations for securing useServer endpoints. It suggests using lint rules to prevent top-level useServer, conducting thorough code reviews, and establishing strict repository rules for changes in data-exposing folders. The paragraph also mentions the possibility of using tRPC with Next.js server actions for an added layer of security and standardization. The speaker reiterates the importance of treating useServer as a means of exposing endpoints that require authentication and secure handling.

Mindmap

Keywords

πŸ’‘Next.js

Next.js is a popular React framework that enables functionality such as server-side rendering and generating static websites. In the context of the video, Next.js is used to demonstrate the use of server components and how they can be a source of potential security vulnerabilities if not handled correctly.

πŸ’‘use server

'use server' is a function in Next.js that marks certain functions to be executed on the server side. The video script emphasizes the importance of understanding the implications of using 'use server', as it can inadvertently expose server-side functions to the client side, leading to potential data leaks.

πŸ’‘Data Leakage

Data leakage refers to the unintended disclosure of sensitive information. The video discusses how improper use of 'use server' in Next.js can lead to data leakage by creating endpoints for all exported functions, which might include sensitive data that should not be exposed.

πŸ’‘Endpoints

Endpoints in web development are access points for clients to interact with server-side resources. The video script uses the term to illustrate how 'use server' inadvertently creates endpoints for all functions, which can be a security risk if sensitive functions are accidentally exposed.

πŸ’‘Authentication

Authentication is the process of verifying the identity of a user or device. The script suggests that any function exposed as an endpoint via 'use server' should have an authentication check to ensure that only authorized users can access sensitive data or functions.

πŸ’‘Code Review

Code review is the process of examining source code by one or more peers to detect errors, improve code quality, and ensure adherence to coding standards. The video emphasizes the importance of code review, especially for files using 'use server', to ensure that all exported functions are properly secured.

πŸ’‘Environment Variables

Environment variables are external variables that are used to configure the application. In the script, environment variables are mentioned as a way to store sensitive data, which should not be exposed through server endpoints created by 'use server'.

πŸ’‘GraphQL

GraphQL is a query language for APIs and a runtime for executing those queries against your data. The video script compares the issues with 'use server' in Next.js to those found in GraphQL, where security concerns arise from the exposure of schema and the need for careful access control.

πŸ’‘TRPC

TRPC stands for 'Type-Safe Remote Procedure Call' and is a framework for building APIs in a type-safe way. The video mentions TRPC as a solution for combining the benefits of server actions in Next.js with robust authentication and type safety.

πŸ’‘Access Control

Access control is a security technique that regulates who or what can view or use resources in a computing environment. The video script discusses the importance of access control in the context of server-side functions exposed by 'use server', to prevent unauthorized access to sensitive operations.

πŸ’‘Code Linting

Code linting is the process of running a program that will analyze source code for programming errors, bugs, stylistic errors, and suspicious constructs. The video suggests using linting tools to enforce rules, such as banning top-level 'use server', to prevent accidental exposure of sensitive functions.

Highlights

Top-level use server in Next.js can inadvertently create endpoints for all exported functions, potentially leading to data leaks.

Use server allows client-side code to hit server-side functions, which can be powerful but requires careful handling.

Functions marked with use server are treated as endpoints, which can be called by client-side code.

An accidental export of a function can lead to significant security risks if it's not intended to be an endpoint.

Use server can embed context like variable values into forms, allowing server-side functions to access them.

Composition of actions with use server can lead to clean and maintainable code architecture.

Sensitive data should never be exported directly; instead, create a wrapper function that includes security checks.

Code reviews are critical for ensuring that only authenticated and intended functions are exposed as endpoints.

Exporting a function with use server means that it is exposed as an endpoint, even if it's not directly used in the client code.

It's important to understand the implications of use server before using it to avoid unintentional security vulnerabilities.

Use server should be used with caution, especially when dealing with sensitive data and authentication.

The concept of data access layer is introduced to handle secure data exposure on the server.

TRPC can be used alongside Next.js server actions to provide authentication and standardization.

It's suggested to have strict code review rules for files that use use server to ensure security.

The use of a lint rule to ban top-level use server can prevent accidental exposure of endpoints.

The importance of treating server actions with use server as API endpoints that require authentication is emphasized.

The video concludes with a discussion on the responsibility of developers when using server actions and the need for security awareness.

Transcripts

play00:00

if you care about security for your

play00:01

nextjs app stop using top level use

play00:03

server it's way too easy to leak data

play00:06

top level use server creates endpoints

play00:08

for all exported functions even if

play00:10

they're never used on the client one

play00:12

accidental export can cause a ton of

play00:15

damage oh boy y'all probably expect me

play00:18

to come out here super defensive like

play00:20

I'm the forell guy right they pay me so

play00:23

I'm going to come out and just say this

play00:24

is all fine this video is not sponsored

play00:26

for a sell has no saying what I say here

play00:28

they might even be mad at me for as I'm

play00:30

going to say because I almost entirely

play00:32

agree with Reese this is scary and we

play00:35

should understand what these tools do in

play00:37

a deep way because it's not as obvious

play00:40

as one might think let's dive into this

play00:42

and what's going on because I think it's

play00:44

important for us to understand before we

play00:46

go any further I want to make sure we

play00:48

understand what US server does because

play00:50

us server does some slightly different

play00:52

things in slightly different ways

play00:54

there's some education to be had here so

play00:56

we're going to make a form and in this

play00:58

form we'll have a button type A submit

play01:01

and the form will have an action which

play01:04

this is where actions become useful

play01:06

we're going to go in here we're going to

play01:07

mark this is use server and we're going

play01:09

to console.log I'm on the server my

play01:13

mistake async cool so now we have here

play01:16

I'll open up the console because when I

play01:18

hit this window's not defined because

play01:21

that code ran on the server not on the

play01:22

client so if I go back and delete window

play01:25

and run this

play01:26

again that code runs on the server see

play01:29

I'm on on the server what used server

play01:32

does is it tells the nextjs compiler hey

play01:36

this function isn't a traditional

play01:38

function this isn't just for you to call

play01:40

between different places in the same

play01:41

service use servers effectively a door

play01:44

that enables client side code to hit

play01:46

this serers side function it might make

play01:48

more sense if I break it out a little

play01:50

here so if I was to async function

play01:53

handle

play01:56

submit we can do the same thing in here

play01:58

with the use server and then I pass this

play02:00

handle submit function to the action

play02:02

instead and it does the same thing

play02:05

things can get a little interesting once

play02:07

we're composing stuff though so first

play02:10

off if I Define this function in here

play02:12

let's say there's a variable that's

play02:13

defined when the page renders like con

play02:15

some number equals math. random time 100

play02:19

now we have this number we want to log

play02:22

it so we put that there and now when we

play02:24

submit and we go to the console code I'm

play02:26

on the server here's the random number

play02:28

since this comp component has context

play02:31

not literally react context but it has

play02:34

the context of this number that's unique

play02:35

to this render because theoretically

play02:37

every time somebody renders this page

play02:39

they're going to get a different number

play02:40

we can prove that by saying

play02:43

console.log rendering with some number

play02:46

so now when I load this page it rendered

play02:49

with 3.9 whatever and then if I submit

play02:52

it's submitting with 3.9 the reason it's

play02:54

able to do that is it's actually

play02:56

embedding that number that value that we

play02:58

have in the closure here inside of the

play03:00

form so that when it calls this function

play03:03

it has access to that data it's almost

play03:05

like we we moved this out here and then

play03:08

in here we have an input that has a

play03:10

default value that is some

play03:13

number the reason that this works this

play03:16

way is so we don't need to know the

play03:18

context of what existed in the component

play03:20

when you call that function at first it

play03:23

wasn't encrypted which made this

play03:24

horrifying but now I actually kind of

play03:26

like it I have these items and I want to

play03:29

be able to list them we've all done this

play03:30

before items. map item thank you super

play03:33

Maven for being based as always so here

play03:36

we have items. map item key item id item

play03:40

cool and if we go back to the page 1 2 3

play03:43

here's where things get interesting what

play03:44

if I want to be able to delete any one

play03:46

of these if I want a little delete

play03:48

button on the side here this is where I

play03:50

actually think the composition of

play03:51

actions gets really

play03:53

cool submit uh we're going to make this

play03:55

an x button also going to class name

play03:59

flag X Gap 4 just put space between them

play04:02

so I have that little x button nice

play04:04

action

play04:06

equals async use server and since we

play04:10

have the context of what id we have here

play04:13

it's actually quite easy I should also

play04:14

make this a let just to make it easier

play04:16

or items equals items. filter and then

play04:20

revalidate path Che to import cool so

play04:24

what's happening here is since the ID of

play04:27

this item exists in the props being

play04:30

passed to this component it's actually

play04:32

included in the scope of the form so

play04:34

that we can just call the ID we passed

play04:36

to this component it's actually magical

play04:38

in the sense that we have this simple

play04:39

items function this could be a row and a

play04:41

table anything like

play04:43

that this is so so so convenient because

play04:47

now I can just dump you server here I

play04:50

can call props the same way I would in

play04:52

any other function in here and this just

play04:54

behaves so if I go here we can take a

play04:56

look at these items and you'll see each

play04:58

of these forms has some data embedded in

play05:01

it or it should yeah so each of these

play05:04

has this hidden data and that hidden

play05:06

data is just the ID for the thing that's

play05:09

being incl included in the scope there

play05:11

so now if I hit delete on any of these

play05:14

it removes it it's that simple and that

play05:16

is a magical pattern it makes enclosures

play05:18

of concerns and the architecture of your

play05:20

stuff really really clean if you have

play05:22

let's say a table and you want a delete

play05:24

button that you just pass a prop to now

play05:27

that delete button can own Its Behavior

play05:29

the catch is that in order for that to

play05:31

work it has to include the context of

play05:34

what that has whatever is enclosed in

play05:37

that functions like closure inside of

play05:39

the form but it does it encrypted so you

play05:41

can actually see the value so although

play05:43

this might seem scary this actually

play05:45

pretty safe where do I think this

play05:46

problem could actually happen is an

play05:48

important question I should probably

play05:49

have answered earlier so I'm going to

play05:50

answer it now let's say that the

play05:51

sensitive data function isn't actually a

play05:53

do not use it's an internal so we want

play05:55

to use this internally notice that I

play05:57

deleted the export the reason is

play05:59

anything doing stuff internally like

play06:01

this we don't want exported we

play06:03

explicitly don't want this to be

play06:04

accessed other places so the solution

play06:06

here would be to export a different

play06:08

function that calls this so we have

play06:10

export async function user access data

play06:15

and here we grab the headers we check

play06:17

that it's a secured user and then we

play06:19

call the internal sensitive data

play06:20

function anything that has the word

play06:22

export should do a security check like

play06:24

this the thing to look for if you're a

play06:26

code reviewer working in code bases like

play06:27

this is making sure anytime an export in

play06:30

an action file or in something marked

play06:32

you server that everything exported has

play06:34

an authentication check of some form and

play06:37

if it's not exported it doesn't matter

play06:39

this isn't exposed only the exports are

play06:41

exposed the way that this would become a

play06:44

mistake the way this could potentially

play06:45

be an issue is if I'm a Dev working on

play06:48

another file here like other actions. TS

play06:51

and I need this internal sensitive data

play06:53

thing I might blindly go here and Export

play06:56

it so I have access to this in another

play06:57

file and now I've just made that an end

play06:59

Point even if I'm using it internally

play07:01

for something like this that's the scary

play07:03

unintuitive part that is going to cause

play07:05

issues if you're looking for where the

play07:06

problem exists that's where the problem

play07:08

exists if you blindly go export

play07:11

something that shouldn't be exported and

play07:12

then that's missed in code review that's

play07:14

a red flag that can cause problems

play07:16

that's a Thing Worth looking for but

play07:18

that's the only case I can imagine where

play07:20

this would be a problem is doing

play07:23

something is inocent as exporting this

play07:24

function that we're using internally

play07:26

here exposing this so the way I would do

play07:29

this is is I would have something like a

play07:31

utils function so utils TS I might even

play07:34

put a comment on top that's like do not

play07:36

export this file and then import server

play07:39

only now we're sure this file is only on

play07:42

the server and won't be used servered

play07:44

not use server this file now we're

play07:46

certain that won't happen now I can call

play07:48

this internal function in other places

play07:50

and even though it's exported it's not

play07:52

exposed because it's exported from a

play07:53

file that doesn't have us server I hope

play07:56

that makes it clear where the problems

play07:58

could be here and what could cause this

play08:00

but I promise you this is just the

play08:02

graphql problem of security again I just

play08:05

wanted a long tangent because I want to

play08:06

show you guys that this isn't actually

play08:08

what we're talking about today if I

play08:10

wanted to move this out let's say I

play08:13

wanted this delete button in another

play08:15

file because separation of concerns is

play08:17

super important we'll make an actions.

play08:20

TS file and here well I guess I can't

play08:22

really use items because items was in

play08:24

the context of that so let's come up

play08:25

with a different example let's say

play08:27

instead of screwing with items it will

play08:29

just I don't know

play08:32

console.log user was here we'll make

play08:35

this more proper export async function

play08:39

or user visited cool something like that

play08:43

so now you have this export async

play08:44

function we could put the use server

play08:45

here but I don't think we're able to

play08:47

import that I'm actually curious if this

play08:50

works or

play08:51

not okay that does work cool so if you

play08:55

want to put a function different file

play08:56

Market your server this way and Export

play08:58

it that all works fine the other way you

play09:00

can do this though is putting the use

play09:01

server up top like this so now we have

play09:03

this export async function user

play09:06

visited and if we go here and click the

play09:09

buttons user was here user was here that

play09:12

all works the Ed server on the top here

play09:15

is effectively telling the compiler hey

play09:17

any Asing functions in this file should

play09:19

be treated the same way an action is

play09:21

treated in an existing file so when we

play09:23

put the US server up here we are telling

play09:25

react hey these functions can be called

play09:27

like end points these can be post Ed to

play09:30

which is an important detail here I want

play09:32

to be clear though you can't just write

play09:34

this in a client file so I can't like

play09:36

have use client up top and then use

play09:39

server in here because use client is

play09:41

saying we're sending this Javascript

play09:43

file to the client use server is saying

play09:45

we're exposing this function to the

play09:47

client so they're different things

play09:49

entirely I know it seems like they're

play09:50

similar because their names are so

play09:51

similar they're not so where's the

play09:54

danger here well sometimes when you're

play09:56

exporting from a use server file the

play09:58

behaviors aren't very intuitive the

play09:59

first one is let's say that we wanted to

play10:01

do the items thing again so we'll Define

play10:03

items in here and we'll export it too

play10:06

well now we're going to get an error

play10:07

because you can't actually export

play10:09

anything but a promise from a use server

play10:11

tagged file because they don't want you

play10:13

to use this to expose things like data

play10:15

like classes like components they want

play10:17

you to expose this as functionality as

play10:20

endpoints effectively so what is the

play10:24

issue here then if we can't export

play10:25

things we shouldn't and we can't export

play10:27

functions what's the problem well let's

play10:28

say there's a fun function in here we're

play10:30

not ready to export like sensitive data

play10:33

we'll even do uh do not use sensitive

play10:37

data and in here we'll log let's go make

play10:39

a fake environment EnV sensitive data

play10:44

equals I hope this doesn't leak so now

play10:47

we have this sensitive thing in the

play10:48

environment we can even log it here env.

play10:51

sensitive what did I call sensitive data

play10:54

process. sensitive data so if we go here

play10:58

we're not going to like have that get

play11:00

hit or anything like we aren't running

play11:02

that function I'll change this to

play11:05

sensitive do not touch and maybe we even

play11:08

return that sensitive data process. EnV

play11:11

do sensitive data so now we have this

play11:13

we're returning this sensitive data but

play11:15

we're not binding it anywhere we're not

play11:16

calling this we import the the user

play11:19

visited function but we're not importing

play11:21

this so what's the issue well the issue

play11:24

is that the way the compiler works is

play11:26

for every route it goes through all of

play11:29

the imported files to see in order where

play11:33

the U server calls are the way that this

play11:35

effectively works is when the compiler

play11:38

runs it goes through each of the

play11:40

different places that a U server exists

play11:42

in the file tree for that route so if

play11:45

this route has an import to actions and

play11:48

in here we have two things that are use

play11:51

server tagged this would be the

play11:52

equivalent of effectively putting this

play11:54

here and here basically the same thing

play11:56

so now this is id1 this is ID 2 even

play11:59

though this one isn't used it is still

play12:01

exposed and it is still creating an

play12:04

endpoint so to speak for that function

play12:06

anytime you have a use server function

play12:09

that is imported and in the import tree

play12:11

of a given route it is now exposed as an

play12:13

endpoint even if you don't directly bind

play12:15

it because what this is effectively

play12:17

doing is letting you post to that page

play12:20

route and have it run the code that

play12:22

you've bound I wanted to confirm that if

play12:24

you don't import the file on a given

play12:26

page this won't be a problem which is

play12:28

the case so this page demo doesn't

play12:30

import any actions so it doesn't have

play12:32

this the actions that are defined on a

play12:34

route are specific to that route so we

play12:36

did import the actions file here so

play12:39

these actions are included on the root

play12:41

slash route but on the SL demo route

play12:44

they're not included because I didn't

play12:45

import any actions here so what do I

play12:47

mean by included that's a great question

play12:49

and we're going to elaborate on that now

play12:51

so here we have that root route again we

play12:53

have the buttons that don't do anything

play12:55

at the moment and if we take a look at

play12:57

the code for these well actually see if

play13:00

I search that again that we have some

play13:03

stuff here that includes it specifically

play13:04

this page JS file very interesting let's

play13:07

grab the contents of that quick in here

play13:10

if I look for that ID which I'll grab

play13:12

again here remember all of the actions

play13:14

have an ID assigned to them so that the

play13:16

react code on the server knows which

play13:19

action each form is attached to because

play13:21

all of them are just posting to the same

play13:22

endpoint so they're using these IDs to

play13:24

figure out which action you actually

play13:26

want to run and here we see oh no do not

play13:29

use sensitive data it's actually

play13:31

included in here that's scary what's

play13:33

even scarier is that we can hit that you

play13:36

know what let's just do this as a curl

play13:37

why not I'm going to delete the cookie

play13:39

because we almost certainly don't need

play13:40

that what we do need is to identify the

play13:42

action just pasted the curl and here we

play13:45

get a response undefined if we were to

play13:47

go add a response to the action uh

play13:50

return cool data hello world now we get

play13:53

back data hello world as the response

play13:55

from that curl call reminder this curl

play13:58

call is just me going to the browser

play14:01

rightclick copy as curl for that request

play14:04

that I did so what happens if we swap

play14:06

that ID well I already have the command

play14:07

where I swapped it you can see the 6dd

play14:09

here so we're going to run this instead

play14:11

oh subscribe to Theo what happened oh

play14:15

sensitive data got changed hopefully

play14:17

you're already subscribed or you're

play14:18

going to deal with leaks like this in

play14:19

the future make sure yourself secure hit

play14:20

that sub button anyways the issue here

play14:23

is very simple but also easy to miss if

play14:26

you're not paying enough attention when

play14:27

you write these things since us server

play14:28

is effectively saying everything in this

play14:31

file is now exposed as an endpoint this

play14:33

is exposed even if we're not using it we

play14:36

got a good point here from Emmy in chat

play14:39

is it really a bad thing to have someone

play14:40

able to enumerate your end

play14:43

points that's a really good question it

play14:45

depends on how your stuff's architected

play14:47

this is effectively the graphql problem

play14:50

where if you have anything in your

play14:51

graphql schema every user can at least

play14:54

call it the solution to a problem like

play14:56

this is to have an off check of some

play14:58

form we have have data from this call

play15:00

being hit and we can use that data to

play15:03

verify that this user is the right user

play15:05

we have access to headers so if I import

play15:08

headers from next I don't know what do

play15:10

we want to have is the header we'll say

play15:12

that there's a secure header that we'll

play15:14

check for if we won't do X forwarded 4

play15:17

we'll say secured user does not equal

play15:23

key then we'll immediately throw error

play15:25

not a secured user otherwise we'll pass

play15:27

it so if we do a check like this and I

play15:29

go back here and I call this again we

play15:31

get this error response because this

play15:33

error was thrown to us because we

play15:35

shouldn't have been able to do that and

play15:36

you can see the error in that response

play15:37

of not a secured user but if I instead

play15:40

just return data you're not a secured

play15:42

user same deal data you're not a secured

play15:44

user if I go back to that curl request

play15:47

and we manually happen to know what that

play15:49

header is secured user and now we have

play15:52

that in here if we run this again data

play15:55

subscribed to Theo because we are

play15:56

actually validating that this user has

play15:58

permit in this case using the secured

play16:00

user header but you could use anything

play16:02

like cookies the same way so the lesson

play16:05

here is first off make sure you

play16:08

understand what these things do before

play16:09

you use them use server is turning all

play16:11

of these functions into endpoints that

play16:13

users can hit which emphasizes the

play16:15

importance of one big key thing make

play16:18

sure any action you create verifies the

play16:21

user has permission to do the thing if

play16:23

you don't want users to hit this

play16:25

sensitive data function make sure you

play16:27

check because effectively every user

play16:28

user can hit this function when you're

play16:30

exposing things with us server you have

play16:32

to assume that this function can be run

play16:34

by anybody I think a lot of the issue

play16:37

that's happening here is that web

play16:39

developers aren't used to dealing with

play16:41

Access Control they're used to these

play16:42

functions being automatically handled

play16:44

with off because a backend Dev did it or

play16:47

they're using something like crate T3

play16:48

app where we handle it for you with

play16:49

secured procedures if you're writing

play16:51

these us server calls yourself that

play16:53

means you're writing API endpoints

play16:55

yourself we are defining an API here the

play16:57

same way we would graphql in fact

play16:59

graphql has these same problems times 10

play17:02

so we need to make sure in all of these

play17:04

functions that the right people can do

play17:06

them and as a code reviewer as a in the

play17:08

no Dev that's watching videos like this

play17:11

this is kind of your responsibility

play17:12

we're no longer just writing JavaScript

play17:14

for the client if we are using these

play17:16

functions if we're taking advantage of

play17:17

these core features in the new react

play17:19

model it is important for us to go out

play17:21

of our way to make sure anything we're

play17:23

exposing to users is secured and I'll be

play17:26

honest this even bit us before when we

play17:27

first did the early early access of

play17:29

upload thing we weren't authenticating

play17:31

our actions well enough because we

play17:33

assumed if you can't get to the route

play17:34

you can't use the actions which was a

play17:35

mistake we learn these lessons as a

play17:36

community and I really want to push

play17:37

these points so I'm not the only one

play17:39

getting burnt by it if you're not

play17:41

authenticating the functions then anyone

play17:43

can hit them Ryan Florence jumped in

play17:44

with a spicy take and honestly I think I

play17:46

agree this doesn't concern me use server

play17:49

means turn these exported functions into

play17:50

endpoints for the client once you know

play17:52

that you won't export a function with

play17:54

sensitive data I get that when you're

play17:55

new to it you might export a function

play17:56

you didn't intend to be an endpoint yep

play17:58

and and then it's up to the framework to

play17:59

salt and sign the IDS it sends the

play18:01

client just like sign cookies yada yada

play18:03

I think it's cool great abstraction

play18:04

would you really trust a code base with

play18:05

a thousand people working on it for all

play18:07

of them to not make this mistake uh yeah

play18:09

I personally think once you have a code

play18:11

base of that size you're almost

play18:12

certainly doing a really good job of

play18:14

auditing and linting these things like

play18:16

my personal understanding is that these

play18:19

problems always exist in big code bases

play18:21

especially with again I hate to keep

play18:23

poking at graphql but it is so guilty of

play18:26

this problem so the graphql schema like

play18:28

this where we have

play18:29

not only do you have to verify and

play18:31

validate company you have to validate

play18:33

every one of these fields because let's

play18:35

say some users have access to name and

play18:37

address but not offices some users have

play18:39

access to offices some users have access

play18:41

to specific offices now not only does

play18:44

every type every object have to be

play18:47

handled like with authentication every

play18:50

single field needs to be validated this

play18:52

is the reality of backend Dev you always

play18:55

have to check permissions when you do

play18:57

anything and the that's unintuitive for

play19:00

web devs is that use server means that's

play19:02

an endpoint that's an endpoint all of

play19:05

these are new API endpoints being

play19:06

exposed the risk here isn't so much this

play19:09

is a unique problem that doesn't exist

play19:11

in other places and only exists in react

play19:13

and server components and next the

play19:15

problem here is that web developers used

play19:17

to using next purely for frontend stuff

play19:19

and then hitting an API that someone

play19:21

else made with all of these off things

play19:23

already handled they're diving into you

play19:24

server and treating it the same way they

play19:26

would hit an external endpoint like to a

play19:29

Dev that's not familiar and is used to

play19:31

just writing these things with an

play19:32

existing backend this to them feels like

play19:34

the equivalent of I don't know let's say

play19:37

we have an async function here it's not

play19:39

used client and we have some data const

play19:42

data equals await Fetch api. ours.com

play19:48

sdata and they use this data for

play19:50

something to a new Dev using these

play19:53

things this feels the same as calling a

play19:55

server action to get that data they're

play19:57

not used to thinking about about API end

play20:00

points as a thing they have to own

play20:02

manage and authenticate ree is pushing

play20:04

back a little bit saying the focus on

play20:06

auth informs is the wrong takeaway

play20:07

because you can't put off on things you

play20:09

don't know exist half agree I think that

play20:12

the our duty as devs is the same way if

play20:15

we have I don't know here where we're

play20:18

exposing all of these things through the

play20:20

switch statement we're just taing back a

play20:23

bunch so when you use us server you're

play20:25

just doing this but you've deleted that

play20:29

that and

play20:30

that it's still the same thing but it's

play20:33

important to understand that when you do

play20:36

use U server you're exposing all of

play20:38

these things as end points and ideally

play20:41

you're auditing these things as well

play20:42

what a surprise CJ already made an es

play20:44

link plugin which disallows top level

play20:46

use server so if you don't want this

play20:48

feature if you don't want this risk you

play20:50

can use this plugin to guarantee Nobody

play20:52

Does it and even use this plugin and

play20:54

then override it in certain files saying

play20:56

okay these files can do it but others

play20:58

can't these are the issues yeah also 3

play21:00

minutes ago great job CJ for those who

play21:02

don't want this you now have a lint rule

play21:04

to ban it really cool the same way that

play21:07

this exists with basically any backend

play21:09

technology code review is really

play21:11

important if there's a file that has a

play21:13

used server on top you need to make sure

play21:15

every function exported is also

play21:17

authenticated very very important and

play21:20

it's a common mistake for that to not be

play21:22

the case but I see this more in graph

play21:24

CUO than I do in react just make sure

play21:26

when you're exposing end points that

play21:28

you're making sure the user should be

play21:30

using that endpoint there's a great blog

play21:32

post by Sebastian markb the wizard who

play21:34

came up with a lot of the server

play21:35

component patterns over at for sell how

play21:37

to think about Security in next and I

play21:38

feel like I reference this blog post far

play21:40

too often so it's important to bring it

play21:43

up again the data access layer is a

play21:45

really important concept that they push

play21:46

here where we have a bunch of things in

play21:49

a folder let's say data that are the

play21:51

things we want to have be secured data

play21:53

that can be accessed on the client so

play21:55

here we have this off TSX function

play21:56

should have just been TS but that's fine

play21:58

exports con get current user which is

play22:00

cached uses the off token cookie decodes

play22:02

it and now we have this user info now we

play22:05

have these functions can see username

play22:07

viewer user this is public info so we

play22:09

always return true we have can see phone

play22:11

number viewer is admin or the team that

play22:13

they're asking for is the viewers team

play22:16

get profile dto now we want to make sure

play22:19

that we don't pass back values they

play22:21

shouldn't have access to so we do this

play22:23

select we uh set user data to the first

play22:27

row here we grab the current user and if

play22:30

they can see this username then we call

play22:33

user data. username and we return that

play22:35

otherwise we return null and then same

play22:36

with phone number we're using these

play22:37

check functions that we wrote before we

play22:39

return and since we have this all in a

play22:41

specific folder that is indicated to our

play22:43

teams as this is our API this is our

play22:46

endpoints this is important and secure

play22:48

and needs to be treated carefully in

play22:50

code review we're much less likely to

play22:52

have issues because we've established a

play22:54

pattern of how data is exposed on our

play22:56

server so as is the case with any

play22:58

technology that is linking to your

play23:00

database and is exposing data it's

play23:02

important to make sure you have patterns

play23:04

for how you want to do that for how you

play23:05

want to expose that data and it might

play23:08

not be intuitive that putting used

play23:09

server at the top of the file means

play23:11

every export is now exposed once you

play23:13

know that you can treat these files

play23:14

properly so it's important that whenever

play23:16

you use a use server on top that you

play23:17

make sure each of these functions is

play23:19

checking to make sure the user should be

play23:21

doing the thing that they're doing one

play23:22

more quick thing I should have covered

play23:23

this a while ago you can use server

play23:25

actions with trpc so if you really want

play23:27

to lock this down set up trpc use all

play23:30

the things I showed earlier and now you

play23:31

can just expose those with actions the

play23:33

same way you could otherwise so we have

play23:35

here create post protected action. input

play23:38

yada yada mutation create post and now

play23:41

we can import this the same way we would

play23:43

any other server action and it just

play23:45

works you even bind it in a form the

play23:46

same way we were showing earlier so you

play23:48

get all of the authentication and

play23:50

standardization benefits of what exists

play23:53

within something like trpc while also

play23:55

getting the ergonomics the zero JS

play23:57

necessary all the other benefits that we

play23:59

get from server actions it's a really

play24:01

nice hybrid solution so if you want to

play24:02

have a good standard for the way you're

play24:04

doing this on the server side there you

play24:06

go the request that I've seen from Ree

play24:08

and others is that these be Tre shook so

play24:11

if this is exposed and this isn't

play24:14

because this isn't actually being used

play24:16

in the import ideally the ID for this

play24:18

isn't included that would be nice I

play24:22

would have concerns about how it's

play24:23

actually architected and set up but I

play24:25

think that should be fine the concern I

play24:28

would have is that just not exposing the

play24:30

ID doesn't solve the problem because

play24:32

theoretically if we only expose user

play24:35

visited in this route what's the point

play24:36

of this even existing it almost

play24:38

certainly is exposed somewhere so if we

play24:40

imported that one I don't know in here

play24:43

even if we don't actually use it we're

play24:44

just importing it I I guess the reason

play24:46

I'm hesitant to agree with the like tree

play24:50

shaking bit is if you're exporting

play24:51

something in one of these it's going to

play24:53

get used eventually so if we were to

play24:55

hide the ID of this function in the

play24:57

pages that don't have access to it so to

play25:00

speak it's going to leak eventually

play25:01

we're effectively just delaying the

play25:03

problem so the only actual solution is

play25:06

that we are good with you server means

play25:09

this thing needs to be authenticated and

play25:12

the solution for that isn't a lter it

play25:15

isn't a change to the way the compiler

play25:17

works the solution is very specific it's

play25:20

a code review thing and I find a lot of

play25:22

the problems that I've been hearing

play25:23

recently are assuming that anyone can

play25:25

commit any code the solution here is we

play25:27

have a data folder we put actions in

play25:29

here and you make sure anytime someone

play25:32

changes anything in the data folder that

play25:34

you have a code owner rule on the repo

play25:36

on GitHub making sure someone who

play25:38

actually knows what the is going on

play25:39

reviewed that that's the only way you

play25:42

can use anything that exposes data to

play25:44

users at scale make strict rules about

play25:46

who has to review things and now

play25:48

whenever I'm doing a code review even if

play25:50

I can't see the US server on top for

play25:51

some reason because the file's massive

play25:53

at the very very least I see that this

play25:55

file is in the data folder which means I

play25:57

need to treat it more carefully it needs

play25:59

to be more thoroughly reviewed and we

play26:00

need to make sure anything in here is

play26:02

exposing data in a way that is safe but

play26:04

that is the solution here it is put

play26:06

these things in a place where we know

play26:08

those are endpoints and then in COD R

play26:10

viiew make sure any endpoint that you're

play26:12

exporting here is treated accordingly

play26:13

otherwise you're doing the same thing I

play26:15

showed earlier with Express where we're

play26:16

just exporting functions within like the

play26:19

post calls without checking you always

play26:21

need to check things that a user can hit

play26:23

and if you're exporting asnc functions

play26:24

in a use server file please please

play26:26

please make sure that users are being

play26:28

authenticated one more simple solution

play26:30

that has been touched on don't export it

play26:32

as long as you're not exporting good let

play26:33

me know what you think and until next

play26:35

time peace nerds

Rate This
β˜…
β˜…
β˜…
β˜…
β˜…

5.0 / 5 (0 votes)

Related Tags
Next.js SecurityServer ComponentsData LeakageCode ReviewAuthenticationAPI EndpointsWeb DevelopmentReact PatternsCode AuditingSecurity Best Practices