Registration Form REST API Best Practices

Issue

I’m building a SaaS application using the MEAN stack and have a few questions regarding how best to secure registration forms. Express.js is what I’m using to generate the endpoints (via angular-fullstack)

I have “tenants” that register using a registration form (name, email, password, etc.). This REST API is currently unsecured (POST /tenants).

My questions are:

  1. Should I somehow secure this POST? If so, how?
  2. If I’m not to secure the POST /tenants endpoint, how do I avoid someone writing a script to just create a whole bunch of tenants and attack my application?
  3. I want to use some sort of confirmation email, but is that good practice when registering a new tenant?

I’d love to get some feedback here on how best to proceed.

Thanks!

Solution

reCAPTCHA is the solution I chose to solve this same problem.

Quoting from Google’s developer site on reCAPTCHA an overview of integrating reCAPTCHA into a site follow. It’s worth noting Google’s reCAPTCHA codelab also includes an example for Java.

Overview

To start using reCAPTCHA, you need to sign up for an API key pair
for your site. The key pair consists of a site key and secret key. The
site key is used to invoke reCAPTCHA service on your site or mobile
application. The secret key authorizes communication between your
application backend and the reCAPTCHA server to verify the user’s
response
. The secret key needs to be kept safe for security
purposes.

First, choose the type of reCAPTCHA and then fill in authorized
domains or package names. After you accept our terms of service,
you can click Register button to get new API key pair.

Now please take the following steps to add reCAPTCHA to your site or
mobile application:

  1. Choose the client side integration:
  2. Verifying the user’s response

And being a Python fan, here’s an example I followed to implement this solution in Django (ref: Google’s codelab example):

1. Getting set up

Install and verify web server

This codelab can either be run locally or through the gcloud shell in
Google Cloud Platform. To get started with the gcloud shell go to
https://console.cloud.google.com/appengine/start.

Download the Code

Clone all the code for this codelab:

git clone https://github.com/googlecodelabs/recaptcha-codelab.git

For Python:

cd recaptcha-codelab/phase1-python
python server.py

In the web
browser, go to http://localhost:8080 to see the example UI without
reCAPTCHA integrated.

2. Registering with the reCAPTCHA Admin Console

… We recommend if you have
time that you follow through and create your own registration in the
admin console. Before you use reCAPTCHA in development or production
you will need to do this step.

First go to The reCAPTCHA Admin Site.

Choose ‘Invisible reCAPTCHA’ as the type of captcha.
enter image description here

Fill in the list of domains you wish to show your captcha. The reCAPTCHA site
key you create will only work on these domains. Save the site key and
secret key for the later stages.
enter image description here
enter image description here

3. Add Invisible reCAPTCHA to the frontend – Python

Let’s update the frontend to call Invisible reCAPTCHA with your
favorite text editor.

First let’s add a script tag to the html element in
feedback.html.

feedback.html

 <html>
 <head>
   <title>Suggestion page</title>
   <script src='https://www.google.com/recaptcha/api.js'></script>

Now update the attributes to the submit button. Add
class='g-recaptcha' data-sitekey="YOUR SITE KEY" and add a
data-callback.

feedback.html

     <button class="g-recaptcha"
       data-sitekey="6LfeHx4UAAAAAAKUx5rO5nfKMtc9-syDTdFLftnm"
       data-callback="onSubmit">Submit</button>

The full file should be:

feedback.html

<!DOCTYPE html>
<!DOCTYPE html>
<html>
<head>
  <title>Suggestion page</title>
  <script src='https://www.google.com/recaptcha/api.js'></script>
  <script>
    function onSubmit() {
      document.getElementById("demo-form").submit();
    }   
  </script>
  <style>
    body {
      font-family: Helvetica, sans-serif;
    }   
    .status-message {
      background-color: #5ff;
      margin-bottom: 10px;
      text-align: center;
    }   
    textarea {
      margin: 10px 0;
      resize: none;
    }   
  </style>
