Skip to main content

Command Palette

Search for a command to run...

Introducing StaticShield๐Ÿ›ก: Password protect a website in less than 2 mins.

How I built StaticShield ๐Ÿญ

Updated
โ€ข8 min readโ€ขView as Markdown
Introducing StaticShield๐Ÿ›ก: Password protect a website in less than 2 mins.

Introducing StaticShield ๐Ÿ›ก

image.png

StaticShield is the easiest way to password protect websites. Be it either static or dynamic ones, all you need is just a single line of code to make your app protected. Really!! only a single line of code is required in the head of the document! StaticShield easily works with any favourite framework of your choice. Either the good ol' HTML or the modern Nextjs, everything just works like magic. You can set an expiration time for the user to logout after a certain period of time automatically or you can programatically log out the user at any given point of time. Oh, how did I forget this - security. Since StaticShield has to deal with protecting sensitive content, I have given security the utmost priority. I have taken some security measures, that I have neither seen nor heard about anyone doing it to their apps. Read more to find out! I guess this is too much of a introduction, lets deep dive in

Demo

Here's a working demo -> https://staticshield-with-html.netlify.app/

Watch how to password protect a website in less than 2 mins ๐Ÿ‘‡

  1. StaticShield website - https://staticshield.vercel.app
  2. StaticShield Docs - https://staticshield.vercel.app/docs/
  3. GitHub repo - https://github.com/Lalit2005/staticshield
  4. GitHub repo (docs) - https://github.com/Lalit2005/staticshield-docs
  5. GitHub repo (StaticShield examples) - https://github.com/Lalit2005/staticshield-examples

Examples

Here are some examples ๐Ÿ‘‡

  • https://staticshield-with-html.netlify.app
  • https://staticshield-with-nextjs-11.netlify.app/
  • https://staticshield-with-sveltekit.netlify.app/protected
  • https://staticshield-with-nuxtjs.netlify.app
  • https://staticshield-with-svelte.netlify.app/
  • https://with-nextjs-11-whole-app.netlify.app
  • https://staticshield-with-vuejs.netlify.app

Tech stack ๐Ÿ“š

  1. Next.js - The most amazing React framework on the planet
  2. TailwindCSS - Styling
  3. Geist UI - React component library
  4. HarperDB - Database
  5. Nextra - Documentation
  6. Auth0 - Authentication
  7. Axios - API requests
  8. React Hook Form - Form validation
  9. Zod - Validation
  10. Web3forms - Form submissions
  11. SWR - Remote data fetching
  12. Typescript - Type checking
  13. Uglify-js - Minifying script
  14. Next-PWA - Convert website to Progressive web app
  15. Vercel - Hosting

The birth of the Idea ๐Ÿ’ก

Once, I just wanted to make an app where only me and my friends could have chat. Just like WhatsApp but only for predefined users. But building a full blown authentication for such an app was definitely an overkill. I just wanted to password protect it. But when I searched the whole internet for a solution, I could not find one ๐Ÿ˜‘. That's when I thought about StaticShield. Although this name sounds like Covishield (A coronavirus vaccine, another name for Astrazeneca), I branistormed this idea and also the name before covid vaccines came out.

Prototyping ๐ŸŽจ

I rarely make prototypes of projects before making them, but for StaticShield, I just wanted to draw a bigger picture of the architecture. For that, I used Excalidraw and it's really an amazing one! But about after a week, I just completely forgot that I had made prototypes ๐Ÿ˜…

Here is an image of the prototype ๐Ÿ‘‡

image.png

Design โœจ

When it comes to design, Vercel is the best. If you carefully observe, most of the design is inspired from Vercel. All their websites look really aesthetic and slick. And that's an another great reason to love Vercel (in fact, who doesn't like Vercel). And that's why I used Vercel's design system/component library itself. If you do not know about it, it's called Geist UI. Geist UI really made the development super simple while keeping the app quality top notch. And this probably is the reason StaticShield gives the Vercel's feel and vibe ๐Ÿค”

image.png

The design of Vercel is concise and aesthetic, this is an important reason for popularity of Vercel.

