HTTP Basic Authentication with S3 Static Site - part 2 Extend

7 minute read   Updated:

This is Part 2 of the 2-part topic on HTTP Basic Authentication with S3 Static Site:

Extend S3 Basic Authentication

We want to address the 2 limitations mentioned in Part 1, and touch on a couple other ways you can utilize S3 password protection:

  1. Allow referenced files to be accessed in a similar way to the Main File main.html
  2. Keep index.html as the Main File and use password.html as the Entry File
  3. Different passwords redirect to different sections
  4. Use CloudFront HTTPS with a password protected S3 site

Updated 05/28/17: updated for S3 new Console UI.
Updated 12/09/16: added complete Sample Code for Section #2.

1. ALLOW REFERENCED FILES TO BE ACCESSED

Recall that our demo S3 bucket is s3-password-demo.codeprocess.io.

Resource

We need to adjust the S3 Policy section of the Main File main.html to allow access not only to the main.html file but also to any other files that main.html references. Basically we need to edit the line "Resource": "arn:aws:s3:::s3-password-demo.codeprocess.io/main.html" to allow for more resources. There are a few options:

# If we structure references files into a single folder
"Resource": "arn:aws:s3:::s3-password-demo.codeprocess.io/assets/*"

# If we structure references files into multiple folders
"Resource": [
    "arn:aws:s3:::s3-password-demo.codeprocess.io/main.html",
    "arn:aws:s3:::s3-password-demo.codeprocess.io/js/*",
    "arn:aws:s3:::s3-password-demo.codeprocess.io/css/*",
    "arn:aws:s3:::s3-password-demo.codeprocess.io/images/*"
]

# A more generic way, allowing all resources
"Resource": "arn:aws:s3:::s3-password-demo.codeprocess.io/*"

It is perfectly OK to use the wildcard * option in the Resource here because all the resources will be guarded by the Referer. Depending on how you structure your files, use an appropriate Resource. For our demo, we’ll use the generic one "Resource": "arn:aws:s3:::s3-password-demo.codeprocess.io/*".

Referer

While we’re at it, let’s relax the Referer condition a bit so it’s more adaptable for other sites. The point of the Referer here is to make sure the request originated from the same site. As long as it’s from s3-password-demo.codeprocess.io we don’t care if it’s index.html page or thisisasecret file or any other file. Also, we don’t want to restrict the protocol to only http since in the future we may host it with https. Our Referer condition now becomes "aws:Referer": "*s3-password-demo.codeprocess.io*".

This has an added benefit that the long S3 URL for the bucket http://s3-password-demo.codeprocess.io.s3-website-us-west-2.amazonaws.com and the short hostname URL http://s3-password-demo.codeprocess.io both satisfy the Referer condition. This means with that our S3 site is fully functional and protected on both URLs.

Combining the edits in Resource and Referer, that section of S3 Policy becomes:

{
    "Sid": "Allow access only if redirected/referenced from the same site",
    "Effect": "Allow",
    "Principal": {
        "AWS": "*"
    },
    "Action": "s3:GetObject",
    "Resource": "arn:aws:s3:::s3-password-demo.codeprocess.io/*",
    "Condition": {
        "StringLike": {
            "aws:Referer": "*s3-password-demo.codeprocess.io*"
        },
        "Null": {
            "aws:Referer": "false"
        }
    }
}

2. KEEP index.html AS THE MAIN FILE AND USE password.html AS THE ENTRY FILE

A big problem with the Basic Idea is that it uses index.html as the Entry File and this conflicts with many web frameworks that rely on index.html as the default index file for the site.

Our goal now is to keep the filename index.html for the Main File and change the Entry File to be password.html.

Entry File password.html

We basically rename our Entry File from index.html to password.html. The content stays the same in password.html:

<!-- password.html -->
<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Password</title>
  </head>

  <body>

    <div style="margin: auto; width:400px; padding-top: 100px; font: 16px arial, sans-serif;">
      <div style="width: 250px;">This is a restricted site.<br> Authorized access only.<br><br>
      Password:<br>
        <form name="password_form" method="post" action="javascript:location.href=window.document.password_form.page.value" style="margin:0;">
          <div style="display:inline;">
            <input type="password" name="page" autocorrect="off" autocapitalize="off" autofocus size="35">
            <input type="submit" value="Enter">
          </div>
        </form>
      </div>
    </div>

    <noscript>
      <div>Javascript is required to access this area. Yours seems to be disabled.</div>
    </noscript>

  </body>
</html>

Main File index.html

For the purpose of a demo, we’ve made a simple index.html by adapting from Static Website Hosting with S3:

<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
  <title>Demo - Static Website Hosting with AWS S3 new Console UI</title>
  <link href="main.css" rel="stylesheet">