</head>
<body>
  <h3>Give us feedback on our webpage!</h3>
  <div class="status-message">%s</div>
  <form id="demo-form" action="/feedback" method="POST">
    Your comment <br><textarea name="feedback" cols="50" rows="5"></textarea><br>
    <!-- Replace this with your site key --> 
    <button class="g-recaptcha"
            data-sitekey="6LfeHx4UAAAAAAKUx5rO5nfKMtc9-syDTdFLftnm"
            data-callback="onSubmit">Submit</button>
  </form>
</body>
</html>

3. Validate the Invisible reCAPTCHA on the Server – Python

In “Registering with the reCAPTCHA Admin Console” we created a new
site key. As part of that process a site secret was created. The site
secret is necessary to validate the CAPTCHA solution. For the purposes
of this code lab you can use the default keys provided. These will
only work on localhost.

The validation of the reCAPTCHA token is done by sending a POST
request to https://www.google.com/recaptcha/api/siteverify. The
details are in Verifying the user’s response.

To validate the reCAPTCHA token, let’s update the server. First we
need to add the site secret and site verify constants.

server.py

SITE_VERIFY_URL = 'https://www.google.com/recaptcha/api/siteverify'
SITE_SECRET = '6LfeHx4UAAAAAFWXGh_xcL0B8vVcXnhn9q_SnQ1b'
RECAPTCHA_RESPONSE_PARAM = 'g-recaptcha-response'

Then we need to update our POST handler to verify the token.

server.py

def do_POST(self):
    self.set_headers();
    post_body = parse_qs(self.rfile.read(int(self.headers['Content-Length'])))

    success = False
    if RECAPTCHA_RESPONSE_PARAM in post_body:
      token = post_body[RECAPTCHA_RESPONSE_PARAM][0]
      resp = urllib.urlopen(
          SITE_VERIFY_URL, urllib.urlencode(
              {'secret': SITE_SECRET, 'response': token}, True)).read()
      if json.loads(resp).get("success"):
        success = True

    if success:
      message = 'Thanks for the feedback!'
    else:
      message = 'There was an error.'
    self.wfile.write(open(curdir + sep + 'feedback.html').read() % message)

The final file should look like this:

server.py

import json
import urllib
from os import curdir, sep
from urlparse import parse_qs
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer

SITE_VERIFY_URL = 'https://www.google.com/recaptcha/api/siteverify'
SITE_SECRET = '6LfeHx4UAAAAAFWXGh_xcL0B8vVcXnhn9q_SnQ1b'
RECAPTCHA_RESPONSE_PARAM = 'g-recaptcha-response'

class Handler(BaseHTTPRequestHandler):
  def set_headers(self):
    self.send_response(200)
    self.send_header('Content-type', 'text/html')
    self.end_headers()

  def do_GET(self):
    self.set_headers();
    self.wfile.write(open(curdir + sep + 'feedback.html').read() % '')

  def do_POST(self):
    self.set_headers();
    post_body = parse_qs(self.rfile.read(int(self.headers['Content-Length'])))

    success = False
    if RECAPTCHA_RESPONSE_PARAM in post_body:
      token = post_body[RECAPTCHA_RESPONSE_PARAM][0]
      resp = urllib.urlopen(
          SITE_VERIFY_URL, urllib.urlencode(
              {'secret': SITE_SECRET, 'response': token}, True)).read()
      if json.loads(resp).get("success"):
        success = True

    if success:
      message = 'Thanks for the feedback!'
    else:
      message = 'There was an error.'
    self.wfile.write(open(curdir + sep + 'feedback.html').read() % message)

if __name__ == '__main__':
  httpd = HTTPServer(('', 8080), Handler)
  httpd.serve_forever()

You’re all done! Now reload the server and give it a try. The
completed version can be found in final-python/server.py. You now have
a basic integration with reCAPTCHA to protect your form. In the
background, we are verifying the user and will sometimes show a
reCAPTCHA challenge to make sure that your website is protected from
abuse. More details and options can be found on our developer site.

Please note: I am an independant software developer with no affiliation with Google.

Answered By – MikeyE

Answer Checked By – Mildred Charles (AngularFixing Admin)

Leave a Reply

Your email address will not be published.