โ€“ Geist UI

Authentication ๐Ÿ”

Since I heard a lot about the newly released Auth0 library specially made for Next.js, I used it for StaticShield. Since StaticShield is vaguely similar to Auth0, I thought it would be difficult to build an Auth0 kind of application on top of Auth0 itself. But it wasn't that difficult as expected ๐Ÿ˜…

Auth0 really simplified the process of adding authentication and authorization. All it required was only two lines of copy pasted code

import { handleAuth } from '@auth0/nextjs-auth0';
export default handleAuth();

The first bug/challenge I faced in building StaticShield was a bug that got solved mysteriously. Auth0 did not work at the first time when I implemented it. I searched all over StackOverflow but ended up without success. I tried to restart the server too, but I did not work. I started the following day trying to reinstall the nextjs-auth0 library. But that did not work too. I was hopeless. When frustration overpowered my patience, I just restarted the server again and again. Finally!! After the 5th or 6th time, it automatically worked!! Till date, I have never seen a bug like this that never throws out an error and not work too! but works when restarting the next app 5-6 times. Anyways, Auth0 is great and the @auth0/nextjs-auth0 library is super awesome too!

Developer Experience (DX) ๐Ÿ‘จ๐Ÿปโ€๐Ÿ’ป

Have you wondered how Vercel gained popularity? It's because of the developer experience it provides in its every product. Every step it takes, makes the life of a developer much easier. I too wanted to provide the smoothest developer experience in StaticShield just like Vercel. In fact I tried to recreate the feedback widget of Vercel in my app. Since I did not find much time, I just made a basic version of it ๐Ÿ™‚ I also wrote the docs really really fast. But unfortunately, I could not complete them. So, I'll be keeping them for another day. If you can and have time, please do contribute to the docs. P.S: You don't require rocket science

image.png

User Interface and Dashboard ๐Ÿ–ฅ

Many elements and infinite tiny-tiny details in StaticShield are inspired from Vercel's user interface. A good example here would be the status of the site. The tiny dot shows before the name of the site, whether the logins to the site are blocked or allowed (Yep, you can block and allow logins too!). In Vercel's interface, this dot shows the status of the deployment.

image.png


The dashboard layout is completely made with TailwindCSS and its brand new and powerful JIT engine. The positioning of the right side of the dashboard was the most difficult one. But still I managed to create a descent one. image.png

To make the app more accessible, I added tooltips at many places

image.png

API (Serverless functions) โ–ฒ

All the API endpoints are serverless functions hosted on Vercel. API's play a main role in querying data from db, posting data to Harper DB, and updating them. But the only problem I faced was the error that every developer at least encounters once - CORS. Yeah, CORS was required for a single serverless function that verifies token. But this token was sent from other websites, hence other domains. I tried all sorts of possible solution such as cors library, next-cors and many more. But none worked. Finally after a dozen of solutions, I found the correct one. The solution was simple. All I had to do was add the following to vercel.json -

"headers": [
    {
      "source": "/api/verify-token/",
      "headers": [
        { "key": "Access-Control-Allow-Credentials", "value": "true" },
        { "key": "Access-Control-Allow-Origin", "value": "*" },
        {
          "key": "Access-Control-Allow-Methods",
          "value": "GET,OPTIONS,PATCH,DELETE,POST,PUT"
        },
        {
          "key": "Access-Control-Allow-Headers",
          "value": "X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version"
        }
      ]
    }
  ]

and this to the serverless function -

res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Origin', req.headers.origin);

This is actually a combination of 3 different solution to cors problem.

Security ๐Ÿ‘ฎ๐Ÿปโ€โ™‚๏ธ

This is the part where I spent most of the time. I took security really seriously for StaticShield. I have taken some steps in hashing the passwords that I have never seen anyone doing. Before storing any password to HarperDB, I hash it 33 times in total. Yeah, you heard me right.

At first, I combine the plaintext password with a long secret(stored in environment variables) and then hash it with the powerful SHA-256 hashing algorithm. Then I just reverse this SHA-256 hash. Finally I hash this hash 25 times with another really powerful, battle tested algorithm - Bcrypt.

