如何防止重播攻击在您的网站

  • A+
所属分类:服务器

%title插图%num

这篇文章最初发表在本的技术会谈现场。

重放攻击在这种情况下,攻击者拦截和重新发送不属于它们的网络数据包是极其危险的,在某些情况下会造成严重的破坏。使这类攻击更加麻烦的是,它们甚至可以在加密的通信信道上进行,而不需要访问解密密钥。攻击者只需窃听您的线路,并且对一组特定数据包正在执行的任务有大致的了解,并且通过重新发送这些数据包或请求,他们将能够中断您的通信或造成更大的破坏性影响。

在本文中,我将向您展示一种基本的、易于实现的方法,它将防止在您的网站上进行重放攻击。它还将有一个副作用,防止混乱的用户重复他们的上一个帖子请求的恼人影响,因为他们不断刷新他们的浏览器在错误的时间。

这远远不是一个完整的解决办法。它有缺陷和悬而未决的问题,但它提供了一个关于令牌和简单协议如何加强您的网站安全的一般看法。示例代码和实现是在ASP.NET和C#中完成的,但是这个概念可以部署在任何其他平台或编程语言上。

一次性令牌概念

本文将提供的解决方案背后的想法是将每个HTTP响应绑定到一个令牌字符串,该字符串只对下一个POST请求有效。以下是所涉及的步骤的简单分类:

  1. 客户端通过键入URL或页面或单击链接来发出GET请求。
  2. 服务器生成随机令牌。随后,它将令牌的副本存储在会话中,并将令牌的副本嵌入到它发送给客户端的响应的<form>标记中。
  3. 客户端处理内容,并向服务器发送POST请求,例如当用户单击包含随机生成的令牌的按钮时。
  4. 服务器接收请求,只有当附加的令牌等于存储在用户会话中的令牌时,才继续处理它。
  5. 服务器使令牌无效并返回到步骤2,在该步骤中,它使用一个新的随机令牌来制定响应。

这样,即使发送给服务器的关键请求被恶意用户截获,也不能重复,因为它包含的令牌在请求发送到服务器后不再有效。同样的情况也适用于这样的情况:一个粗心大意的用户错误地按下键盘上的F5,并在向服务器发送信息后重新发送请求。

试验台

为了实现一次性令牌的概念,我们将创建一个示例页面,其中包含一个简单的文本框和一个提交按钮。我们还将添加一个Label控件来显示测试输出。

%title插图%num

后面的代码将是一个简单的片段,它显示提交的时间以及文本框中包含的数据。

%title插图%num

这是初始GET请求之后页面的输出。

%title插图%num

提交页面后,输出将如下所示:

%title插图%num

问题是,如果您刷新页面,它将重新发布您的数据并重复上次请求,服务器将毫不费力地处理它。现在想象一下,如果您刚刚完成了一个非常重要的100万美元事务,并且无意中按下了键盘上的F5。或者更糟糕的是,一些恶意用户拦截您的请求,发现这是一个支付交易,并重复它,以虹吸您的资金和恶意。

%title插图%num

为了防止POST请求被重复,我们更新标记以添加一个隐藏字段,该字段将存储令牌。

%title插图%num

接下来,我们将创建一个函数,该函数生成一个随机令牌,并将其嵌入到隐藏字段和会话集合中。

%title插图%num

之后,我们将Page_Load()函数更改为只有当POST令牌与存储在会话中的令牌相等时才显示已发布的数据。

%title插图%num

最后,我们重写OnPreRender()函数,以便在最终输出发送到客户端之前生成一个新令牌。这使得它成为一个一次性令牌,因为每次发送新请求时都会更新它。

%title插图%num

现在,当您通过单击按钮提交表单时,它会像以前一样工作。但是,如果您试图通过刷新页面来模拟重放攻击,您将得到以下错误,因为与表单一起发送的令牌不再等于存储在服务器上的令牌:

%title插图%num

这样,我们就可以区分有效的按钮点击提交和错误重复的请求。

完善守则

虽然这段代码修复了页面的重放攻击问题,但它有几个需要解决的问题:

  • 它必须在所有页面上重复。
  • 如果您在同一个网站上打开了几个选项卡,它将无法工作,因为令牌是在请求之间共享的。
  • 太丑了

作为一个狂热的面向对象编程(OOP)爱好者,我总是在寻找机会,通过利用这种最棒的编程范式来重构和完善代码。

为了解决上述问题,我们首先要做的是定义一个封装令牌生成功能的类。我们将调用TokenizedPage类,并将其从System.Web.UI.Page派生出来,以便以后能够将其用于页面。

%title插图%num

接下来,为了提高代码的可读性和可管理性,我们将页面令牌和会话令牌封装到两个不同的属性中,添加到TokenizedPage类中。为了使代码在网页中易于移植,我们将使用ViewState集合而不是隐藏的输入字段来存储页面令牌。我们还使用Page.Title属性作为在会话中存储令牌的键。这将改善我们的代码,并将部分解决第二个问题,这将限制我们的网站使用在浏览器中的单一标签。通过应用此更改,我们将能够在不同的选项卡中打开站点的不同页面,但无法在单独的选项卡中打开同一页的多个实例,因为它们仍将共享令牌。这一问题将在稍后讨论。

%title插图%num

接下来,我们添加一个名为IsTokenValid的只读布尔属性,它遵循其他Page属性的示例,例如IsPostBack和IsValid。此属性的目的是确保页令牌与会话令牌相等。

%title插图%num

最后,我们添加GenerateRandomToken()函数和OnPreRender()事件的覆盖,就像在测试床中所做的那样。

%title插图%num

现在,为了使用一个令牌模式,我们所需要做的就是创建一个新页面,从TokenizedPage派生它,并在需要一次性令牌时使用IsTokenValid。

%title插图%num

好多了。

使它变得更好

此代码的一个问题是,如果浏览器中有两个选项卡指向同一个页面,那么发布一个选项卡将使另一个选项卡失效,因为它们使用的是相同的会话令牌键。这可以通过添加一个令牌ID来解决,这将确保发生在一个选项卡中的每个请求响应序列都使用它自己的一组唯一令牌,并且不会干扰同一页上的其他请求。第一个任务是返回TokenizedPage类并添加TokenID属性。该属性在初始GET请求中第一次调用它时生成一个随机ID,并将其存储在ViewState集合中以供将来重用。

%title插图%num

接下来,我们将修改SessionHiddenToken属性以使用TokenId属性,而不是使用Page.Title属性。

%title插图%num

最酷的是,由于我们使用了抽象和封装原则(这是对OOP好处的又一次大喊),我们不需要做任何其他更改,新机制将适用于我们从TokenizedPage派生的所有页面。

avatar

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: