I Tried to Warn You
317 segments
Hey, remember this tweet from last
December when Next.js said, "Hey
everybody, you better upgrade. Uh if you
don't, well, there's kind of a remote
code execution on pretty much every
version of Next.js. So,
hey, but server components are really
cool though." And of course, everybody
upgraded only to be met yet again just a
couple days ago. Hey, guess what
everybody?
You better uh upgrade because well, if
you don't upgrade, you will simply have
um a denial of a middleware proxy
bypass, another a middleware proxy
bypass, another denial of service,
another middleware proxy bypass, a
server-side request forgery, a
middleware proxy bypass, a cross-site
scripting, a second cross-site
scripting, a denial of service, a cache
poisoning, a second cache poisoning, or
potentially a fifth middleware proxy
bypass.
Hey, you know what I'm talking about?
AI, man. Isn't AI pretty sweet? Like,
look at that. You know, like that's AI,
bro. That's what we're getting out of
this. Hey, honestly, React is more
secure now. You just got to upgrade.
Either way, I'd like to call myself a
bit of a Nostradamus.
As a person who's written a data
fetching library, let me just tell you,
it's really easy to screw it up. Now,
obviously, the forced upgrades will
continue until morale improves or you
finally switch over to using HTMX, which
by the way, the lord's library, and it
works really well with AI. So,
if you're I'm just just throwing that
out there, okay? Now, obviously, there's
quite a few vulnerabilities here, so we
are going to take apart the very tippy
top one, the highest one, which is
actually a bug that's within React. But
before we do that, we got to get the
bag. Hey, is that HTTP? Get that out of
here. That's not how we order coffee. We
order coffee via SSH, terminal.shop.
Yeah, you want a real experience? You
want real coffee? You want awesome
subscription so you never have to
remember again? Oh, you want exclusive
blends with exclusive coffee and
exclusive content? [music]
Then check out Chrome. You don't know
what SSH is?
Well, maybe the coffee's not for you.
>> [singing]
[music]
>> Now, there's no information on the
actual exploits, at least given out
publicly officially, but there is this
beautiful slop pository in which has
reproduction steps for pretty much every
single one of them all listed out
nicely. Now, we're going to go to the
very tippity top one that I showed you
before, and it gives you this beautiful
piece of code right here. And if you
jump into the YAML, you can actually
read what it's about. It's just taken
straight from the actual CV website,
which is going to say this: Next.js app
router consumes React decode reply
action bundled from React server DOM
webpack to parse React server components
replies server action bodies. Pre-patch
React walk the reply graph during model
resolution without any depth cycle or
row count limit. An unauthenticated
attacker can post a
form encoded reply body to an app router
page with the next action header and
force the server to spin CPU/stack
overflow for tens of seconds per
request. In other words, you're
effectively doing a DDoS. You're denying
the server's ability to process any
other request cuz remember, JavaScript.
Yo, we're single-threaded, pretty much.
As this thing just spins and does
nothing, your CPU's pegged and you have
pretty much no insights as to what's
happening, and you can even get a stack
overflow if you provide enough of these
encoded items. So, what are these
encoded items? Remember I said it was a
slop pository? Well, here's part of the
example of the slop pository. Inside of
this beautiful bash file, we also have
some beautiful Python going on right
here. I mean,
nothing tells me that an AI has written
something than code that looks like
this. This is This is what makes AI
happy, okay? If AI has the ability to be
in bash piping out to Python, I mean, it
is one happy mythos. Honestly, kind of
making Dario proud right now. And so,
then it produces this value right here
over and over again, and it does this
whole next thing right here. So, it
says, "Hey, all right. I'm going to
create a dollar sign F with a value in
hexadecimal, and I'm going to then going
to create an object that references this
next value also in hexadecimal. Now,
you're probably thinking, "Okay, I don't
even have any idea what that means."
Well, that's not a big deal, because
guess what? This isn't our first time
inside this code. So, if you go over to
this parse model string function, it
actually is doing the parsing. Now,
remember, in regular user land,
typically people from websites send up
JSON. Oh, no, no, no, no, no, no. Not in
React land. See, in React land, you have
your client, right? Big old C client
here. Let me increase the size. Oh,
and I went down in size. Oh my gosh,
embarrassing. Can I say that?
Embarrassing. Okay, a little too big,
but whatever.
Uh your client and your server
communicate back and forth. Now,
typically in your normal world, probably
at your normal job, you're using JSON.
So, you can just call like JSON.parse.
Better wrap a little try-catch around
that, or else your server explodes, but
you get the idea. Pretty
straightforward. Your client does the
exact same thing, but not in React
server components land. The special
meetings almost always start off as a
string. It starts off as a string with a
dollar sign. From there, it has some
sort of control character, like, "Hey,
what kind of action are you actually
asking me to perform?" If I remember
correctly, it's actually this code right
here, a subclass B for blob. This is
actually how the previous one had remote
code execution is by calling this
function right here, which ended up
returning a function in which was
actually specified as part of the input
and being stringified as a function into
an actual JavaScript function, which
allowed the user just to say, "Hey,
here's what I want you to execute. I
control the string and you turn it into
JavaScript for me and I can do whatever
I want on your machine."
It was not good, not good at all. But
this time, it's an uppercase F. Again,
jump right here, you can see the
uppercase F, so it's setting something
up. And what this does is this says,
"Hey, I have a reference. You are
referring to some object somewhere on
the server, so I'm going to decode it
for you." And so it does this get
outlined model call right here. Well,
outlined model will simply go off and do
the hydration, do whatever it needs to
do, except if this model has already
been resolved once before. Now, this is
where this beautiful loop comes in right
here because if you look at this, who am
I referring to? I'm going to refer to
the next one in line unless if we've
gone all the way around, then I'm going
to make a nice little ring right here,
so that way the last element refers to
the first element. So when we get here
and we've already initialized the model,
it's going to call this, "Hey, we need
to initialize this chunk." Specifically,
so it would actually points to the
correct object. Now, inside of
initialize model chunk, what it's doing
is going to revive the model. I assume
this is like the hydration process.
Like, "Hey, here's all the stuff that
exists on the server, so go off and do
it." Now, when you're reviving the
model, if the value I passed into you
was a string, oh my gosh, it's a string.
So it's going to go here and recall
parse model, so you can kind of guess
what happens here. We parse model to get
outlined model to initialize model chunk
to revive model to parse model string.
And we're just going to keep on doing
this nice little circle as much as we
can until bad things happen. In fact, it
only takes 53,000 times around this loop
for my stack to be exceeded. I bet your
servers probably are some cheap EC2
instances or something and they probably
don't get nearly as deep with the stack.
>> [snorts]
>> Now, the crazy part about this is that
the user doesn't even need to be
authenticated. They just simply need
access to this payload and you need to
be on an old enough version of React.
And so therefore, they can just send you
this message and boom your computer that
one single message one message means
your server up in the clouds completely
off. It's going to crash the process but
before it crashes the process it's going
to take hundreds of milliseconds if not
longer to process through everything
first. You see back in my day react used
to just be a view library. In fact way
way back in the day it used to say hey
we're the V in model view controller
MVC. Now I know those days are long
they're those are the bygone days of
yore at this point. Effectively react /
next JS just does everything. I I just
have a question okay? Honestly I don't
really understand the entire appeal of
server side components to begin with
right? Your client goes over here it
makes a request and all of this is to
prevent yourself from having the N plus
one query problem and you can also get
some kind of cool page dynamic caching
because the first moment you hit a
suspense like
component it oh my gosh look at that
line that was a perfect line. It once
you hit that first suspense whatever the
initial HTML that comes across the wire
that thing can be cached by a CDN and
then all the follow up stuff is only
user specific data. Like is that is that
the entire reason why people use react
server components? Am I crazy? This is a
lot of engineering just to avoid you
thinking about how to load your data.
I'm just going to throw it out there. I
feel like you you you could just you
could just do this instead. You know you
could just load the data you need. I
know novel concept. All right well looks
like that's it. I just kind of wanted to
yap about this for a little bit cuz I
thought these this was pretty
interesting. There's also one with cross
site scripting and how it does some you
know dangerously skip HTML which is very
funny by the way. It's super super funny
to see react the library in which you're
not supposed to have to think about HTML
and being able to do or like removing
any of the escaped characters or
anything like that. You don't have to
think about any of that. Instead, you
just hand it to React and it renders it
correctly and it just turns out you
can't hand everything to React because
some of the items underneath the hood,
well, they're actually using set HTML
dangerously and they weren't properly
escaping things. Very, very hilarious.
Anyways, so if hey, if you're using
React, you
better upgrade because even if you're
not using Next.js, you still got that
problem I just showed you right there.
That's pretty serious, huh? Can I tell
you Can I tell you like a little story
that I'm a little ashamed of? In 2016
when Netflix was flirting
with
with React and putting it on a
television, I was a part of the initial
performance side of things and when
comparing a very skinny app that has
virtually no features to an app that's
completely filled with features and has
a decade of legacy code, you may This
may surprise you, but the
the skinny one was faster. And so people
kept pushing it and I'm not going to lie
to you guys. During my dark days right
before I became jaded, but I thought
React was really great in 2016 and then
I used it a whole bunch and then I saw
what happened and then I I stopped
liking React after that.
>> [laughter]
>> I can't help it. I stopped liking it.
But you know what happened? I learned a
lot during those days, okay? I learned
very, very, very valuable lessons, which
was just say no early on. Just say no.
No.
A gen.
Ask follow-up questions or revisit key timestamps.
The video discusses recent, significant security vulnerabilities discovered in Next.js and React, highlighting a series of bugs ranging from denial of service to cross-site scripting. The speaker breaks down a specific vulnerability that allows an unauthenticated attacker to cause a stack overflow on the server through a specially crafted request, exploiting how React handles nested references. The video also explores the complexities introduced by React Server Components and shares the speaker's critical perspective on the evolution of React.
Videos recently processed by our community