对HTTP参数进行编码/混淆

18

我们目前正在开发一个非常简单的Web应用程序,并且我们希望以某种方式“混淆”(什么是正确的术语?)或编码请求参数,以便我们可以降低空闲用户发送任意数据的机会。

例如,url看起来像这样:/webapp?user=Oscar&count=3

我们想要类似于:/webapp?data=EDZhjgzzkjhGZKJHGZIUYZT 并在服务器端解码该值以获取真正的请求信息。

在开始实现自己的代码之前(很可能做错了),我想知道是否有现成的解决方案?

我们的服务器使用Java,客户端使用JavaScript。


如果您从表单中提交内容,参数将不会成为URL的一部分。 - DwB
据我理解,混淆是指由于复杂性(例如ROT13或XOR操作)而隐藏数据,加密是指必须知道一个秘密才能访问数据。 - Gary
为了后人记录,我不记得这个项目,但我肯定记得Mike Atlas的被接受的答案是正确的方法:在应用程序上工作进行身份验证/授权。 - OscarRyz
6个回答

34
不要这样做。如果您可以在客户端代码中构建某些东西来混淆传输回服务器的数据,那么恶意黑客也可以这样做。无论您的官方客户端做什么,都无法信任发送到服务器的数据。坚持对客户端数据进行转义并在服务器端使用白名单验证。使用SSL,如果可以,请将请求参数放在POST而不是GET中。 扩展编辑 您的困惑源于阻止用户篡改请求数据的目标,以及实施标准安全措施的需要。Web应用程序的标准安全措施包括使用身份验证、权限和会话管理、审计跟踪、数据验证和安全通信渠道的组合。
使用SSL无法防止客户端篡改数据,但它可以防止中间人查看或篡改数据,并指导行为良好的浏览器不将敏感数据缓存到URL历史记录中。
看起来您有一个简单的Web应用程序,没有身份验证,并且在GET中传递控制它的请求参数,因此一些非技术类人员可能会发现user=WorkerBee可以在其浏览器栏中简单更改为user=Boss,从而访问他们不应该看到的数据或做他们不应该做的事情。 您(或客户)希望混淆这些参数是天真的,因为这只会挫败最不敏感技术人员。这是一个不成熟的措施,而且您还没有找到现有解决方案的原因是这不是一个好方法。最好花时间实现一个体面的身份验证系统,并加入审计跟踪以确保安全(如果这确实是您所做的,请将Gary的答案标记为正确)。
因此,总结一下:
  1. 混淆并不是真正的安全。
  2. 即使数据经过了混淆,也不能信任用户数据。
  3. 验证数据
  4. 使用安全通信渠道(SSL)有助于阻止其他相关威胁。
  5. 你应该放弃你的方法并采取正确的方式。在您的情况下,正确的方法可能意味着添加一个带有特权系统的身份验证机制,以防止用户访问他们没有足够权限查看的内容-包括他们可能试图通过篡改GET参数来访问的内容。Gary R的回答,以及Dave和Will的评论都指出了这一点。

2
所以,您的建议是“不要这样做,因为它不完美”吗?我真的不明白如何验证客户端数据。例如,如果URL是/getinfo?user=oscar,我应该如何对值“Oscar”进行白名单验证?使用SSL + POST难道不会像不使用它们一样容易被篡改吗? - OscarRyz
1
@OcarRyz,针对你在这里的示例,你可能想要将用户登录到你的应用程序中,并使用存储在 cookie 中的会话 ID 来验证他们的身份以进行未来的请求。然后,当他们尝试查看 Oscar 的信息时,您可以验证 Oscar 是否存在,并且该用户既可以是 Oscar 本人,也可以是有权查看 Oscar 信息的其他人。 - Dave Aaron Smith
1
@Oscar -- 一眼看去,你在这里处理的问题不是数据篡改问题,而是授权问题。如果任何人请求 Oscar 的信息,但他们没有被授权查看 Oscar 的权限,那么他们将得到空白。如果这是你的关注点,那么需要实现一个完整的“角色和特权”授权方案,而不是试图通过防止用户通过黑客请求提出“坏问题”的方式来解决问题。 - Will Hartung
1
@Mike 感谢推荐 @Oscar 你可能想查看这个链接 http://static.springsource.org/spring-security/site/faq/faq.html,其中详细介绍了Spring Security解决此问题的方法。 - Gary
1
答案是在服务器上进行验证。例如,如果用户试图访问奥斯卡的数据,请在服务器上执行检查以确保他们应该看到奥斯卡的数据,然后再提供给他们。如果他们试图执行仅限管理员的选项,请不要仅仅隐藏非管理员的按钮,而是在服务器上检查他们是否是管理员,然后再执行该操作。 - Yuliy
显示剩余4条评论

13
如果您的目标是“减少空闲用户发送任意数据的机会”,那么我建议尝试另一种更简单的方法。创建一个加密私钥并将其存储在应用程序服务器端。每当您的应用程序生成一个URL时,使用您的私有加密密钥创建URL的哈希值,并将该哈希值放入查询字符串中。每当用户请求带有URL参数的页面时,重新计算哈希并查看是否匹配。这将让您相信是您的应用程序计算了该URL。但它也将使查询字符串参数可读。伪代码如下:
SALT = "so9dnfi3i21nwpsodjf";

