如果在登录屏幕上,用户提交带有用户名和密码的表单,则密码以纯文本形式发送(即使使用Post,如果我出错,请更正我的错误)。
所以问题是,什么是保护用户及其密码不受可能窃听通信数据的第三方攻击的正确方法?
我知道HTTPS解决了这个问题,但是是否有任何方法可以使用标准HTTP协议(POST请求)来确保至少某种程度的安全性?(可能以某种方式使用javascript)
编辑我可能遗漏了一些重要的事情。
我所说的是一个页面——这是由PHP生成的登录页面,它当然以HTML文件的形式发送给HTTP GET请求中的用户。服务器和客户机之间没有(@jeremy powell)连接,因此我无法创建这种握手协议。我希望整个过程对用户是透明的——他想提交一个密码,而不是处理密码。
谢谢。
- 关于持久连接的好观点。
- 如果没有客户机使用密码,您可能无法完成这项工作,但用户不必看到这样的过程。他只需输入密码,您的PHP生成的代码(例如javascript)就可以为您处理所有的密码。
- 您描述的问题是HTTPS被发明的原因。如果你把一个秘密发送给客户机来加密密码,窃听者就可以在回程中嗅探它并解密密码。
- 所以在您的建议中,S可以是唯一的密码(或者用户名+密码以任何方式组合),因为这是用户拥有的唯一"秘密"。我说的对吗?因此,解决方案如下:server为html页面提供一个隐藏的表单字段r-用户输入密码,在发送密码之前,javascript计算h(r,s)并将其发送到服务器,甚至可能通过使用ajax-服务器计算h(r,s),并将其与接收到的进行比较,然后发送对ajax请求的响应,当其他身份验证通过-javascript将浏览器重定向到所需网页
- 但我猜最后一步是某种安全问题。不管怎样,我也将研究https选项。
- 我真的认为https是个好主意。:)不要把它用在任何事情上。一旦你得到了认证令牌(cookie),你就不再需要SSL了,所以你只需要在一个小地方使用它。
- @杰里米·鲍威尔——虽然你所描述的是常见的做法,但它也容易受到中间人的攻击,中间人可以从头部嗅出cookie并通过重用cookie来模仿用户。中间人攻击很难防御,除非你使用HTTPS
- @电磁炉:同意。
- 对于将来遇到这个问题的人:登录之后,您还需要保护会话cookie。(所以:使用https真的很容易。)
将HTTP与SSL结合使用将使您的生活更加轻松,您可以轻松地休息,非常聪明的人(至少比我聪明!)多年来一直在仔细研究这种保密通信方法。
- …和"但是我必须支付一个SSL证书!"这不是一个有效的投诉,因为这些天你可以花30美元买到它们。你的数据真的不值得30美元来保护吗?
- 如果您订阅的Web主机不支持添加SSL证书怎么办?
- @卡马留斯-然后你搬到一个真正的网络主机
- @Borntocode这在技术上意味着您需要有一个专用的IP,并且您需要拥有服务器硬件(或者至少是一个VP)才能使用HTTPS。共享的webhosts不能执行https,除非整个服务器受主机所有者证书的保护。
- 共享的webhosts当然可以做https,使用en.wikipedia.org/wiki/server_name_指示
- @布里安明顿,你是100%对的。我运行一个托管多个域的linode,其中只有一些域有ssl,一些域在非标准端口上运行ssl,等等。这也可以很容易地在非vps上完成,因为这只是向相关域的apache配置中插入几行而已。
安全认证是一个广泛的话题。简而言之,正如@jeremy powell提到的,总是倾向于通过HTTPS而不是HTTP发送凭证。它将消除许多与安全相关的头痛。
TSL/SSL证书现在很便宜。事实上,如果你根本不想花钱,有一个免费的letsencrypt.org——自动证书颁发机构。
您可以进一步使用caddyserver.com,它在后台调用letsencrypt。
现在,一旦我们把https排除在外…
您不应该通过post-payload或get参数发送登录名和密码。使用授权头(基本访问认证方案),其构造如下:
-
The username and password are combined into a string separated by a
colon, e.g.: username:password
-
The resulting string is encoded using
the RFC2045-MIME variant of Base64, except not limited to 76
char/line.
-
The authorization method and a space i.e."Basic" is then
put before the encoded string.
source: Wikipedia: Authorization header
号
这看起来有点复杂,但事实并非如此。有很多好的库可以为您提供开箱即用的功能。
您应该使用授权头有几个很好的理由
这是一个标准
这很简单(在你学会如何使用它们之后)
它允许您在URL级别登录,如:https://user:[email protected]/login(例如chrome会自动将其转换为Authorization头文件)
重要提示:正如@zaph在下面的评论中指出的那样,将敏感信息作为get查询发送不是一个好主意,因为它很可能最终会出现在服务器日志中。
。
- 将凭据(密码)作为get参数发送的问题是,用户/密码对可能会出现在服务器日志中,这不是一个好主意。最好是邮寄凭证。
- 啊…一点也不。您看到的屏幕截图会被修改,以说明浏览器中发生了什么。当您点击EnterBrowser时,将转换您的URL,创建一个Authorization头文件。试试看。日志会提醒清理。当然,如果您从服务器发出一个调用(如果这是您所担心的场景),那么您当然应该以编程方式生成头。
- 你不能记录你看不到的东西。浏览器中的username:password@url转换为:url+Authorization请求头。至于get查询…就像我说的,使用授权头。更好。
- 我目前的项目我需要所有路线的JWT授权。为此,必须首先通过登录来获取令牌。/auth/login允许post和get方法。Post预期与login和password具有有效载荷,而get需要基本授权(header)。当您从SPA或移动应用程序登录时,您将使用有效负载调用Post或使用头调用get,或者作为一个纯粹的方便类型,将'user:pass@url'输入浏览器地址栏,该地址栏将转换为带有头的get。在所有情况下,原木都是干净的。
- 希望我能在扎夫说清楚。但是不管你在这里发现什么,请为自己运行一些测试,并查看我的帖子中的链接,这些应该会有所帮助。
- @如果我的英语不够清楚,扎夫我道歉。另外,我已经读了你的评论两遍了。我也这么做了:)老实说,我从来没有建议用as-get参数发送敏感数据。我完全同意你的观点。在昨天的评论中,我描述了生产设置,并提到了您的关注点,希望它可以帮助澄清一些事情。希望有帮助。
- @扎夫感谢你的投入。我已经更新了我的答案(包括内容和图片)
- 我同意:"我从未建议用as-get-params发送敏感数据。"但是一些开发人员这样做了,我只是觉得您的声明会因为这样的澄清而受益。我将删除我的大部分评论,稍后再讨论,它们不再相关。
- 你不能解决这个问题:保护敏感数据不被中间人窃听。重点不是寻找一种替代和/或更标准化的方法来传递凭证,而是通过不安全的渠道保护凭证。
- 应该强调的是,只有当您已经用tls/ssl加密连接时,此解决方案才有效。base64不是加密。
您可以使用挑战响应方案。假设客户机和服务器都知道一个秘密,那么服务器可以通过以下方式确保客户机知道密码(而不泄露):
服务器向客户机发送随机数r。
客户机将h(r,s)发送回服务器(其中h是加密散列函数,如s h a-256)
服务器计算h(r,s)并将其与客户机的响应进行比较。如果匹配,服务器知道客户机知道密码。
编辑:
这里有一个关于r的新鲜度和HTTP是无状态的事实的问题。这可以通过让服务器创建一个只有服务器知道的秘密(称为q)来处理。然后协议如下:
服务器生成随机数r,然后发送给客户机h(r,q)(客户机不能伪造)。
客户机发送r,h(r,q),并计算h(r,s),然后将其全部发送回服务器(其中h是一个加密散列函数,如s h a-256)
服务器计算h(r,s)并将其与客户机的响应进行比较。然后它需要r并计算(再次)h(r,q)。如果客户机的h(r,q)和h(r,s)版本与服务器的重新计算匹配,则服务器认为客户机已通过身份验证。
要注意,由于客户端不能伪造h(r,q),h(r,q)充当cookie(因此可以实际实现为cookie)。
另一个编辑:
以前对协议的编辑是不正确的,因为任何观察到h(r,q)的人似乎都能用正确的散列值重放它。服务器必须记住哪些R不再是新的。我在写这个答案,所以你们可以编辑一下,找出一些好的答案。
- +1-您需要使用javascript(或flash/silverlight/etc)在客户端计算响应。
- 我只是想说同样的话。不确定是否有任何JS库可以这样做。跟踪这篇文章会很有趣。
- R的发送可以用一个隐藏的表单字段来完成(然后服务器需要验证它接收到的R是新的并且以前没有使用过)。
- 不能阻止中间人或冒充攻击。例如,通过WiFi。看来这只会给人一种错误的安全感。
- 这可以防止被动攻击,但中间人仍然可以攻击。
- 此外,它还要求服务器知道原始密码。
- @道格拉斯:是的。不幸的是。有没有一种方法可以通过将密码哈希保存到服务器来做到这一点?
- 不要重新发明密码!(生产中)
如果你的网络主机允许,或者你需要处理敏感数据,那么使用https,period。(阿法克法律经常要求这样做)。
否则,如果您想通过HTTP做一些事情。我会这样做的。
服务器将其公钥嵌入登录页。
客户机填充登录表单并单击提交。
Ajax请求从服务器获取当前时间戳。
客户端脚本连接凭证、时间戳和salt(从模拟数据(如鼠标移动、按键事件)散列),并使用公钥对其进行加密。
提交结果哈希。
服务器解密哈希
检查时间戳是否足够新(仅允许5-10秒的短窗口)。如果时间戳太旧,则拒绝登录。
将哈希存储20秒。在此时间间隔内拒绝登录的相同哈希。
对用户进行身份验证。
这样就可以保护密码,并且不能重播相同的身份验证哈希。
关于会话令牌的安全性。这有点难。但有可能使重用被盗会话令牌变得更加困难。
服务器设置一个包含随机字符串的额外会话cookie。
浏览器在下一次请求时发送回此cookie。
服务器检查cookie中的值,如果它不同,则会破坏会话,否则一切正常。
服务器用不同的文本再次设置cookie。
因此,如果会话令牌被盗,并且其他人发送了一个请求,那么在原始用户的下一个请求中,会话将被销毁。所以如果用户主动浏览站点,经常点击链接,那么窃贼就不会带着偷来的令牌走得太远。此方案可以通过对敏感操作(如帐户删除)要求另一个身份验证来加强。
编辑:请注意,如果攻击者使用不同的公钥设置自己的页面,并将请求代理到服务器,这不会阻止MITM攻击。为了防止出现这种情况,必须将公钥固定在浏览器的本地存储中或应用程序中,以检测这些技巧。
关于实现:RSA可能是最著名的算法,但是对于长密钥来说,它是相当慢的。我不知道的PHP或JavaScript实现有多快。但可能有更快的算法。
- 在这种情况下,密码是否真正受到保护?难道有人不能嗅出发送的内容,然后用公钥解密,然后在稍后使用时更新时间戳吗?我错过什么了吗?
- @Michaellinahl非对称加密意味着只有永远不会离开服务器的私钥才能用于解密。公钥只能用于加密。
- 浏览器和服务器之间的计算机可以在登录页面上更改公钥。
我将使用带有Ajax或多表单提交的服务器端和客户端Diffie-Hellman密钥交换系统(我推荐前者),尽管我在Internet上没有看到任何好的实现。记住,JS库总是可以被MITM损坏或更改。在某种程度上,本地存储可以用来帮助解决这一问题。
您可以使用SRP在不安全的通道上使用安全密码。其优点是,即使攻击者嗅探流量或破坏服务器,他们也不能在不同的服务器上使用密码。https://github.com/alax/jsrp是一个javascript库,支持浏览器或服务器端(通过节点)的HTTP安全密码。
HTTPS非常强大,因为它使用了非对称加密技术。这种类型的加密技术不仅允许您创建加密的隧道,而且可以验证您与正确的人交谈,而不是黑客。
这里是Java源代码,它使用非对称密码RSA(由PGP使用)进行通信:http://www.hushmail.com/services/downloads(服务/下载)/
你可以为你的主机使用ssl。有免费的ssl项目,比如letsencrypt。https://letsencrypt.org网站/
- 检查第三句话。(此外,请务必阅读其他答案,以确保不会添加不太详细的副本。)