如何在不登录的情况下保护 Web 服务
Posted
技术标签:
【中文标题】如何在不登录的情况下保护 Web 服务【英文标题】:How To Secure Web Service Without Login 【发布时间】:2012-08-09 00:39:29 【问题描述】:我有一个与网络服务对话的移动应用程序(目前是 ios,很快是 android)。没有登录,数据不是私人的。基本上,该应用会发布一个标记(经度、纬度)并获取最近的 25 个标记以显示在地图上。
这是一个非常微不足道的应用程序,我无法想象有人会付出巨大的努力来滥用网络服务。但是,我可以看到有人在发布许多标记时很有趣。我最担心的是有人在运行一个推送许多请求的脚本(使用昂贵的带宽并让我的应用数据变得毫无意义)。
我慢慢得出结论,这并不安全。最好的答案是“不要这样做”。不要提供未经身份验证的 Web 服务。没有多少服务如此开放。 Google 的 You Tube API 是开放的,但大多数都不是。不幸的是,我别无选择。因此,经过几天的研究,这是我的想法。请注意,我离安全专家还很远,我相信我的方法可以改进。但这可能会为您指明正确的方向。希望更有经验的人可以加入并纠正/改进这一点。我发现this article 和 cmets 特别有帮助。
消息级安全
我将使用哈希加密来保护消息。客户端和 Web 服务都保留一个共享密钥的副本,该密钥用作盐以从 URL 和所有 POST 参数创建散列。散列作为附加参数传递,并且在另一端重建和比较散列(使用共享密钥作为盐)。在您了解任何移动客户端代码都可以在几分钟内进行逆向工程之前,这非常好。在这一点上,这条防线完全没有用。
客户措施
客户端将消息速率限制作为限制诚实用户发送的消息数量的措施。再一次,这对于越狱移动设备的攻击者来说毫无用处。
服务器端安全
因此,服务器端必须尽可能多地采取额外的安全措施,以独立于假设您的客户端(和共享机密)被泄露的情况。这是我所拥有的:
一个 msg arg 是用于限制重放攻击的 UTC 时间。这应该可以防止攻击者在服务器上重复触发相同的消息。
服务器通过 IP 进行速率限制。是的,IP 很容易被欺骗,并且代理切换是孩子们的游戏,但是当您拥有这么少的东西时,一切都会有所帮助。
当然,服务器会严格验证所有参数,使用参数化查询并且不返回异常。
传输级安全
不幸的是,我相当有信心在没有注册过程的情况下颁发单个客户端 SSL 证书是不可能的。而且因为我使用的是 msg 哈希检查(而且我的数据不是私有的),所以我不完全确定 SSL 带来了什么。但是,我可能会使用 SSL(带有一个应用程序范围的证书),因为它增加了另一个级别的安全性,部署起来很容易且成本低廉(尽管每个 msg 都会花费额外的连接时间)。
我的方法中的大洞
我被警告说,如果该应用程序变得流行,那么有人会破坏客户端上的共享机密。仅仅因为他们可以而且他们可能会将其发布在互联网上。所以实际上这一切都归结为服务器端。不幸的是,我无法识别和阻止攻击者。这是我非常喜欢的。
最后的恳求
经过几天的研究,这就是我所拥有的。但我想要更多。我将特别感谢任何加强服务器端的想法。所以,我把我所有的 SO 点都作为赏金。是的,先生,全97分!
【问题讨论】:
可能值得添加速率限制?这可能是一个更简单的解决方案。 @Velox 您的意思是限制每个客户端(每天)的请求数吗?如果是这样,是的,我正在应用程序上这样做。但是我看不到如何通过 IP 来做服务器端(这总比没有好,但我希望有更好的东西可用)。 您是否维护可以将这些数据推送给您的已注册设备或用户的列表。如果不是,您是否希望它在消息中......或者任何人都可以推送数据并使用它吗? @VamsiMohanJayanti:没有注册过程。该应用程序已下载,您已关闭。要求是没有登录。我可以想象在第一次加载时在幕后进行设备注册——但这似乎并不比密钥解决方案更安全,服务器上的负载也更多。如果我错过了你的观点,请告诉我。 我喜欢@kuba 的解决方案,但想警告可能过早的优化。如果您确实创建了一个流行的应用程序并且您确实收到了垃圾邮件,那么您是否也真的重新设计了您的 API(如果只是为了处理扩展问题)?您是攻击者,可以轻松破解您的客户端,但您也可以更新您的客户端/服务器并使他们的破解无效... 【参考方案1】:实际上在您的特定情况下,由于它目前是一个仅限 iOS 的应用程序,因此有一个解决方案。
在用户第一次下载并运行应用程序后,应用程序会访问带有 GUID 的 /access_token/create
API,并通过 Apple 的推送通知将其中继回应用程序。
App 存储此 access_token,并在所有后续请求中使用它。您的实际 API 可以根据 access_token 进行速率限制。
基本上,您让 Apple 完成所有艰苦的工作,以确保初始请求来自实际的 iOS 设备。
将其扩展到桌面客户端是可能的,但在某种程度上会破坏用户体验。只需更改步骤 1 以允许 /access_token/create
接受任意请求,如果请求不是来自 iOS 设备,则强制用户在向他们发出 access_token 之前验证他们的电子邮件地址/解决验证码等。
Android 设备(不是很熟悉)可能有类似的推送通知机制,在这种情况下您可以使用它,或者可能没有推送通知机制,在这种情况下,您可能会让您的 Android 用户遭受所列的不便以上。
【讨论】:
这很有趣。您能否再详细说明一下,因为现在还不清楚(至少对我而言)?你说的这个/access_token/create
API 是什么?是苹果的服务吗?它如何确保来自 iOS 设备?服务器如何知道特定的 GUID 是以这种方式生成的?如果 iOS 设备越狱了怎么办?
/access_token/create
API 是您自己在服务器上提供的任意 URL。当应用程序第一次启动时,它会调用此 URL 以及可以通过注册推送通知获得的设备令牌。 (整个流程由 Apple 记录在 developer.apple.com/library/mac/#documentation/…)。服务器知道特定的 GUID 是以这种方式生成的,因为服务器自己生成了它,并保留了它已分发的 access_tokens 的列表。
关于你问题的最后一部分,我推荐你apple.stackexchange.com/questions/57225/…
非常好。我什至没有听说过 APN。谷歌确实有一个push service。我没有资格说这是否提供了与 APN 类似的安全级别。我将进一步了解这些服务。非常感谢。
只要这个解决方案真正适用于 iOS 和 Android,并且没有与越狱相关的缺陷或其他“问题” - 我真的很喜欢它,因为它可能是最容易实现的。它应该是这里的赢家。【参考方案2】:
我曾经听说过这个想法,在谈到寻找垃圾邮件问题的全局解决方案时:强制您的客户执行一些耗时的计算。
确切地说:找到一些计算算法,可以在眨眼之间为一对x
和y
计算一些z
,但是计算z
需要相当长的时间仅给出x
。我无法提供实际的算法,但我确信有很多算法符合这个标准。
现在整个过程应该如下所示:
-
在第一次客户端请求时生成一些
session_id
并为此session_id
一对x
和y
。
为您的客户提供session_id
和x
。
客户端一收到数据就可以开始计算(在一些与用户交互无关的后台线程中)。
要请求标记,客户必须提供session_id
和计算出的z
。
您可以快速验证客户的z
是否正确,因为您已经有了x
和y
,让您轻松搞定。
(选项 1)对于每个 session_id
,存储请求的次数/频率。当您怀疑它被滥用时 - 强制重新生成 x
和 y
。
(选项 2)在每次连续请求 session_id
时强制使用新的 x
和 y
。
在 6 和 7 之间进行选择实际上是一种调整,这取决于算法的复杂性与对标记数据库的预期“公平”使用。如果您的估计是好的 - 邪恶的客户端永远不会获取太多数据或使您的服务器过载。
希望对你有帮助。
【讨论】:
有趣。下周我需要花时间研究一下。我一直保持这个问题,直到赏金的最后一天,但我喜欢这是一个新鲜的想法。谢谢。 一个可能的候选问题是discrete logarithm problem:对于方程g^x = h (mod p)
[其中p
是素数,g
是乘法群模p
的生成器],求解对于给定基础g
和解决方案h
的电源x
。 g^x (mod p)
计算容易,log_g(h) (mod p)
计算困难。【参考方案3】:
在客户端您真的无能为力。您必须将整个应用程序(包括任何密钥或任何其他保护机制)提供给您的用户。如果恶意用户想对您的 Web 服务进行恶作剧,他将不得不进行一些逆向工程才能访问您的 Web 服务。你可以让它变得更难,但无论你怎么努力,你都无法阻止它。
我只是实现一些服务器端速率限制(每个 IP 地址),而不再担心这个。这是一场你无法赢得的战斗。如果有人真的想伤害您的服务器,他可以在不了解您的 Web 服务协议的情况下直接 DDOS。
此外,在第一次连接时自动为每个用户生成唯一的密钥或证书根本没有帮助。在攻击者对您的协议进行逆向工程后,他知道如何处理所有这些并且不必遵守您的规则。每次遇到速率限制时,没有什么能阻止他从您的服务器请求新密钥。
Kuba Wyrostek 描述的方法可行 - 让客户端执行一些耗时的计算,您可以在允许处理请求之前快速检查。但这不会花费太长时间,否则您的用户会抱怨电池寿命缩短。此外,攻击者可能会使用更强大的桌面硬件而不是另一部 iPhone。
最后一点 - 你真的认为这是必要的吗?您不希望您的用户必须注册,因此您的数据或服务不能太重要。那么,对你的应用程序进行逆向工程并用请求淹没你的服务器会得到什么?
【讨论】:
好点,尤其是最后一点。数据完全不重要(除了作弊游戏的价值)。但是,至少(以及服务器端速率限制),我将使用哈希来保护 msg 以强制进行逆向工程——这对于一个大障碍来说似乎是一个很小的成本。谢谢。【参考方案4】:我实际上一直在寻找实施其中一些想法的理由。到目前为止很好的问题和答案。
我同意@Kuba Wyrostek 的观点,将其视为垃圾邮件问题是解决方案的一部分。特别是如果您的应用程序将包含文本消息(添加商店、服务或消息),您可能会发现向您的应用程序发送垃圾邮件的常见原因是宣传某些内容。这将导致我的第一个建议:
1) 将每条消息的有效性视为 0% 到 100% 有效的百分比。在服务器端开发一个流程以使用启发式方法将消息标记为或多或少有效。这将允许您将一些附加方法(例如强制客户端计算复杂值)仅针对需要它的那些请求。您还可以更轻松地记录和审查可能的滥用行为(并且在针对该滥用行为后更轻松地清除它)。
但是,在垃圾邮件大战中,您的应用确实比电子邮件服务器具有强大的优势 - 您可以控制对话的双方。这种情况实际上让我想起了另外两个您可能会觉得有帮助的相关情况:卫星付费电视“战争”和即时通讯软件克隆“战争”。 (例如,请参考 Black Sunday hack 上的 Jeff Atwoods 帖子)。以下是这些僵局中的一些想法,可能会帮助您在猫捉老鼠的游戏中领先一点:
2) 要求客户端发送额外数据 - 尽可能多的有关请求的数据。在 iOS 上,发送位置的准确度指标。在 Android 上,您实际上可以获得原始 GPS 数据,例如星历信息。然后,您可以(可能不是马上,而是稍后)开始检查这些数据的有效性。这迫使某人对请求进行逆向工程以更加努力地工作。例如,如果他们将 GPS 卫星发送到视野中,您可以对照公开数据进行确认。
3) 将您的对手强加到移动设备上 - 正如@Sven 所说,您的攻击者可能使用台式电脑,这意味着“计算成本高”的请求可能变得微不足道。不要让他们这样做(或至少让他们更努力地工作)。例如,您可以让客户端计算一些数学函数(由服务器发送),并根据手机型号查看它是否需要正确的毫秒数才能完成。或者使用来自服务器的数据执行小型 3D 渲染任务,这依赖于硬件裁剪行为。散列结果并将其发回。所有这些都在一个范围内——它是一个多任务操作系统。但这会很有帮助。
4) 动态处理它们 - 发送需要在客户端上下文中计算的算法位。 Apple 对要解释的远程代码感到有些好笑,但是像发送一些不会呈现给用户的 javascript 之类的东西可能会起作用。该代码可能会提出各种难以预先预料到的独特问题(屏幕分辨率、浏览器版本、WebKit 怪癖)。随着他们的追赶,您可以通过这些获得更多创意。
5) CAPTCHA - 如果您的启发式方法开始看到可疑数据,请强制他们进行身份验证。如果您有一个多语言应用程序,它可能就像将图片或 unicode 字符与另一个匹配一样简单。以您以后可以更新的方式呈现它。
无论如何 - 一些额外的想法。祝你好运!
【讨论】:
好主意。我很欣赏@Kuba 和你自己的整个垃圾邮件方法。当我有时间时,我需要评估我实施它们的程度。我不确定我的应用程序需要太多额外的 cpu 工作。但对于具有更重要数据的其他应用程序来说将非常有用。在我的情况下,我没有用户文本输入,任何文本响应都作为整数传递并打开客户端。我喜欢一个简单的验证码的想法,用于阻止脚本的可疑请求。我现在有选择。我只需要确定一个比这些响应之前更好的平衡点。谢谢。 请注意您控制对话双方的声明。从安全的角度来看,您无法控制客户端。黑客可以将应用程序代码修改为其他内容。所以你需要一些方法来确保它不被修改。强制攻击者使用移动设备对 PC 没有任何保护 - 他们只是一个易受攻击的对象。【参考方案5】:在服务器端实现速率限制的最简单方法是使用 Web 服务器插件/模块。例如,如果您的服务在 apache 上运行,请安装并配置 mod_evasive。这将允许您根据 IP 地址、服务/URL 等进行速率限制。Mod Evasive 将比您自己实施的更有效。
如果您使用密钥,您需要有某种基于验证码的方式让客户在注册时获取密钥。您可以让它删除滥用用户的帐户。是的,当然一个参数是时间戳,它将在服务器端被验证为最近和过去。密钥将与时间戳一起加密整个有效负载,并作为附加参数添加。以每个密钥为基础存储请求的频率最终需要某种循环数据库,除非您只检查最后一个请求的新近度。
没有纯粹的客户端速率限制会产生任何影响。甚至从未见过您的客户端的人就可以在网络上发现您的 API。我怀疑共享秘密是否会长期有效。
您会发现许多不需要密码且仅受速率限制的 Web 服务……例如,twitter API 提供了许多速率受限的未经身份验证的 API 服务。
【讨论】:
关于 Web 服务器插件/模块的重要提示。我对开放 Web 服务的数量持正确态度。很多都是新的!谢谢。【参考方案6】:这是另一个“解决方案”:
不要在这个问题上浪费时间。因为:
您不会向世界公开公共接口,因此您可以随时通过更新 Web 服务和更新您的应用来更改您的 Web 服务接口。 该应用程序“非常琐碎”(正如您所说的那样),目前可能没有太多使用 您现在可能有更好的事情要做,只是在浪费时间如果存在可疑的性能或查询峰值,请使用耗时最少的解决方案:
引入保存在您的应用中的密码 (clientid)(阻止 95% 的这些用户),如果其他程序员想要合法访问您的服务,该 clientid 以后可用于识别不同的客户端 引入速率限制(如上所述)这将在 99.99% 的情况下解决您的问题,您可以立即开始工作并编写出色的新功能。
【讨论】:
公平点和可能我会做的事情不是客户绝对坚持现在或将来没有用户注册/密码。她相信吸收将依赖于此。请问有经验吗?我很想听听一位开发人员在没有安全性的情况下将 API 投入使用。 正如@dsummersl 在问题下方的评论中所建议的,我将逐步引入任何安全措施。 没有进行注册。 clientid 在您的应用程序中是硬编码的。如果其他开发人员想要访问您的网络服务,只需让他们向您发送电子邮件,然后您向他们发送 clientid。我在“狂野”中有一个 Web 服务,只有这个 clientid。但相当足够的 android 应用程序并不像流行的东西。我在他的博客中使用了与 jeff atwood 相同的技术,只是使用了静态验证码。之后没有看到垃圾邮件。 啊,对不起,我误会了。没有计划向其他开发人员提供 API 访问权限 - 所以这可能只是未来版本的考虑因素。并且任何客户端硬编码密钥都必须被假定为已泄露 - 可能只是在广泛采用时,但计划很少采用根本没有计划。【参考方案7】:这很棘手,您不希望任何人篡改数据......所以您关心的不是完整性。 而且由于您不维护任何客户列表……就不能担心真实性吗?
对于所有众所周知的 Web 服务攻击(如 DoS 或重放攻击),您可以获得可以阻止它们的防火墙。所以我认为你不需要太在意它们。
但是您不想发送纯文本数据,并希望确保您的下载应用程序是推送数据的对象。
如果您查看您正在评估的方法:
安全密钥:据我了解,服务器和应用程序将共享相同的密钥,如果我正确,所有设备上的所有应用程序都将共享相同的密钥。当应用程序推送数据时,它会对实际提要进行散列处理,并通过实际提要 + 散列提要进行发送。在服务器端,您将使用密钥并对实际提要进行散列,并验证它是否与散列提要匹配。在我看来,这个解决方案主要是解决数据完整性方面的问题,这对你来说不是一个主要问题。 rgt! (是的,逆向工程可能很容易。)
在上述方法中,服务器需要存储密钥因此,如果您的密钥被泄露,您的整个服务将被破坏,并且很难使用新密钥更新所有应用程序。 或者,如果应用程序生成一个密钥,它必须将密钥与消息一起作为摘要或其他东西(如时间戳+一些随机数)在线发送。没那么难破解。
证书:即使有证书,您也可以获得相同的安全性.. 但它很难破解但很容易钢化 :)。如果您在设备上保留一个私钥(当然,您必须在服务器上维护一个公钥)。您必须为每个客户端分配一个私钥,然后服务器需要为所有分配的私钥维护公钥。如果私钥被泄露,则只能对单个应用进行红色标记并请求更新。
所以剩下的就是从应用程序开发的角度来看,您要避免捏造数据。为了防止恶作剧 检查这些事情的唯一一点是在应用程序逻辑中。您将需要缓存最后十个(或者甚至是最佳数量)提要(来自同一 IP),并通过某种逻辑验证是否存在缺陷。
【讨论】:
RE:Certificate - 为每个设备颁发证书不需要 CA 吗?我认为对于这个项目来说,使用 CA 的成本太高了。假设是这种情况,那么您是说我提出的(客户端上的密钥和服务器上的 IP 监视)已经尽我所能了吗? 是的,如果您选择 CA,则需要付费。但我认为您可以使用自签名证书。创建您自己的 CA 并使用 openssl 生成证书。而且由于您的应用程序是消费者(使用服务)并且您是服务提供者,我认为您应该对自签名证书有任何问题,您始终可以覆盖“不是受信任的 CA 问题”。如果您计划使用密钥..您是要为每个 IP 维护一个密钥还是只维护一个密钥? RE:安全密钥 - 您是否建议为每个设备使用唯一的盐(这样我就可以警告任何滥用者)?因此,在第一次使用时,服务器会发送一个与 IP 相关联的唯一盐。在我看来,这很容易被逆向工程所破坏——攻击者只会反复重新注册。还是我误解了你的建议? RE:Certs - 好的,我不知道我可以成为拥有自签名证书的 CA。这似乎是一个很好的解决方案。我不知道如何实现,需要花时间查看 openssl。但是,现阶段我有一个问题 - 是否需要任何用户交互来创建/下载唯一证书,或者这可以仅由我的应用程序在幕后完成吗?【参考方案8】:您可以使用速率限制 + 客户端“软”注册。
基本上,您会生成一个设备 ID,您可以在第一次请求时将其存储在用户默认值中。 对于每个请求,您都会跟踪有多少请求已发送到服务器并限制它在服务器端。 这可以很快实现。
您还可以使用某种共享密钥来使用生成的设备 ID + 发布/获取参数来签署您的请求
【讨论】:
谢谢,但如果我理解正确的话,这对于对代码进行逆向工程的攻击者没有任何好处(以每个人的额外工作服务器端为代价)。攻击者可以简单地重复重新注册。而且我已经有一个共享的秘密签名。我误解了你的建议吗? 如果在移动应用程序中,它需要一些漏洞来提取您的共享秘密。如果您考虑到这一点,签署您的请求并应用 API 速率限制就足够了。跟踪客户(IP...)也是一个好主意。当您生成设备 ID 时,您可以让它遵循一个只有您自己知道的模式... 是的,如果无法反编译应用程序,我会高枕无忧。但它似乎possible。我不知道这有多容易或有多大可能,但如果完成,我的 ws 是敞开的。可能是我太担心了。 查看所有嵌入 Facebook 和 Twitter API_KEY 和 API_SECRET 的应用程序,通常将它们放在#define API_KEK 中。通过访问这些参数,您将能够模拟开发者的应用程序,看起来他们并不担心! :) 我想这取决于您是否会因使用您的网络服务而产生费用,或者您是否会被禁止,或者您是否使用了从其他人的应用程序中窃取的凭据,这些凭据并没有保护它们;)。 Apple 和 google 确实提供了一种方法来检查应用程序是否已被修改。谷歌为苹果和安卓设备提供了一项名为 firebase appcheck 的服务。不确定 id 是否将其用于敏感数据,但对于简单的 Web 服务可能会有所帮助以上是关于如何在不登录的情况下保护 Web 服务的主要内容,如果未能解决你的问题,请参考以下文章
如何在不维护 jsessionid 的情况下在 Spring Boot 中保护 RESTful API
如何在不登录 jwt django rest-framework 的情况下获取请求数据