And i guess, there's no way to get the plaintext password back. Here's how the hashPassword.ts file looks like -

// lib/hashPassword.ts

import bcrypt from 'bcrypt';
import { SHA256 } from 'crypto-js';

const hashPassword = (password: string) => {
  const SHAHash = SHA256(password + process.env.HASH_SECRET).toString();
  const reversedSHAHash = SHAHash.split('').reverse().join('');
  const bcryptHashedPassword = bcrypt.hashSync(reversedSHAHash, 5);
  return bcryptHashedPassword;
};

export default hashPassword;

Database (HarperDB ๐Ÿฆฎ )

I have used one of the most unique and best database in the world - HarperDB. HarperDB works seamlessly with SQL as well as NoSQL queries. It harnesses the power of both worlds. For StaticShield updating, reading and deleting seemed easy with SQL. All it required was single line queries such as

const res = await fetch(process.env.HARPERDB_URL, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Basic ${process.env.HARPERDB_KEY}`,
    },
    body: JSON.stringify({
      operation: 'sql',
      sql: `SELECT * FROM site_schema.sites where user_id = "${userId}"`,
    }),
  })

But creating a new record was really easy with NoSQL queries. Here lies the true power of HarperDB where it provides you with the flexibilitity in writing complex as well as simple queries.

const res = await fetch(process.env.HARPERDB_URL, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Basic ${process.env.HARPERDB_KEY}`,
    },
    body: JSON.stringify({
      operation: 'insert',
      schema: 'site_schema',
      table: 'sites',
      records: [{ ...record, user_id }],
    }),
  });

Features โœจ

The complete list of features of StaticShield

  • Password protect site (in <2mins)

image.png

  • Log out the user after a certain period of time automatically โฐ

image.png

  • Or log out the user programatically ๐Ÿ”’

image.png

image.png

image.png image.png

  • Block logins to any password protected site temporarily โŒ

image.png

  • Readymade code snippets โšก๏ธ

image.png

  • PWA - Progressive Web App. Install it anywhere! It's only about 500kb

image.png

Future Roadmap ๐Ÿ—บ

  • Ability to add usernames and separate password for each site instead of only password
  • Customize the login page
  • Add Cypress tests
  • Add screenshot preview of every
  • Improve documentation

Thanks ๐Ÿ™

Thanks a lot for reading till the end. Hope you enjoyed reading on how I built StaticShield. Feel free to comment down your thoughts below in the comments section below

Comments (16)

Join the discussion
A

Now this is a really good product idea good job Lalit ๐Ÿ‘

2
L
Lalit5y ago

Thank you so much Andrew Baisden

A

This is one product that I had been looking for. I always wanted to created something like this but never got the time.

Congratulations!

4
L
Lalit5y ago

Thanks Aditya Mitra

Hope you liked it ๐Ÿ™‚

1
P

