防止重放攻击_如何防止您的网站上的重放攻击

防止重放攻击

Preventing replay attacks

This article was originally published at Ben’s Tech Talks site.

本文最初在Ben的Tech Talks网站上发布。

Replay attacks, in which attackers intercept and resend network packets that do not belong to them, are extremely dangerous and can in some cases cause serious damage. What makes these kinds of attacks even more noisome is that they can even be staged on encrypted communication channels without gaining access to the decryption keys. Attackers only have to eavesdrop on your line and have a general knowledge of what task a specific set of packets are performing, and by resending those packets or requests, they will be able to disrupt your communications or cause more damaging effects.

重播攻击 (攻击者拦截并重新发送不属于它们的网络数据包)极为危险,在某些情况下可能会造成严重破坏。 使这类攻击更加令人讨厌的是,它们甚至可以在加密的通信通道上进行,而无需访问解密密钥。 攻击者只需窃听您的电话,并且对特定的一组数据包正在执行的任务有一个一般的了解,并且通过重新发送这些数据包或请求,他们将能够破坏您的通信或造成更大的破坏性影响。

In this article, I’ll show you a basic, easy-to-implement method that will prevent replay attacks on your website. It will also have the side benefit of preventing the annoying effects of confused users repeating their last POST request by constantly refreshing their browser at the wrong time.

在本文中,我将向您展示一种基本的,易于实现的方法,该方法可以防止对您的网站进行重放攻击。 通过在错误的时间不断刷新浏览器,还可以防止混乱的用户重复他们的上一个POST请求的烦人的副作用。

This is far from a complete solution. It has flaws and pending issues, but it gives you a general view of how tokens and simple protocols can enhance security in your websites. Sample codes and implementation are done in ASP.NET and C#, but the concept can be deployed on any other platform or programming language.

这远非一个完整的解决方案。 它具有缺陷和悬而未决的问题,但是它使您可以大致了解令牌和简单协议如何增强网站的安全性。 示例代码和实现在ASP.NET和C#中完成,但是该概念可以部署在任何其他平台或编程语言上。

一次性代币概念 (The One-time Token Concept)

The idea behind the solution that will be offered in this post is to tie every HTTP response to a token string which will be valid only for the next post request. Here’s a simple breakdown of the steps involved:

本文将提供的解决方案背后的想法是将每个HTTP响应与一个令牌字符串相关联,该令牌字符串仅对下一个发布请求有效。 这是所涉及步骤的简单分解:

  1. The client makes a GET request by typing the URL or a page or by clicking on a link.

    客户端通过键入URL或页面或单击链接来发出GET请求。
  2. The server generates a random token. Subsequently, it stores a copy of the token in the session and embeds a copy of the token in the <form> tag of the response it sends to the client.

    服务器生成一个随机令牌。 随后,它将令牌的副本存储在会话中,并将令牌的副本嵌入到发送给客户端的响应的<form>标记中。
  3. The client processes the content, and sends a POST request to the server, say when the user clicks on a button, which contains the randomly-generated token.

    客户端处理内容,然后向服务器发送POST请求,例如,当用户单击包含随机生成的令牌的按钮时。
  4. The server receives the request and proceeds with processing it only if the attached token is equal to the one stored in the user’s session.

    服务器仅在附加令牌等于用户会话中存储的令牌时,才接收请求并继续进行处理。
  5. The server invalidates the token and returns to step 2, where it formulates the response with a new random token.

    服务器使令牌无效,并返回到步骤2,在此它用新的随机令牌来表示响应。

In this manner, even if a critical request sent to the server is intercepted by a malicious user, it cannot be repeated because the token it contains is no longer valid after the request is sent to the server. The same goes for the scenario where a careless user mistakenly presses F5 on the keyboard and resends the request after posting information to the server.

这样,即使发送给服务器的关键请求被恶意用户拦截,也无法重复执行,因为它所包含的令牌在请求发送到服务器后不再有效。 对于粗心的用户错误地按下键盘上的F5并在将信息发布到服务器后重新发送请求的情况也是如此。

试验台 (The Test-bed)

In order to implement the one-time token concept, we’re going to create a sample page that contains a simple textbox and a submit button. We’ll also throw in a label control to display the test output.

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

simple textbox and button code

The code behind will be a simple snippet that displays the time of the submission plus the data contained in textbox.

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

time of the submission plus the data contained in textbox

This is the output of the page after the initial GET request

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

output after the initial GET request

After submitting the page, the output will look like this:

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

output after submission

The problem is, if you refresh your page it will re-POST your data and repeat the last request, and the server will process it without a hitch. Now imagine if you had just made a critical $1,000,000 transaction and inadvertently pressed F5 on your keyboard. Or worse, some malicious user intercepts your request, figures out it’s a payment transaction, and repeats it in order to siphon your funds and spite you.

问题是,如果刷新页面,它将重新发布您的数据并重复上一个请求,服务器将顺利处理它。 现在想象一下,如果您刚刚进行了一次重要的$ 1,000,000交易并且无意中按下了键盘上的F5键。 或更糟糕的是,某些恶意用户拦截了您的请求,发现这是一笔付款交易,然后重复执行以窃取您的资金并吞噬您。

Preventing replay attacks

解决方案 (The Solution)

In order to prevent a POST request from being repeated, we update the markup to add a hidden field, which will store the token.

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

adding a hidden field

Next, we will create a function that generates a random token and embeds it both in the hidden field and the session collection.

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

random token

Afterwards, we change the Page_Load() function to only display the posted data if the posted token is equal to the one stored in the session.

然后,我们将Page_Load()函数更改为仅在发布的令牌等于会话中存储的令牌时显示发布的数据。

