Help with adding security features in a Flask REST API

Hi everyone!

I have a school project where we need to add security features such as authentication, authorisation, encryption, and event monitoring for a Flask REST API.

I have been reading on them, and I am done with the CRUD part. I followed the code from leon-sleepinglion to make a basic one here:

from flask import Flask
from flask_restful import Api, Resource, reqparse

app = Flask(__name__)
api = Api(app)

users = [
    {
        "name": "Mar Lee",
        "username": "mar.lee",
        "authorisation": "Scientist"
    },
    {
        "name": "Jun Huang",
        "username": "jun.huang",
        "authorisation": "Astronaut"
    },
    {
        "name": "Jen Oh",
        "username": "jen.oh",
        "authorisation": "Astronaut"
    }
]


class User(Resource):
    def get(self, name):
        for user in users:
            if (name == user["name"]):
                return user, 200
        return "User not in the database.", 404

    def post(self, name):
        parser = reqparse.RequestParser()
        parser.add_argument("username")
        parser.add_argument("authorisation")
        args = parser.parse_args()

        for user in users:
            if (name == user["name"]):
                return "{} is already registered".format(name), 400

        user = {
            "name": name,
            "username": args["username"],
            "authorisation": args["authorisation"]
        }
        users.append(user)
        return user, 201

    def put(self, name):
        parser = reqparse.RequestParser()
        parser.add_argument("username")
        parser.add_argument("authorisation")
        args = parser.parse_args()

        for user in users:
            if (name == user["name"]):
                user["username"] = args["username"]
                user["authorisation"] = args["authorisation"]
                return user, 200

        user = {
            "name": name,
            "username": args["username"],
            "authorisation": args["authorisation"]
        }
        users.append(user)
        return user, 201

    def delete(self, name):
        global users
        users = [user for user in users if user["name"] != name]
        return "{} is deleted.".format(name), 200


api.add_resource(User, "/user/<string:name>")

if __name__ == '__main__':
    app.run(debug=True)


"""source:
https://gist.github.com/leon-sleepinglion/97bfd34132394e23ca5905ec730f776a""""


Then I also tried working on one with authentication from Raghav Agrawal.

from flask import Flask, request
from flask_restful import Resource, Api

app = Flask(__name__)
api = Api(app)

class Welcome(Resource):
    def get(self):
        return {"Welcome to": "SciTec App"
        }

api.add_resource(Welcome, '/')

if __name__ == '__main__':
    app.run(debug=True)

"""source https://www.analyticsvidhya.com/blog/2022/01/rest-api-with-python-and-flask/"""

In a separate file, based on the same tutorial, I was able to make an authentication file:

from flask import Flask
from flask_restful import Resource, Api
from flask_httpauth import HTTPBasicAuth
app = Flask(__name__)
api = Api(app, prefix="/api/v1")
auth = HTTPBasicAuth()
USER_DATA = {
    "admin": "authorised"
}

#password verification
@auth.verify_password
def verify(username, password):
    if not(username and password):
        return False
    return USER_DATA.get(username) == password
class PrivateResource(Resource):
    @auth.login_required
    def get(self):
        return {"authorised": "astronaut"}

api.add_resource(PrivateResource, '/private')
if __name__ == '__main__':
	app.run(debug=True)

I have several questions:

  1. How can I add the security features authentication, authorisation, encryption, and event monitoring with either of these? Should they be in a separate Python file?

  2. Is the only way to execute the CRUD API through Postman or other API tools? I believe it can be done through ‘curl’, but we need to demonstrate it in a CLI. I am not sure if it is possible to demonstrate the security function requirements from the function mentioned above through Postman.

Thank you!

Since this is a project for learning how these things work, I strongly recommend learning one other absolutely essential aspect of authorization: Do NOT store people’s passwords in clear text! Instead, store a hashed version of the password - something that can’t be converted back into the original password - and when someone authenticates, you hash the password they provided, compare it to the stored hash, and see if they match. If the password is correct, the hash will match. (If the password is incorrect, there is a minuscule chance that the hash will match regardless - called a hash collision - but with a properly designed hash function, this is less likely than that you’ll win the lottery and then get struck by lightning on the way to collect your winnings.)

One popular method of password hashing is called bcrypt. It’s highly likely you’ll encounter this in the real world. For your project, though, I would recommend scrypt which is available in Python’s standard library. You can use it something like this:

>>> import hashlib, os
>>> salt = os.urandom(16)
>>> hash = hashlib.scrypt(b"authorised", salt=salt, n=4096, r=32, p=16)

Store both the hash and the salt. Later, when someone tries to log in, use the salt you have stored for that user (you should use different salt for every user, and fresh salt every time someone changes their password), and generate a hash for the password that was entered.

This is a VERY high level overview and omits a lot of detail, but you can take this as a basis for your own research :slight_smile:

3 Likes

Thank you, Chris! You are right, the passwords should not be visible. Will do more research, and revise this some more. :slight_smile: Much appreciated.

1 Like