-
Notifications
You must be signed in to change notification settings - Fork 61
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
df41b5e
commit 8fc39f1
Showing
38 changed files
with
338 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,18 @@ | ||
# Basic Honeypot | ||
|
||
👨💼 When users sign up for an account on our app, we send a confirmation email. | ||
If spam bots submit random people's email addresses in there, we'll get marked | ||
as spam and our deliverability will be poor (emails we send won't get to where | ||
they're supposed to go). | ||
|
||
Kellie 🧝♂️ put together a new <LinkToApp to="/signup" /> route which accepts the | ||
user's email address. We need you to add a honeypot field to the form so we can | ||
detect bots and not send emails if that field is filled out. | ||
|
||
You just need to make sure that regular users don't accidentally fill the field | ||
out. It can be pretty basic, because many bots aren't very sophisticated. But | ||
we'll improve it later to deal with more sophisticated bots in the future. | ||
|
||
The form only actually redirects to `/` for right now. If the honeypot field is | ||
filled in then the `action` should return a 400 error response (you can use | ||
`invariantResponse` for this if you like). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,5 @@ | ||
# Basic Honeypot | ||
|
||
👨💼 Great job! Your honeypot is awesome, but Kellie 🧝♂️ just found a library that | ||
will do this for us, so we're going to use that library instead. Sorry about | ||
that 😅 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,46 @@ | ||
# Remix Utils | ||
|
||
👨💼 We're going to use [`remix-utils`](https://npm.im/remix-utils) to put | ||
together the honeypot in the UI and check it in our `action`. This is going to | ||
require a bit of setup because it can do more than just handle the single field | ||
for us. | ||
|
||
First you're going to need to | ||
create <InlineFile file="app/utils/honeypot.server.ts" /> to create a honeypot | ||
instance. For example: | ||
|
||
```tsx nocopy | ||
import { Honeypot } from 'remix-utils/honeypot/server' | ||
|
||
export const honeypot = new Honeypot({ | ||
validFromFieldName: 'validFrom', | ||
encryptionSeed: process.env.HONEY_POT_ENCRYPTION_SEED, | ||
nameFieldName: 'name', | ||
randomizeNameFieldName: true, | ||
}) | ||
``` | ||
|
||
We don't need all those options just yet though. In fact, for this first bit, we | ||
just want to set `validFromFieldName` to `null` for now. We'll get to the rest | ||
of the options later. | ||
|
||
Once you have that set up, you can use it in the | ||
`action` of <InlineFile file="app/routes/_auth+/signup.tsx" />. For example: | ||
|
||
```tsx nocopy | ||
try { | ||
honeypot.check(formData) | ||
} catch (error) { | ||
if (error instanceof SpamError) { | ||
throw new Response('Form not submitted properly', { status: 400 }) | ||
} | ||
throw error | ||
} | ||
``` | ||
|
||
The `SpamError` comes from `remix-utils/honeypot/server`. | ||
|
||
And then for the UI, you can remove our custom `input` stuff with | ||
`<HoneypotInputs />` from `remix-utils/honeypot/react`. | ||
|
||
Good luck! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,10 @@ | ||
# Remix Utils | ||
|
||
👨💼 Great! That's better. It's nice to use a library for stuff like this because | ||
it means that as spam bots get more sophisticated, the library can be steadily | ||
improved and we can just update our code to use the new version. | ||
|
||
🧝♂️ I'm going to update <InlineFile file="app/root.tsx" /> a bit to make the next | ||
thing you're going to do a bit easier. It's easier to | ||
just show you the diff than explain it. So you | ||
can <DiffLink>check it out here</DiffLink>. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,24 @@ | ||
# Honeypot Provider | ||
|
||
👨💼 If a user is able to submit the form within a second of the form being | ||
generated, they're probably not a human (or maybe they're just | ||
[Barry Allen](https://en.wikipedia.org/wiki/List_of_The_Flash_characters#Barry_Allen_/_Flash) ⚡). | ||
So as a part of our honeypot, we can have another hidden field that keeps track | ||
of when the form was generated. Then when the form is submitted, we just make | ||
sure it was submitted at least a second after it was generated. | ||
|
||
There are a few problems with this we'll need to consider. For example, if we're | ||
running automated tests, then our user actually _is_ a bot and that's okay 😅 | ||
So when we're running tests, we don't want to include the valid from field. | ||
|
||
We'll know whether we're in a testing environment if `process.env.TESTING` is | ||
set. (That's set in <InlineFile file="index.js" />). If that's set, then just | ||
set the `validFromFieldName` to `null` and that will prevent remix-utils from | ||
including and checking for that field. Otherwise, you can set it to a string, or | ||
just use `undefined` to have the default value be used. | ||
|
||
Another challenge will be is synchronizing our UI with our server config for the | ||
honeypot fields. So we need to update <InlineFile file="app/root.tsx" /> to | ||
handle this. | ||
|
||
There are other issues, but let's just start with this. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,14 @@ | ||
# Honeypot Provider | ||
|
||
👨💼 Great! Now we're probably going to catch even more bots! But we've got | ||
another problem we'll want to deal with next. | ||
|
||
🧝♂️ Before you get to that though, I'm going to make a small change. We're going | ||
to apply this honeypot stuff to all our public forms, so to reduce repetition, | ||
I'm going to move some of the boilerplate from the `action` | ||
in <InlineFile file="app/routes/_auth+/signup.tsx" /> | ||
to a utility called `checkHoneypot` | ||
in <InlineFile file="app/utils/honeypot.server.ts" />. | ||
|
||
Feel free to <DiffLink>check the diff</DiffLink> or even do it yourself if you | ||
like. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,31 @@ | ||
# Encryption Seed | ||
|
||
👨💼 So, you may not have noticed this, but `remix-utils` doesn't actually put the | ||
real date the form was generated in the `validFrom` input of the form. It's just | ||
some random string of characters. Well, it's not really random, it's actually | ||
encrypted. The reason for this is because we don't want bots to be able to just | ||
change the date on the form and then submit it quickly. So `remix-utils` will | ||
encrypt the actual valid date and that's what the form is set to. | ||
|
||
To be able to decrypt the value, we need an encryption key. `remix-utils` will | ||
generate one for us if we don't set one ourselves. Unfortunately, there's a | ||
problem with doing things this way. The key is generated at startup time, so if | ||
you restart your server, or your running multiple instances of your app, a form | ||
could be generated with one key and validated with another. | ||
|
||
So instead, we can set it to something consistent across all instances of our | ||
app. We can do this by setting the `encryptionSeed` option in our config. The | ||
tricky bit is we need this to be secret, so we're not going to just commit this | ||
to the repository. We need this to be kept secret. So we'll use environment | ||
variables. | ||
|
||
So we're going to place it in our `.gitignore`d `.env` file which we're loading | ||
at startup time during development, and then you'll want to make sure to set | ||
this environment variable in your production environment as well | ||
([for example](https://fly.io/docs/reference/secrets/)). | ||
|
||
🐨 So first, you'll set the variable in <InlineFile file=".env" />, then | ||
go to <InlineFile file="app/utils/env.server.ts" /> to validate at startup time | ||
that the variable is set (and get type safety on it as well). | ||
|
||
🐨 Once you've got that, you can set the `encryptionSeed` in the honeypot config. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,6 @@ | ||
# Encryption Seed | ||
|
||
👨💼 Awesome! Now we're able to handle the massive network of bots randomly | ||
submitting forms all over the internet. Again, this won't really help with a | ||
targeted attack (we'll do more with that later). This definitely helps a lot | ||
with general abuse though, so great work! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,46 @@ | ||
# CSRF Setup | ||
|
||
👨💼 We've got to get some things set up with the `remix-utils` CSRF utilities | ||
before we can actually start protecting our forms. We'll dive deeper into | ||
cookies in the [Web Auth](https://auth.epicweb.dev) workshop later, but you do | ||
need to set a signed cookie in the user's browser so you can use | ||
[Remix's `createCookie`](https://remix.run/docs/en/main/utils/cookies#createcookie) | ||
to help with that. | ||
|
||
```tsx | ||
import { createCookie } from '@remix-run/node' | ||
|
||
const cookie = createCookie('csrf', { | ||
path: '/', | ||
httpOnly: true, | ||
secure: process.env.NODE_ENV === 'production', | ||
sameSite: 'lax', | ||
secrets: process.env.SESSION_SECRET.split(','), | ||
}) | ||
``` | ||
|
||
🐨 Go ahead and stick that in <InlineFile file="app/utils/csrf.server.ts" />. | ||
|
||
You'll notice we're using another environment variable. 🐨 You'll need to set | ||
that up like we did with the `HONEYPOT_SECRET` earlier. | ||
|
||
Feel free to [read the Remix docs](https://remix.run/docs/en/main/utils/cookies) | ||
all about cookies if you want to learn more about these options, but we'll get | ||
to this stuff in more depth in the Web Auth workshop. | ||
|
||
🐨 Once you have the cookie object created, you can use that to create a CSRF | ||
utility; | ||
|
||
```tsx | ||
import { CSRF } from 'remix-utils/csrf/server' | ||
|
||
// ... | ||
|
||
export const csrf = new CSRF({ cookie }) | ||
``` | ||
|
||
Now, we need to get the user's unique token in our UI for our forms and in the | ||
user's cookie so we can validate it on the server. We'll do that | ||
in <InlineFile file="app/root.tsx" />. 🐨 See you there! | ||
|
||
- [📜 Remix `createCookie`](https://remix.run/docs/en/main/utils/cookies#createcookie) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,26 @@ | ||
# CSRF Setup | ||
|
||
👨💼 Great! You should now be able to get that cookie value set in the browser's | ||
"cookie jar." You can verify this by opening the browser's developer tools and | ||
looking at the cookies for the page under the "Application" tab. The cookie | ||
will be called "csrf" and have a value that looks like a bunch of nonsense like: | ||
|
||
``` | ||
IjdDa3p6WkV1d3NNel&nev3r&60nna&gIv3&yoU&uP&UW0zYmxDMDNyRjQi.NM71601wmCvZ%2FZaGIG6wV%2FuX%2FvGafzDEAmamK1hNu88 | ||
``` | ||
|
||
And while you can't visually verify it, that (unsigned) token value is in the | ||
browser as well. You can check by executing this in the console: | ||
|
||
```js | ||
__remixContext.state.loaderData.root.csrfToken | ||
``` | ||
|
||
Which should get you something like this: | ||
|
||
``` | ||
7Ck&Nev3r&60nna&1et&yoU&d0wn&UOfQm3blC03rF4 | ||
``` | ||
|
||
So the next step is to get that into our form and verify it to prove that the | ||
form was submitted using our app and not some other site. Let's go! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.