从Flask中的登录表单安全地发送密码

问题描述 投票:-1回答:2

我有一个使用会话的简单登录功能的网站。我认为它是安全的,因为我正在对密码进行哈希和腌制,但我已经意识到实际上(可能)正在发生的事情是我在POST请求中向服务器发送明文密码,然后我才对它进行散列和腌制。我以为我理解如何管理密码,但现在我想知道该怎么做。

这是我的代码:

import bcrypt
from argon2 import PasswordHasher, exceptions
import db as database

ph = PasswordHasher()


@app.route("/login", methods=["POST", "GET"])
def login():
    if check_logged_in(session, 0):
        return redirect("/index")
    else:
        error = ""  # set to nothing unless we get error below
        if request.method == "POST":  # if user submits form
            username = request.form["username"]  # retrieve username from form
            password = request.form["password"]  # retrieve password from form
            if verify_login(username, password):  # if their login is valid
                session["username"] = username  # update session with their username and redirect them to index
                return redirect("/index")
            error = "Invalid username or password"  # else update the error message
        return render_template("login.html", error=error)  # and return login with error message


def verify_login(username, password):
    db_hash, salt = database.retrieve_pw_salt(username)  # gets hashed/salted password and salt from database given a username
    if db_hash is None:
        return False  # invalid username
    try:
        ph.verify(db_hash, salt + password)
        return True  # valid username and password
    except exceptions.VerifyMismatchError:
        return False  # invalid password


def check_logged_in(session, required_privilege):
    if "username" not in session:
        return False  # not logged in
    elif session["username"] == "admin":
        return True  # logged in as admin
    elif session["username"] == "reception" and required_privilege <= 1:  # checks to ensure a reception isn't trying to access admin pages/features
        return True  # logged in as reception
    elif session["username"] == "teacher" and required_privilege == 0:  # checks to ensure a teacher isn't trying to access admin/reception pages/features
        return True  # logged in as teacher
    return False  # else not logged in

我想我认为这个代码将在客户端运行,但我认为不是。在这种情况下,我如何将我的用户的盐添加到客户端,然后将其与密码相结合,哈希并将其发送到我的服务器?

我的服务器在Ubuntu 18.04上使用uWSGI运行Nginx,如果这有所不同的话。

谢谢。

编辑:这是我在第一时间生成盐的方式:

def generate_salt():  # creates a salt
    salt = bcrypt.gensalt().decode("utf-8")
    return salt
python flask hash passwords
2个回答
1
投票

我正在使用HTTPS,但我认为这也被使用了。

所有安全措施都需要清楚地了解它们将保护您的内容,以及您需要做什么取决于您尝试覆盖的攻击媒介。

您的担心似乎是有人可以在途中读取密码:

但我已经意识到实际上(可能)正在发生的事情是我在POST请求中向服务器发送明文密码

但是,如果您在使用https时将密码作为纯文本发送,则表示您的密码不正确。 Https意味着服务器和浏览器之间的通道是加密的,并且......被认为是安全的(*)。加密两次不会增加任何安全性,即使看起来更多的工作应该给你“东西”。

如果您正在尝试防范可以控制浏览器或客户端计算机的攻击者,那么任何数量的客户端加密都无济于事;如果您尝试防范可控制服务器的攻击者,情况就是相同的。

可能泄漏密码的一种情况是POST期间未处理的异常。大多数网络应用程序通过电子邮件或Sentry等服务报告此类错误。与客户端加密(例如Django的@sensitive_post_parameters(...) https://docs.djangoproject.com/en/2.1/howto/error-reporting/#django.views.decorators.debug.sensitive_post_parameters)相比,Web框架通常有一种减轻这种方式的方法,这种方法更通用,更易于使用。

您将使用客户端加密的一种情况是,如果用户想要在服务器管理员无法读取的服务器上存储/检索数据。服务器无法与数据交互,因此在登录期间不是密码的用例。

(*)如果你想要保护一个可以解密https频道的人,那么你就会超出普通网站登录表所需的范围(如果你在NSA工作,你可能应该在内部而不是在SO上;-)

ps:你可能不应该解码('utf-8')bcrypt.gensalt()的结果 - 我没看过,但我无法想象它会返回utf-8数据。

附录:并非所有二进制数据都是utf-8。 base64是创建unicode表示的一种方法:

>>> b = b'\xc3\x28'   # byte sequence that is not valid utf-8
>>> b.decode('utf-8')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc3 in position 0: invalid continuation byte
>>> import base64
>>> base64.b64encode(b)
b'wyg='
>>> base64.b64encode(b).decode('ascii')
'wyg='
>>>

0
投票

从攻击者的角度来看,客户端哈希不提供额外的安全性。见https://security.stackexchange.com/questions/62280/client-side-hashing-of-password-before-sending-it-from-login-form

© www.soinside.com 2019 - 2024. All rights reserved.