Bcrypt Hash возвращает TypeError («Unicode-объекты должны быть закодированы перед хэшированием») и Invalid Salt - PullRequest
0 голосов
/ 27 июня 2018

Я посмотрел на все вопросы StackOverflow, связанные с этим, но я просто не могу понять это. Когда я хэширую пароль и проверяю его на себя, он возвращает TypeError «Unicode-объекты должны быть закодированы перед хэшированием» с текущим кодом:

from scripts import tabledef
from flask import session
from sqlalchemy.orm import sessionmaker
from contextlib import contextmanager
import bcrypt

(Unrelated Python code...)

def hash_password(password):
     return bcrypt.hashpw(password.encode('utf8'), bcrypt.gensalt())


def credentials_valid(username, password):
    with session_scope() as s:
        user = s.query(tabledef.User).filter(
            tabledef.User.username.in_([username])).first()
        if user:

            return bcrypt.checkpw(password.encode('utf8'), user.password)
        else:
            return False

Когда я пытаюсь исправить эту ошибку, установив user.password= user.password.encode('utf8'), я получаю «Недопустимая соль».

Что не так с этим кодом?

UPDATE: Я храню пароли через ввод Flask от пользователя:

import json
import sys
import os
import plotly
import pandas as pd
import numpy as np
import plotly.graph_objs as go


from scripts import tabledef
from scripts import forms
from scripts import helpers
from flask import Flask, redirect, url_for, render_template, request, session, flash, Markup
from flask_socketio import SocketIO, emit

@app.route('/', methods=['GET', 'POST'])
def login():
    if not session.get('logged_in'):
        form = forms.LoginForm(request.form)
        if request.method == 'POST':
            username = request.form['username'].lower()
            password = request.form['password']
            if form.validate():
                if helpers.credentials_valid(username, password):
                    session['logged_in'] = True
                    session['username'] = username
                    session['email'] = request.form['email']
                    session['password'] = request.form['password']
                    return json.dumps({'status': 'Login successful'})
                return json.dumps({'status': 'Invalid user/pass'})
            return json.dumps({'status': 'Both fields required'})
        return render_template('login.html', form=form)
    user = helpers.get_user()
    return render_template('home.html', user=user)

@app.route('/signup', methods=['GET', 'POST'])
def signup():
    if not session.get('logged_in'):
        form = forms.LoginForm(request.form)
        if request.method == 'POST':
            username = request.form['username'].lower()
            password = helpers.hash_password(request.form['password'])
            email = request.form['email']
            if form.validate():
                if not helpers.username_taken(username):
                    helpers.add_user(username, password, email)
                    session['logged_in'] = True
                    session['username'] = username
                    session['email'] = request.form['email']
                    session['password'] = request.form['password']
                    return json.dumps({'status': 'Signup successful'})
                return json.dumps({'status': 'Username taken'})
            return json.dumps({'status': 'User/Pass required'})
        return render_template('login.html', form=form)
    return redirect(url_for('login'))

Это ошибка, которую я получаю:

/lib/python3.5/site-packages/flask/app.py", line 1718, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/home/suraj/Documents/Programming/current-projects/GW_Dining_Tracker/env/lib/python3.5/site-packages/flask/_compat.py", line 35, in reraise
    raise value
  File "/home/suraj/Documents/Programming/current-projects/GW_Dining_Tracker/env/lib/python3.5/site-packages/flask/app.py", line 1813, in full_dispatch_request
    rv = self.dispatch_request()
  File "/home/suraj/Documents/Programming/current-projects/GW_Dining_Tracker/env/lib/python3.5/site-packages/flask/app.py", line 1799, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/home/suraj/Documents/Programming/current-projects/GW_Dining_Tracker/Flaskex-master/app.py", line 34, in login
    if helpers.credentials_valid(username, password):
  File "/home/suraj/Documents/Programming/current-projects/GW_Dining_Tracker/Flaskex-master/scripts/helpers.py", line 64, in credentials_valid
    return bcrypt.checkpw(password.encode('utf8'), user.password)
  File "/home/suraj/Documents/Programming/current-projects/GW_Dining_Tracker/env/lib/python3.5/site-packages/bcrypt/__init__.py", line 101, in checkpw
    raise TypeError("Unicode-objects must be encoded before checking")
TypeError: Unicode-objects must be encoded before checking

1 Ответ

0 голосов
/ 27 июня 2018

Проблема в том, что вы берете значение из столбца SQLAlchemy String и передаете его в bcrypt.checkpw. String предназначен для строк Unicode, он предоставляет значения как str. Но bcrypt работает только с байтовыми строками, поэтому он ожидает bytes. Это то, о чем TypeError говорит, что «Unicode-объекты должны быть закодированы перед хэшированием».

В зависимости от того, какую серверную часть базы данных и библиотеку DB-API вы используете (и, в некоторых случаях, от того, как сконфигурирована ваша база данных), когда вы сохраняете значение bytes s в столбце String, это может сэкономить s.decode(), и в этом случае вы можете просто использовать user.password.encode(), чтобы вернуть те же байты, но это не так. Например, он также может просто сохранить, скажем, str(s). В этом случае, если хеш был bytes значением b'abcd', значением столбца будет строка "b'abcd'", поэтому при вызове encode вы получите b"b'abcd'", а не b'abcd'.

Самый простой способ справиться с этим - использовать Binary столбец 1 - или, может быть, лучше, Binary(60) 2 - для хранения хешей вместо String столбец. Любой DB-API, который поддерживает Binary, просто сохранит bytes как есть и вернет его как bytes, что именно то, что вам нужно.


1. Binary является необязательным типом. Если он отсутствует для вашего DB-ABI, может быть доступен тот же тип, что и BINARY. Если нет, просмотрите список типов и попробуйте другие типы, которые наследуются от _Binary. Те, у кого нет «большого» имени или аббревиатуры, вероятно, будут более эффективными, но в противном случае любой из них должен работать.

2. При настройках по умолчанию bcrypt печатаемых дайджестов всегда будут точно 60 байтов. В базах данных обычно можно хранить поля фиксированной ширины, например, BINARY(60), и искать их быстрее, чем в полях переменной ширины, например, VARBINARY. Можно просто использовать обычный BINARY, но он также может работать как VARBINARY или тратить пространство и работать как BINARY(255) и т. Д.

...