function computeUrl(url) {
  return url + "&hash=" + md5(url + SALT );
}

function checkUrl(url) {
  hash = /&hash=(.+)/.match(url);
  oldUrl = url.strip(/&hash=.+/);
  return md5(oldUrl + SALT ) == hash;
}

你说的“每当你的应用程序生成一个URL”是什么意思?你是指“当客户端生成请求时”吗? - Mike Atlas
@Mike,我假设url字符串将起源于应用程序的服务器端。例如,也许应用程序会执行类似于<a href="<?PHP echo(computeUrl("/webapp?user=Oscar&count=3"))?>">Get 3 of these.</a>的操作。 - Dave Aaron Smith
如果任何参数来自表单输入控件,那么它很快就会出现错误。 - Mike Atlas
@Mike,非常好的观点。我认为除了最简单的形式之外,任何其他方法都会破坏你在答案中讨论的更细致的服务器端验证方法。客户端方法可以通过Firebug进行黑客攻击,而除了你所描述的方法之外的服务器端方法将是无用的。 - Dave Aaron Smith
@jbinto,我有点不理解。这里没有进行解密。我正在生成和比较哈希值以确保数据完整性。也许混淆的一部分是我错误地标记了PRIVATE_KEY。应该像@Will的解决方案中一样将其称为SALT。 - Dave Aaron Smith

7
如果你想限制对数据的访问,那么请使用某种带有cookie提供单点登录认证密钥的登录机制。如果客户端发送带有密钥的cookie,则他们可以根据与其账户相关联的权限(管理员、公共用户等)来操作数据。在Java中,只需查看Spring Security、CAS等即可轻松使用此实现。cookie中提供的令牌通常使用发行服务器的私钥进行加密,并且通常是防篡改的。
或者,如果您希望未经身份验证的公共用户能够向您的站点发布一些数据,则所有投注都将关闭。您必须在服务器端进行验证。这意味着限制对某些URI的访问,并确保所有输入都已清除。
这里的黄金法则是禁止一切,除了您知道是安全的东西。

3
如果目标是防止“静态”URL被篡改,那么可以简单地加密参数或对其进行签名。在URL参数后面添加MD5哈希值以及一些盐(salt)可能已经足够安全了。盐可以是会话中存储的随机字符串。
然后可以这样做:
http://example.com/service?x=123&y=Bob&sig=ABCD1324

这种技术暴露了数据(即他们可以“看到”xyz=123),但他们无法更改数据。
“加密”有一个优点(我使用这个术语并不严格)。这是指加密URL的整个参数部分。
在这里,你可以做一些像这样的事情:
http://example.com/service?data=ABC1235ABC

使用加密的好处有两个。

首先,它可以保护数据(例如,用户永远看不到xyz=123)。

另一个特点是它是可扩展的:

http://example.com/service?data=ABC1235ABC&newparm=123&otherparm=abc

在这里,您可以解码原始负载,并与新数据进行(安全)合并。
因此,请求可以向请求添加数据,但不能更改现有数据。
您可以通过签名技术执行相同的操作,只需将整个请求合并为一个“blob”,该“blob”会被隐式签名。那是“有效”的加密,只是一种弱加密。
显然,您不希望在客户端上执行任何此操作。毫无意义。如果您可以做到这一点,“他们”也可以做到这一点,您无法区分它们,因此最好根本不要这样做 - 除非您想在普通HTTP端口(而不是TLS)上“加密”数据,但是然后人们会明智地想知道“为什么要麻烦”。
对于Java,所有这些工作都在过滤器中完成,这是我做的方式。后端与此隔离开来。
如果您愿意,可以使用出站过滤器使后端完全与此隔离,以处理URL加密/签名。
这也是我所做的。
缺点是要正确和高效地完成这项工作非常复杂。您需要一个轻量级的HTML解析器来提取URL(我编写了一个流解析器来即时完成它,因此它没有将整个页面复制到RAM中)。
好处是所有内容侧“只需工作”,因为它们对此一无所知。
在处理Javascript时还有一些特殊处理(因为您的过滤器不容易“知道”何时存在要加密的URL)。我通过要求签名的URL是具体的“var signedURL ='....'”来解决这个问题,因此我可以轻松找到输出中的那些。对于设计师来说,并不像您想象的那样具有压倒性的负担。
过滤器的另一个好处是您可以将其禁用。如果出现一些“奇怪的行为”,只需关闭它即可。如果行为继续,您已经发现了与加密相关的错误。它还使开发人员可以使用纯文本进行工作,并将加密留给集成测试。
虽然需要付出努力,但最终效果很好。

大多数人不是通过将数据存储在服务器端会话中来实现这一点吗?这对我来说就像是MS视图状态方法。 - UpTheCreek

1

您可以使用base64或类似的编码方式对数据进行编码。我会将参数本身编码为JSON以进行序列化。


1

我需要在服务器端使用JavaScript解释器吗? - OscarRyz
不,只需查看包含的php文件(encrypt.php),然后用Java重写即可 :) - oimoim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接