3

TCP/IP 是否会阻止同一数据包的多个副本到达目的地?还是由端点在其上方分层幂等性逻辑?

如果可能,请参考 TCP/IP 规范中的特定段落。

4

8 回答 8

8

从重复数据包中恢复是 TCP 堆栈的工作:

TCP 必须从 Internet 通信系统损坏、丢失、复制或乱序传送的数据中恢复。这是通过为每个传输的八位字节分配一个序列号来实现的,并且需要来自接收 TCP 的肯定确认 (ACK)。如果在超时间隔内没有收到 ACK,则重新传输数据。在接收端,序列号用于正确排序可能被乱序接收的段并消除重复。通过向每个传输的段添加校验和,在接收器处对其进行检查并丢弃损坏的段来处理损坏。

-- RFC 793 - 传输控制协议,第 1.5 节

但是,如果它们是具有新序列号的相同数据包,则不会。

于 2008-09-26T18:32:06.843 回答
5

TCP 在重传的情况下使用序列号来检测重复,这也将防止微不足道的重放攻击。

来自 RFC 793,第 3.3 节 - 序列号:

设计中的一个基本概念是通过 TCP 连接发送的每个八位字节数据都有一个序列号。由于每个八位字节都是按顺序排列的,因此它们中的每一个都可以被确认。所采用的确认机制是累积的,因此对序列号 X 的确认表示已接收到直到但不包括 X 的所有八位字节。这种机制允许在存在重传的情况下直接进行重复检测。段内八位字节的编号是紧跟在头之后的第一个数据八位字节编号最低,后面的八位字节连续编号。

重复检测将确保不能轻易重传相同的数据包。序列号还将确保注意到数据流中数据的插入(而不是替换),因为伪造数据包之后的其他合法数据包将具有重复的序列号,这将破坏数据流。这可能会导致这些数据包作为重复数据包被丢弃,这可能会破坏正在使用的协议。

有关原始 (1981) TCP/IP 规范的更多信息,请参见RFC 793以及许多其他涉及 TCP/IP 协议扩展或修改的 RFC。

于 2008-09-26T18:37:59.793 回答
4

是的,TCP 层防止重复数据包。它下面的 IP 层没有。

RFC 1122中的详细信息。

于 2008-09-26T18:38:48.933 回答
3

您似乎担心两件不同的事情:

  1. TCP 可靠传递提供了哪些保证
  2. 攻击者能否通过重放攻击影响我的服务器进程

回答1:

TCP 保证字节序列的可靠、有序传递。在服务器调用期间,客户端应用程序发送到 TCP 的任何数据write()都将完全相同。read()

回答2:

重放攻击不适用于 TCP,因为每个连接都依赖于客户端和服务器分别生成的两个随机 32 位数字。要使重放攻击起作用,攻击者必须猜测服务器为其发起的虚假连接生成的序列号(理论上,攻击者有 1 / 2**32 的机会正确猜测)。如果攻击者猜错了,她在最坏的情况下会在你的操作系统中造成一些数据缓冲。

请注意,仅仅因为重放攻击不起作用,没有什么可以阻止攻击者与您的服务器建立合法连接并将她想要的任何数据流传输到您的应用程序。这就是为什么始终验证输入很重要的原因。

于 2010-02-14T19:58:16.637 回答
2

TCP 之下的层可能会遇到多个数据包或丢弃的数据包。TCP 之上的层不会遇到重复或丢弃的数据包。

于 2008-09-26T18:30:26.537 回答
1

我不知道数据包重复,但我从未使用 TCP/IP 遇到过它,而且我知道它确实保证数据包都以正确的顺序到达,所以我不明白为什么它不会。

于 2008-09-26T18:28:49.970 回答
1

这实际上取决于您接收数据的方式——尽管从技术上讲,协议不应给您重复(即具有相同 tcp 校验和的数据包),但其他因素可能会导致您看到重复数据——例如,您使用的网络硬件;此外,如果您使用嗅探器查看 tcp 流,而不仅仅是在应用程序中读取打开的套接字,即使它们正在监视的实际 tcp 流没有 dup 数据包,也有可能从嗅探器获取 dup 数据包。

举一个真实的例子——目前我正在为一家大型证券交易所对内部网络进行一些 tcp 分析,我正在查看的数据来自多个嗅探器并被拼接在一起。所以在提取数据时,我发现我需要做一些预处理步骤,包括查找和删除重复项。例如,在我刚刚读入的大约 60,000 个数据包的流中,我找到并删除了 95 个重复的数据包。

我在这里采取的策略是保留 10 个最近的 tcp 校验和的滚动窗口,并忽略与这些校验和匹配的数据包。请注意,这适用于 PSH 数据包,但不适用于 ACK 数据包 - 但无论如何我不太关心这些。

为了跟踪这个 tcp 校验和的滚动窗口,我编写了一个特殊的集合,这可能对其他人有帮助:

/// <summary>
/// Combination of a double-linked-list and a hashset with a max bound; 
/// Works like a bounded queue where new incoming items force old items to be dequeued; 
/// Re-uses item containers to avoid GC'ing;
/// Public Add() and Contains() methods are fully thread safe through a ReaderWriterLockSlim;
/// </summary>
public class BoundedHashQueue<T>
{
    private readonly int _maxSize = 100;
    private readonly HashSet<T> _hashSet = new HashSet<T>();
    private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
    private readonly Item _head;
    private readonly Item _tail;
    private int _currentCount = 0;

    public BoundedHashQueue(int maxSize)
    {
        _maxSize = maxSize;
        _head = _tail = new Item();
    }

    private class Item
    {
        internal T Value;
        internal Item Next;
        internal Item Previous;
    }

    public void Add(T value)
    {
        _lock.Write(() =>
            {
                if (_currentCount == 0)
                {
                    Item item = new Item();
                    item.Value = value;
                    _head.Next = item;
                    item.Previous = _head;
                    item.Next = _tail;
                    _tail.Previous = item;
                    _currentCount++;
                }
                else
                {
                    Item item;
                    if (_currentCount >= _maxSize)
                    {
                        item = _tail.Previous;
                        _tail.Previous = item.Previous;
                        _tail.Previous.Next = _tail;
                        _hashSet.Remove(item.Value);
                    }
                    else
                    {
                        item = new Item();
                        _currentCount++;
                    }
                    item.Value = value;
                    item.Next = _head.Next;
                    item.Next.Previous = item;
                    item.Previous = _head;
                    _head.Next = item;
                    _hashSet.Add(value);
                }
            });
    }

    public bool Contains(T value)
    {
        return _lock.Read(() => _hashSet.Contains(value));
    }
}}
于 2009-05-14T13:06:09.540 回答
0

你没有完全理解问题。请参阅此链接: http ://en.wikipedia.org/wiki/Transmission_Control_Protocol

在这个页面上写着:

“在 RFC 1323 中定义的 TCP 时间戳帮助 TCP 计算发送方和接收方之间的往返时间。时间戳选项包括一个 4 字节时间戳值,发送方在其中插入其时间戳时钟的当前值,以及一个 4 字节回显回复时间戳值,接收方通常插入它收到的最新时间戳值。发送方在确认中使用回显回复时间戳来计算自确认段发送以来经过的总时间。[2]

TCP 时间戳也用于帮助 TCP 序列号遇到其 2^32 边界并“环绕”序列号空间的情况。该方案被称为防止包装序列号或 PAWS(有关详细信息,请参阅 RFC 1323)。”

问候,联合(波兰)

于 2009-07-25T23:05:07.343 回答