The idea is really nice, but since this is client-side, it can easily be avoided: https://disk.yandex.com/i/u_yuF6BhxMVF2Q
I can just block the entire staticshield.vercel.app domain and add some custom CSS to bypass staticshield.
This is not a very secure solution. Also, the page that informs you that you need to enable javascript ( https://staticshield.vercel.app/errors/noscript )... does not work without javascript

1
L
Lalit5y ago

Hello Piotr Badeล‚ek, I am aware that you can bypass now. I am fixing all these issues currently with a greater security in place. This will be out soon. For now, you can use it with apps that has non-developer users.

https://staticshield.vercel.app/errors/noscript )... does not work without javascript

Yeah, I've noticed it. I'm finding solution to avoid this. I'll be fixing this soon

M

This is simply amazing. Authorization is a notoriously common thing to do wrong. To just automate it, what a brilliant idea!

What about privacy, though?

L
Lalit5y ago

Thanks for the kind words Miloslav ๐Ÿณ๏ธโ€๐ŸŒˆ ๐Ÿฆ‹ Voloskov

What about privacy, though?

Do you mean users' privacy? If yes, every user data is handled by Auth0, which is trusted by many companies. StaticShield accesses only email, name, nickname, profile picture and user-id of users.

Please do let me know your queries ๐Ÿ™‚

1
M

Lalit auth0? Nice! Iโ€™ll definitely give it a try then ๐Ÿ‘

3
G

This is next-level work. a password-protected site just in two min. Awesome !!

8
L
Lalit5y ago

Thanks a lot Gaurav Tewari

Glad you liked it ๐Ÿ™‚

3
R

This is awesome man!! ๐Ÿš€๐Ÿš€ The UI sure does give the vercel vibe. Great job with the implementation. ๐Ÿ‘๐Ÿ‘

2
L
Lalit5y ago

This means a lot. Thanks you so much Rutik Wankhade

C
Candy5y ago

Liked your project idea a lot!!!

L
Lalit5y ago

Glad you liked my project Candy Tsai ๐Ÿ™‚

1
I
itkj5y ago

Thank u for new knowledge for me.

S

Lalit Your site and work looks amazing. Keep up the great work. But does it really works ? scripting based security can be manipulated in browser, One can block the script in browser and can access the secured page even without password.

  1. Block the script in browser.
  2. Unhide staticshield-div

And there you go you can see the secured page, no matter how many times it is hashed :p

Maybe this can be used as a quick solution but not certainly to make anything secured,

Please let me know if I am missing anything.

L
Lalit5y ago

Thanks SHOBHIT RASTOGI, glad you liked StaticShield

โ€“ One can block the script in browser and can access the secured page even without password.

I have taken that into account - If javascript is blocked from executing, the <noscript> tag instantly gets activated which then redirects the page here which asks the user to enable Javascript (and links them to a guide). Moreover Javascript frameworks like react and vue require js to render and paint the screen itself.

โ€“ Unhide staticshield-div

When a password protected site loads, the rendered HTML is not displayed with the help of CSS. And as soon as the javascript script loads, the user is redirected to login page if no valid token exists.

Removing the staticshield-div is practically not possible because the rendering of html and css happens in milliseconds usually, and opening the DevTools and removing the staticshield-div class in this short period of time is practically impossible.

Frameworks like Next.js (version 11) prioritize the execution of client js scripts before the rendering of html and css when the stratergy is set to beforeInteractive. More info here

Please do let me know your queries ๐Ÿ™‚

S

Lalit Request is blocked not in real time, Please check this you might understand what I am trying to say,

https://streamable.com/sxjm3r

Thanks,

L
Lalit5y ago

Thanks for the response SHOBHIT RASTOGI

The link seems to be broken, can you please check it?

Thanks

S

Lalit Clicking on it should work have uploaded on google drive too

Link

Please check

L
Lalit5y ago

SHOBHIT RASTOGI That was a good catch! I did not know that you can block certain requests with Chrome DevTools.

I just pushed a solution for it!! I have updated only the with-html example. I will be updating others soon.

Here's a demo ๐Ÿ‘‡

S

Lalit I am afraid that any client side fix will not work, It can still be manipulated.

  1. Install extension in chrome.
  2. Inject the script with the settings shown in the video.

Still secured page can be accessed.

2
A

SHOBHIT RASTOGI This is an excellent catch. I didn't think of it this way. Another concern is the way security is handled in the backend. In this block of code, the TOKEN_SECRET is manually added to the token.

Now I can mint my own tokens

2
S

Amal Shaji That is an excellent observation.

L
Lalit5y ago

Amal Shaji I do not know why jwt.io shows the signature is verified, but the jwt token is signed with a different key (JWT_TOKEN) and the . is replaced with an other key (TOKEN_SECRET)

A

Lalit The only way the signature gets verified is only when the tokens are the same. I did not introduce any external factors, all the variables are derived from the token presented to me after a login.

Y

Wow! This saved my day!

Loved it

Going to use it to protect our small company's MRR details and analysis. Very handy indeed.

Thanks for the project Lalit!!

2
L
Lalit5y ago

Happy to see StaticShield in wild!

Thanks Yafet Shah