Как в Eve можно безопасно хранить пароль пользователя?

Если вы поместите отладчик в файл запуска, вы увидите, что пароль пользователя хеширован, но когда вы посмотрите в коллекцию монго, пароль пользователя хранится в виде простого текста. Как сохранить пароль пользователя в виде хеша?

Вот мои файлы:

запустить.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's 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()

настройки.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's 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,
}

person sam    schedule 20.11.2014    source источник


Ответы (2)


В вашем run.py было несколько важных вещей, которые мешали вам пройти аутентификацию:

  • В обработчике событий create_user вы генерировали соль с помощью bcrypt.gensalt(), но нигде не сохраняли соль. Соли полезны для предотвращения атак радужных таблиц, но их нужно сохранять, чтобы при попытке хешируйте пароль снова, вы получите тот же результат.
  • Вы используете обработчик событий on_insert_accounts для изменения документа перед его публикацией, но затем возвращаете post_internal вместо того, чтобы позволить обработчику событий идти своим чередом. Это может работать, но мне кажется, что вы должны просто использовать обработчик событий так, как это было задумано.

Вот модифицированный 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's 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()

В вашем 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's 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
}
person Jen Garcia    schedule 20.11.2014
comment
document['salt'] = bcrypt.gensalt() и bcrypt.hashpw(password.encode('utf-8'), account['salt']) == account['password'], и это сработало. Спасибо, Джен. - person sam; 21.11.2014

Я сделал что-то подобное, чтобы безопасно хранить пароль пользователя и блокировать создание администратора без ключа авторизации:

class TokenAuthCutom(TokenAuth):
    def check_auth(self, token, allowed_roles, resource, method):
        payload = decode_token(token)
        # check if payload is valid and if allowed_roles is not empty check if role is valid
        return payload and (not allowed_roles or payload['roles'] in allowed_roles)

def before_insert_user(documents):
    body = request.get_json(force=True)
    admin_key = body.get('admin_register_key')
    for document in documents:
        password = document['password'].encode('utf-8')
        document['password'] = bcrypt.hashpw(password, bcrypt.gensalt()).decode('utf-8')
        document['roles'] = 'admin' if admin_key is not None and admin_key == ADMIN_REGISTER_KEY else 'client'


if __name__ == '__main__':
    app = Eve(auth=TokenAuthCutom)
    app.on_insert_users += before_insert_user
    app.run(host=HOST, port=PORT)
person Ângelo Polotto    schedule 02.08.2018