我有一个带有使用会话的简单登录功能的网站。我认为这是安全的,因为我在对密码进行哈希处理和加盐处理,但是我已经意识到,实际上(可能)发生的事情是,我在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
答案 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)
这是一个老问题,但我想知道同样的问题,所以我想我应该与以后可能会发现它的人分享我的发现。
大多数网站不会在客户端更改密码。这有两个原因。
对客户端散列的支持确实不多。例如,Javascript 不支持 SHA。
散列客户端实际上并没有使网站更加安全。 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