2

Many FaunaDB authentication guides promote storing the client secret in as a cookie in order to attach it to the subsequent db queries, but how safe is it? The secret token is, practically, hundred per cent exposed in the browser's cookie storage.

Say this is the /pages/api/login.js handler under a Next.JS app:

    import { Client, query as q } from 'faunadb'
    import cookie from 'cookie'
    
    const client = new Client({ secret: 'YOUR_SERVER_SECRET_KEY_DO_NOT_EXPOSE_THIS_EVER' })
    
    const serializeFaunaCookie = secret => {
      const cookieSerialized = cookie.serialize('FAUNA_SECRET_COOKIE', secret, {
        sameSite: 'lax',
        secure: process.env.NODE_ENV === 'production',
        maxAge: 72576000,
        httpOnly: true,
        path: '/',
      })
      return cookieSerialized
    }
    
    export default async (req, res) => {
      client
        .query(q.Login(q.Match(q.Index('userByHandle'), req.body.handle), { password: req.body.password }))
        .then(({ secret }) => {
          res.setHeader('Set-Cookie', serializeFaunaCookie(secret))
          res.status(200).end()
        })
    }

The result of this rather obvious, the domain will set a cookie like FAUNA_SECRET_COOKIE whateverrandomUpperCaseandlOWERcASEalphanum3r1calstring - which has no encryption. Thanks!

Comi9
  • 69
  • 1
  • 5

2 Answers2

3

It's a bit tricky to understand. Your example is not unsafe in itself, although it's a very bad practice, unnecessary and can likely lead to security issues.

In your example, you're using YOUR_SERVER_SECRET_KEY_DO_NOT_EXPOSE_THIS_EVER, which, I assume, is either gonna be a server or admin role secret key.

enter image description here

There are a few things to understand:

  • Your FAUNA_SECRET_COOKIE cookie cannot be accessed from the browser because it uses httpOnly: true, which makes it readable from the server only. Thus, the secret cannot be accessed by anyone, but only by you.
  • Storing server/admin role secret key in a cookie is a very bad practice, even though it uses a httpOnly cookie, if you send this to the browser then this secret can be used to do about anything on your fauna database, it grants too much permissions.

Although, sending a Fauna token in the browser (stored in a httpOnly cookie) is not necessarily unsafe, but it mostly comes down to what role that token grants (and what permissions comes with it).

What most online examples fail to explain thoroughly is that you should only provide secret tokens to the browser if those tokens have a limited set of permissions. And this usually require creating custom roles, like the Public role above-mentioned in the screenshot.

Taking a real-world example based on https://github.com/Vadorequest/rwa-faunadb-reaflow-nextjs-magic (I'm the author), you can see a demo of the app at https://rwa-faunadb-reaflow-nextjs-magic.vercel.app/.

On this app, there is a Fauna secret key associated to the Public role that is available in the browser for everyone who accesses the app. This is not unsafe, because the permissions associated to that role are very limited. You can see them here. (they basically allow to read/write one document of id=1, nothing else)

This is one example of a secure, yet shared token.

Another example (from the same app) is when a user authenticates, when doing so, he gains a new token that is then stored in a httpOnly cookie (same as your example). This token is not related to a role, but to a document instead.

The document is the "User" that's been authenticated, and this is handled through another custom Role "Editor". The role is configured with a restrictive set of permissions, basically allowing the token to only read the User itself, and documents that have ownerId set to the user id.

To sum it up:

  • If someone were to "steal" the Public token (it's hardcoded in the browser and very easy to retrieve), they could only perform a very limited set of operations, it is safe.
  • If someone were to steal a user's token (which is not straightforward, because it's not stored on the browser, although possible because it's sent to the browser), they could only impersonate that particular user. Also, the token as a 7h TTL, so it'll be automatically invalidated 7h after being generated.
Vadorequest
  • 16,593
  • 24
  • 118
  • 215
  • Understood! Thanks a lot for this! I'm thinking now that I'd rather have to use the ABAC approach as oposed to using the standard auth flow that FaunaDB describes. So, defining privileges for documents, to the roles assigned to users is the way to go. And, in that case, if one logs in and receives a token that's valid to his very own documents, then how should my login api change? if it should at all. – Comi9 Apr 21 '21 at 09:24
  • However, what if I need a super-admin user role that should be able to manage the entire database? That doesn't defeat the purpose anymore. – Comi9 Apr 21 '21 at 17:47
  • You need both, and it's what I do in the POC. I have a `server` token managed through `.env` and I have a user-related token generated during login. The server key is for managing things that are outside of the user's scope while the user-related key is for managing things related to the user. The user-related key is sent to the browser because I use the real-time feature and it's a necessity, but also because I run my GraphQL queries directly from the browser instead of going through an API proxy endpoint. The server key is never shared with the browser. – Vadorequest Apr 23 '21 at 09:05
1

if it's a server key or an admin key, you should NEVER give that to the frontend, it should be exclusive to the backend, because of this:

enter image description here

Anything you give to the frontend becomes public, that is, anyone can access it, it doesn't matter if it's store in the cookie, in the local storage, or a response to an xhr, malicious users will inspect everything in search of a key that you happen to leak out to the frontend.

However, like with what @Vadorequest explain, if it's a key the user is using to do operations ON HIS OWN DATA (and it can't be used for anything else), then I think it's completely fine.

aprilmintacpineda
  • 1,114
  • 13
  • 21
  • Thanks for your input! Yes, the fauna server key isn't exposed to the front-end, it's going to be stored either in an untracked by git .env file, or - when using vercel -, it can be stored as a vercel environment variable. – Comi9 Apr 21 '21 at 08:39