In Eve, how can you safely store a user's password?

If you put the debugger in the startup file, you will see that the user's password is hashed, but when you look in the mongo collection, the user's password is stored in plain text. How do I save a user's password as a hash?

Here are my files:

run.py:

from eve import Eve
from eve.auth import BasicAuth

import bcrypt

class BCryptAuth(BasicAuth):
    def check_auth(self, username, password, allowed_roles, resource, method):
        # use Eve own db driver; no additional connections/resources are used
        accounts = app.data.driver.db["accounts"]
        account = accounts.find_one({"username": username})
        return account and \
            bcrypt.hashpw(password, account['password']) == account['password']

def create_user(*arguments, **keywords):
    password = arguments[0][0]['password']
    username = arguments[0][0]['username']
    user = {
        "password": bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()),
        "username": username,
    }
    return post_internal("accounts", user)


app = Eve(auth=BCryptAuth)
app.on_insert_accounts += create_user

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

      

settings.py:

API_NAME = "gametest"

CACHE_CONTROL = "max-age=20"
CACHE_EXPIRES = 20
MONGO_DBNAME = "gametest"
MONGO_HOST = "localhost"
MONGO_PORT = 27017
PUBLIC_ITEM_METHODS = ["GET"]
RESOURCE_METHODS = ["GET"]

accounts_schema = {
    "username": {
        "type": "string",
        "required": True,
        "unique": True,
    },
    "password": {
        "type": "string",
        "required": True,
    },
}

accounts = {
    # the standard account entry point is defined as
    # '/accounts/<ObjectId>'. We define  an additional read-only entry
    # point accessible at '/accounts/<username>'.
    "additional_lookup": {
        "url": "regex('[\w]+')",
        "field": "username",
    },

    # We also disable endpoint caching as we don't want client apps to
    # cache account data.
    "cache_control": "",
    "cache_expires": 0,

    # Finally, let add the schema definition for this endpoint.
    "schema": accounts_schema,
    "public_methods": ["POST"],
    "resource_methods": ["POST"],
}
games_schema = {
    "game_id": {
        "type": "objectid",
        "required": True
    },
    "title": {
        "type": "string",
        "required": True
    },
}

games = {
    "item_title": "game",
    "schema": games_schema,
}

orders = {
    "schema": {
        "game": {
            "type": "objectid",
            "required": True,
        },
    },
    "resource_methods": ["GET", "POST"],
}

DOMAIN = {
    "accounts", accounts,
    "orders": orders,
    "games": game,
}

      

+1


source to share


1 answer


There run.py

were several important things in yours that prevented you from authenticating:

  • In the trick, create_user

    you generated the salt with bcrypt.gensalt()

    , but you didn't store the salt anywhere. Salts are useful for preventing rainbow table , but you need to store them so that when you re-hash the password, you get the same result.
  • You use an event binding on_insert_accounts

    to modify the document before publishing it, but then return it post_internal

    instead of letting the event cursor fire your course. It might work, but I feel like you should just use the event hook as intended.

Here's the modified one run.py

:



from eve import Eve
from eve.auth import BasicAuth

import bcrypt

class BCryptAuth(BasicAuth):
    def check_auth(self, username, password, allowed_roles, resource, method):
        # use Eve own db driver; no additional connections/resources are used
        accounts = app.data.driver.db["accounts"]
        account = accounts.find_one({"username": username})
        return account and \
            bcrypt.hashpw(password.encode('utf-8'), account['salt'].encode('utf-8')) == account['password']

def create_user(documents):
    for document in documents:
        document['salt'] = bcrypt.gensalt().encode('utf-8')
        password = document['password'].encode('utf-8')
        document['password'] = bcrypt.hashpw(password, document['salt'])

app = Eve(auth=BCryptAuth)
app.on_insert_accounts += create_user

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

      

Yours settings.py

had a few typos, so I'm using the working version for a good estimate:

API_NAME = "gametest"

CACHE_CONTROL = "max-age=20"
CACHE_EXPIRES = 20
MONGO_DBNAME = "gametest"
MONGO_HOST = "localhost"
MONGO_PORT = 27017
PUBLIC_ITEM_METHODS = ["GET"]
RESOURCE_METHODS = ["GET"]

accounts_schema = {
    "username": {
        "type": "string",
        "required": True,
        "unique": True
    },
    "password": {
        "type": "string",
        "required": True
    }
}

accounts = {
    # the standard account entry point is defined as
    # '/accounts/<ObjectId>'. We define  an additional read-only entry
    # point accessible at '/accounts/<username>'.
    "additional_lookup": {
        "url": "regex('[\w]+')",
        "field": "username",
    },

    # We also disable endpoint caching as we don't want client apps to
    # cache account data.
    "cache_control": "",
    "cache_expires": 0,

    # Finally, let add the schema definition for this endpoint.
    "schema": accounts_schema,
    "public_methods": ["POST"],
    "resource_methods": ["POST"]
}
games_schema = {
    "game_id": {
        "type": "objectid",
        "required": True
    },
    "title": {
        "type": "string",
        "required": True
    }
}

games = {
    "item_title": "game",
    "schema": games_schema
}

orders = {
    "schema": {
        "game": {
            "type": "objectid",
            "required": True,
        }
    },
    "resource_methods": ["GET", "POST"]
}

DOMAIN = {
    "accounts": accounts,
    "orders": orders,
    "games": games
}

      

+3


source







All Articles