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

时间:2019-03-22 12:54:35

标签: python flask hash passwords

我有一个带有使用会话的简单登录功能的网站。我认为这是安全的,因为我在对密码进行哈希处理和加盐处理,但是我已经意识到,实际上(可能)发生的事情是,我在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

3 个答案:

答案 0 :(得分: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)相比,网络框架通常可以缓解这种情况,这种方法更通用,更易于使用。

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

(*),如果您想防止有人可以解密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='
>>>

答案 1 :(得分:0)

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

答案 2 :(得分:0)

这是一个老问题,但我想知道同样的问题,所以我想我应该与以后可能会发现它的人分享我的发现。

大多数网站不会在客户端更改密码。这有两个原因。

  1. 对客户端散列的支持确实不多。例如,Javascript 不支持 SHA。

  2. 散列客户端实际上并没有使网站更加安全。 MITM 攻击者可以更改 javascript 并简单地关闭散列。因此,实际上,它只会有助于防止被动窥视。

以下是一些帮助我理解的主题: https://security.stackexchange.com/questions/53594/why-is-client-side-hashing-of-a-password-so-uncommon https://security.stackexchange.com/questions/42062/how-do-you-verify-if-an-app-is-using-secure-comms