change the Page_Load() function

Finally, we override the OnPreRender() function to generate a new token before the final output is sent to the client. This is what makes it a one-time token, because it’s renewed every time a new request is sent.

最后,在将最终输出发送到客户端之前,我们重写OnPreRender()函数以生成新令牌。 这就是使其成为一次性令牌的原因,因为每次发送新请求时都会对其进行更新。

override the OnPreRender() function

Now when you submit the form by clicking on the button, it works just as it did before. But if you try to simulate the replay attack by refreshing the page, you’ll get the following error because the token that is sent with the form is no longer equal to the one stored on the server:

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

error due to incorrect token

This way, we can distinguish valid button-click submissions from falsely-repeated requests.

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

完善代码 (Refining the Code)

Although this code fixes the replay attack problem for your page, it has several issues that need to be addressed:

尽管此代码解决了页面的重播攻击问题,但仍有一些问题需要解决:

  • It has to be repeated across all pages

    必须在所有页面上重复
  • It will not work if you have several tabs open on the same website, because the token is being shared across requests

    如果您在同一网站上打开了多个标签,则该标签将无法正常工作,因为令牌是在请求之间共享的
  • It’s downright ugly

    真丑

As a fanatic Object Oriented Programming (OOP) enthusiast, I’m always looking for opportunities to refactor and refine code by leveraging the power of this most awesome programming paradigm.

作为狂热的面向对象编程(OOP)爱好者,我一直在寻找利用这种最出色的编程范例的力量来重构和完善代码的机会。

In order to address the above-mentioned issues, the first thing we do is to define a class that will encapsulate the token generation functionality. We’ll call the class TokenizedPage and will derive it from System.Web.UI.Page in order to be able to use it for pages in the future.

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

calling the class TokenizedPage

Next, in order to make the code more readable and manageable, we encapsulate the page token and the session token into two different properties that we add to the TokenizedPage class. In order to make the code easily portable in web pages, we will use the ViewState collection instead of the hidden input field to store the page token. We also use the Page.Title property as the key for storing the token in the session. This will improve our code and will partially address the second issue, which would limit the use of our site to a single tab in the browser. By applying this change, we’ll be able to have separate pages of the site open in different tabs, but we won’t be able to have several instances of the same page open in separate tabs, because they’ll still be sharing tokens. This issue will be addressed later.

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

encapsulating  page and session tokens

Next, we add a read-only Boolean property named IsTokenValid, which follows the example of other Page properties such as IsPostBack and IsValid. The purpose of this property is to make sure the page token is equal to the session token.

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

adding IsTokenValid

Finally, we add the GenerateRandomToken() function and the override of the OnPreRender() event as was done in the test-bed.

最后,我们添加了GenerateRandomToken()函数和OnPreRender()事件的覆盖,如在测试台上所做的那样。

GenerateRandomToken and OnPreRender

Now, in order to use the one-token pattern, all we need to do is to create a new page, derive it from TokenizedPage and use the IsTokenValid whenever the one-time token is needed.

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

IsTokenValid

Much better.

好多了。

使它变得更好 (Making it Even Better)

One of the problems with this code is that if you have two tabs in your browser pointing to the same page, posting one will invalidate the token of the other, since they’re using the same session token key. This can be addressed by adding a token ID which will make sure each request-response sequence happening in one tab will use its own set of unique tokens and will not interfere with other requests on the same page. The first order of business is to go back to the TokenizedPage class and add a TokenID property. This property generates a random ID the first time it is called in the initial GET request and stores it in the ViewState collection for future reuse.

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

adding a TokenID property

Next, we will alter the SessionHiddenToken property to use the TokenId property instead of using the Page.Title property.

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

using the TokenId property

The cool thing is that since we had used abstraction and encapsulation principles (another big shout out to the benefits of OOP), we don’t need to make any other change and the new mechanism will work with all pages that we’ve derived from TokenizedPage.

很棒的事情是,由于我们使用了抽象和封装原理(另一种大呼OOP的好处),因此我们无需进行任何其他更改,并且新机制将适用于我们从中衍生的所有页面TokenizedPage。

剩余的问题 (Remaining Issues)

This is about it for the one-time token pattern. There are two issues that remain:

这与一次性令牌模式有关。 仍然存在两个问题:

  • An unlimited number of token IDs will be generated for each session (the number of GET requests that is sent to each session) to be more precise. This can be addressed by implementing a stack or cache mechanism that pops older IDs when a number limit is exceeded or when they become unused for a specific duration. I’ll leave the implementation to you.

    更精确地说,将为每个会话生成无限数量的令牌ID(发送到每个会话的GET请求数量)。 可以通过实现堆栈或缓存机制来解决此问题,该机制将在超出数量限制或在特定持续时间内不使用它们时弹出较旧的ID。 我将把实施留给您。
  • The default random number generator is not what you would call the most secure and reliable source of randomness and a savvy hacker might be able to predict the sequence of tokens. However, if you’re using SSL encryption, they won’t be able to get a hold of the token anyway.

    默认的随机数生成器不是您所说的最安全,最可靠的随机性源,精明的黑客可能能够预测令牌的序列。 但是,如果您使用的是SSL加密,则无论如何他们将无法获得令牌。

Do you have any enhancements to add or would like to share its implementation in another platform and programming language? Please leave a note in the comments section below.

您是否要添加任何增强功能或希望以其他平台和编程语言共享其实现? 请在下面的评论部分中留下注释。

翻译自: https://www.sitepoint.com/how-to-prevent-replay-attacks-on-your-website/

防止重放攻击

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值