</head>
<body>
  <h1>AWS S3 Demo</h1>
  <p>This is hosted with an S3 bucket</p>
  <p>There is an "index.html" file (this file) and a "<a href="main.css">main.css</a>" file</p>
  <div class="blue">This should have a White text on Blue background color if CSS is working</div>

  <h1>AWS S3 Demo - Password</h1>
  <p>This site is restricted. Direct access to this site is not possible.</p>
</body>
</html>

AWS S3 config

Static Website Hosting

Recall that with the Basic Idea, in S3 Static Website Hosting config we used the old Entry File index.html for both Index document and Error document.

Now that our Main File is index.html and Entry File is password.html, we want to set Index document with Main File and Error document with Entry File:

We also adjust the S3 Metadata redirection from the Secret File thisisasecret to the new Main File index.html (instead of main.html):

S3 Policy

Putting together the name change of Entry File to password.html, and the edits for Resource and Referer above, we have our final S3 Policy:

{
    "Version": "2012-10-17",
    "Id": "S3 Basic Authentication using Secret file redirection & HTTP Referer",
    "Statement": [
        {
            "Sid": "Allow public access to Entry File",
            "Effect": "Allow",
            "Principal": {
                "AWS": "*"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::s3-password-demo.codeprocess.io/password.html"
        },
        {
            "Sid": "Allow public access to Secret File",
            "Effect": "Allow",
            "Principal": {
                "AWS": "*"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::s3-password-demo.codeprocess.io/thisisasecret"
        },
        {
            "Sid": "Allow access if redirected/referenced from the same site",
            "Effect": "Allow",
            "Principal": {
                "AWS": "*"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::s3-password-demo.codeprocess.io/*",
            "Condition": {
                "StringLike": {
                    "aws:Referer": "*s3-password-demo.codeprocess.io*"
                },
                "Null": {
                    "aws:Referer": "false"
                }
            }
        }
    ]
}

Test it out with Live Demo

As mentioned above, for our Live Demo we use the S3 bucket s3-password-demo.codeprocess.io which is accessible through either:

Going to the Live Demo will load the Index document which is the Main File index.html, but S3 Policy doesn’t allow direct access to it hence we’ll run into an S3 error. Thanks to us setting the Error document with the Entry File password.html, S3 will present that instead:

Typing in anything random will result in an S3 error which S3 will again show the Error document password.html until we enter in the correct password thisisasecret. This will make password.html load the file thisisasecret. The Secret File will in turn redirect to index.html, and thanks to our S3 Policy allowing Referer from the same site, we can now see the content of index.html:

One issue

Recall that the way redirection works on password.html is to take whatever users type in and change the URL to whatever. Couple this with the Main File is now index.html, users who are familiar with this process can bypass the password protection by entering index.html into the password field. This works because the index.html page is now refered by the password.html which is on the same site.

Luckily there is a fix for this which involves adjusting the S3 policy to be stricter for the index.html, and somehow create a Referer value that is not password.html.

This is achievable by these 3 steps:

  • Adjust the S3 policy to be stricter
  • Introduce a new file, a companion of Secret File, called Secret File HTML
  • Adjust the redirections from Entry File to Secret File, Secret File to Secret File HTML, and finally Secret File HTML to Main File

SAMPLE CODE: we’ve put together all the necessary pieces in this Sample Code.

Instructions     : README

S3 Policy Config : s3.policy.json

Entry File       : password.html
Secret File      : thisisasecret
Secret File HTML : thisisasecret.html
Main File        : index.html

This Sample Code is what deployed on our Live Demo.

3. DIFFERENT PASSWORDS REDIRECT TO DIFFERENT SECTIONS

This is a particular use case that we employed at our Agency where we wanted to show different demos to different clients, using the same landing page (same Entry Point file). Our landing page is highly styled with lots of info so we wanted to reuse it for multiple clients.

The idea is have multiple Secret Files, each redirects to a different sub-folder for a particular client’s demo. This is how we structure our files:

# Entry File (landing page for all clients)
demo.agency.com/index.html

# Secret Files (each file per client)
demo.agency.com/secretClient1
demo.agency.com/secretClient2
demo.agency.com/secretClient3

# Each Secret File redirects to a different sub-folder/Main File
secretClient1 ==> /client1/index.html
secretClient2 ==> /client2/index.html
secretClient3 ==> /client3/index.html

4. USE CLOUDFRONT HTTPS WITH A PASSWORD PROTECTED S3 SITE

This section deserves its own post. In short, it’s possible to combine CloudFront, HTTPS, S3, and password protection together and make them work. The trick is to forward the request’s header Referer through CloudFront:

That’s it

You should be a master in using S3 by now. Let us know in the comments if something isn’t working for you, not correct, or you have suggestions for.

Updated 05/28/17: updated for S3 new Console UI.
Updated 12/09/16: added complete Sample Code for Section #2.

Tags: , ,

Published:

Leave a Comment