Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >我们说 TCP 是流式协议究竟意味着什么?

我们说 TCP 是流式协议究竟意味着什么?

作者头像
范蠡
发布于 2021-07-16 02:31:12
发布于 2021-07-16 02:31:12
3K00
代码可运行
举报
运行总次数:0
代码可运行

一、TCP 协议是流式协议

很多读者从接触网络知识以来,应该听说过这句话:TCP 协议是流式协议。那么这句话到底是什么意思呢?所谓流式协议,即协议的内容是像流水一样的字节流,内容与内容之间没有明确的分界标志,需要我们人为地去给这些协议划分边界。

举个例子,A 与 B 进行 TCP 通信,A 先后给 B 发送了一个 100 字节和 200 字节的数据包,那么 B 是如何收到呢?B 可能先收到 100 字节,再收到 200 字节;也可能先收到 50 字节,再收到 250 字节;或者先收到 100 字节,再收到 100 字节,再收到 100 字节;或者先收到 20 字节,再收到 20 字节,再收到 60 字节,再收到 100 字节,再收到 50 字节,再收到 50 字节......

不知道读者看出规律没有?规律就是 A 一共给 B 发送了 300 字节,B 可能以一次或者多次任意形式的总数为 300 字节收到。假设 A 给 B 发送两个大小分别是 100 字节和 200 字节的数据包,作为发送方的 A 来说,A 是知道如何划分这两个数据包的界限的,但是对于 B 来说,如果不人为规定多少字节作为一个数据包,B 每次是不知道应该把收到的数据中多少字节作为一个有效的数据包的,而规定每次把多少数据当成一个包就是协议格式定义的内容之一。

经常会有新手写出类似下面这样的代码:

发送端:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//...省略创建socket,建立连接等部分不相关的逻辑...
char buf[] = "the quick brown fox jumps over a lazy dog.";
int n = send(socket, buf, strlen(buf), 0);
//...省略出错处理逻辑...

接收端:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//省略创建socket,建立连接等部分不相关的逻辑...
char recvBuf[50] = { 0 };
int n = recv(socket, recvBuf, 50, 0);
//省略出错处理逻辑...
printf("recvBuf: %s", recvBuf);

为了专注问题本身的讨论,我这里省略掉了建立连接和错误处理的逻辑。上述代码中发送端给接收端发送了一串字符”the quick brown fox jumps over a lazy dog.“,接收端收到后将其打印出来。

类似这样的代码在本机一般会工作的很好,接收端也如期打印出来预料的字符串,但是一放到局域网或者公网环境就出问题了,即接收端可能打印出来字符串并不完整;如果发送端连续多次发送字符串,接收端会打印出来的字符串不完整或出现乱码。不完整的原因很好理解,即对端某次收到的数据小于完整字符串的长度,recvBuf 数组开始被清空成 \0,收到部分字符串后,该字符串的末尾仍然是 \0,printf 函数寻找以 \0 为结束标志的字符结束输出;乱码的原因是如果某次收入的数据不仅包含一个完整的字符串,还包含下一个字符串部分内容,那么 recvBuf 数组将会被填满,printf 函数输出时仍然会寻找以 \0 为结束标志的字符结束输出,这样读取的内存就越界了,一直找到 \0 为止,而越界后的内存可能是一些不可读字符,显示出来后就乱码了。

我举这个例子的目的是希望你能对“ TCP 协议是流式协议”有一个直观的认识。正因为如此,我们需要人为地在发送端和接收端规定每一次的字节流边界,以便接收端知道从什么位置取出多少字节来当成一个数据包去解析,这是我们设计网络通信协议格式要做的工作之一。

二、如何解决粘包问题

网络通信程序实际开发中,或者技术面试时,面试官通常会问的比较多的一个问题是:网络通信时,如何解决粘包?

有的面试官可能会这么问:网络通信时,如何解决粘包、丢包或者包乱序问题?这个问题其实是面试官在考察面试者的网络基础知识,如果是 TCP 协议,在大多数场景下,是不存在丢包和包乱序问题的,TCP 通信是可靠通信方式,TCP 协议栈通过序列号和包重传确认机制保证数据包的有序和一定被正确发到目的地;如果是 UDP 协议,如果不能接受少量丢包,那就要自己在 UDP 的基础上实现类似 TCP 这种有序和可靠传输机制了(例如 RTP 协议、RUDP 协议)。所以,问题拆解后,只剩下如何解决粘包的问题。

先来解释一下什么是粘包,所谓粘包就是连续给对端发送两个或者两个以上的数据包,对端在一次收取中收到的数据包数量可能大于 1 个,当大于 1 个时,可能是几个(包括一个)包加上某个包的部分,或者干脆就是几个完整的包在一起。当然,也可能收到的数据只是一个包的部分,这种情况一般也叫半包

粘包示意图如下:

无论是半包还是粘包问题,其根源是上文介绍中 TCP 协议是流式数据格式。解决问题的思路还是想办法从收到的数据中把包与包的边界给区分出来。那么如何区分呢?一般有三种分包方法:

固定包长的数据包

顾名思义,即每个协议包的长度都是固定的。举个例子,例如我们可以规定每个协议包的大小是 64 个字节,每次收满 64 个字节,就取出来解析(如果不够,就先存起来)。

这种通信协议的格式简单但灵活性差。如果包内容不足指定的字节数,剩余的空间需要填充特殊的信息,如 \0(如果不填充特殊内容,如何区分包里面的正常内容与填充信息呢?);如果包内容超过指定字节数,又得分包分片,需要增加额外处理逻辑——在发送端进行分包分片,在接收端重新组装包片(分包和分片内容在接下来会详细介绍)。

以指定字符(串)为包的结束标志

这种协议包比较常见,即字节流中遇到特殊的符号值时就认为到一个包的末尾了。例如,我们熟悉的 FTP协议,发邮件的 SMTP 协议,一个命令或者一段数据后面加上"\r\n"(即所谓的 CRLF)表示一个包的结束。对端收到后,每遇到一个”\r\n“就把之前的数据当做一个数据包。

这种协议一般用于一些包含各种命令控制的应用中,其不足之处就是如果协议数据包内容部分需要使用包结束标志字符,就需要对这些字符做转码或者转义操作,以免被接收方错误地当成包结束标志而误解析。

包头 + 包体格式

这种格式的包一般分为两部分,即包头和包体,包头是固定大小的,且包头中必须含有一个字段来说明接下来的包体有多大。

例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct msg_header
{
    int32_t bodySize;
    int32_t cmd;
};

这就是一个典型的包头格式,bodySize 指定了这个包的包体是多大。由于包头大小是固定的(这里是 size(int32_t) + sizeof(int32_t) = 8 字节),对端先收取包头大小字节数目(当然,如果不够还是先缓存起来,直到收够为止),然后解析包头,根据包头中指定的包体大小来收取包体,等包体收够了,就组装成一个完整的包来处理。在有些实现中,包头中的 bodySize可能被另外一个叫 packageSize 的字段代替,这个字段的含义是整个包的大小,这个时候,我们只要用 packageSize 减去包头大小(这里是 sizeof(msg_header))就能算出包体的大小,原理同上。

在使用大多数网络库时,通常你需要根据协议格式自己给数据包分界和解析,一般的网络库不提供这种功能是出于需要支持不同的协议,由于协议的不确定性,因此没法预先提供具体解包代码。当然,这不是绝对的,也有一些网络库提供了这种功能。在 Java Netty 网络框架中,提供了FixedLengthFrameDecoder 类去处理长度是定长的协议包,提供了 DelimiterBasedFrameDecoder 类去处理按特殊字符作为结束符的协议包,提供 ByteToMessageDecoder 去处理自定义格式的协议包(可用来处理包头 + 包体 这种格式的数据包),然而在继承 ByteToMessageDecoder 子类中你需要根据你的协议具体格式重写 decode() 方法来对数据包解包。

这三种包格式,希望读者能在理解其原理和优缺点的基础上深入掌握。

三、解包与处理

在理解了前面介绍的数据包的三种格式后,我们来介绍一下针对上述三种格式的数据包技术上应该如何处理。其处理流程都是一样的,这里我们以包头 + 包体 这种格式的数据包来说明。处理流程如下:

假设我们的包头格式如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//强制1字节对齐
#pragma pack(push, 1)
//协议头
struct msg_header
{   
    int32_t  bodysize;         //包体大小  
};
#pragma pack(pop)

那么上面的流程实现代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//包最大字节数限制为10M
#define MAX_PACKAGE_SIZE    10 * 1024 * 1024

void ChatSession::OnRead(const std::shared_ptr<TcpConnection>& conn, Buffer* pBuffer, Timestamp receivTime)
{
    while (true)
    {
        //不够一个包头大小
        if (pBuffer->readableBytes() < (size_t)sizeof(msg_header))
        {
            //LOGI << "buffer is not enough for a package header, pBuffer->readableBytes()=" << pBuffer->readableBytes() << ", sizeof(msg_header)=" << sizeof(msg_header);
            return;
        }

        //取包头信息
        msg_header header;
        memcpy(&header, pBuffer->peek(), sizeof(msg_header));

        //包头有错误,立即关闭连接
        if (header.bodysize <= 0 || header.bodysize > MAX_PACKAGE_SIZE)
        {
            //客户端发非法数据包,服务器主动关闭之
            LOGE("Illegal package, bodysize: %lld, close TcpConnection, client: %s", header.bodysize, conn->peerAddress().toIpPort().c_str());
            conn->forceClose();
            return;
        }

        //收到的数据不够一个完整的包
        if (pBuffer->readableBytes() < (size_t)header.bodysize + sizeof(msg_header))
            return;

        pBuffer->retrieve(sizeof(msg_header));
        //inbuf用来存放当前要处理的包
        std::string inbuf;
        inbuf.append(pBuffer->peek(), header.bodysize);
        pBuffer->retrieve(header.bodysize);
        //解包和业务处理
        if (!Process(conn, inbuf.c_str(), inbuf.length()))
        {
            //客户端发非法数据包,服务器主动关闭之
            LOGE("Process package error, close TcpConnection, client: %s", conn->peerAddress().toIpPort().c_str());
            conn->forceClose();
            return;
        }
    }// end while-loop
}

上述流程代码的处理过程和流程图中是一致的,pBuffer 这里是一个自定义的接收缓冲区,这里的代码,已经将收到的数据放入了这个缓冲区,所以判断当前已收取的字节数目只需要使用这个对象的相应方法即可。上述代码有些细节我需要强调一下:

  • 取包头时,你应该拷贝一份数据包头大小的数据出来,而不是从缓冲区 pBuffer 中直接将数据取出来(即取出来的数据从 pBuffer 中移除),这是因为倘若接下来根据包头中的字段得到包体大小时,如果剩余数据不够一个包体大小,你又得把这个包头数据放回缓冲区。为了避免这种不必要的操作,只有缓冲区数据大小够整个包的大小(代码中:header.bodysize + sizeof(msg))你才需要把整个包大小的数据从缓冲区移除,这也是这里的 pBuffer->peek() 方法 peek 单词的含义(中文可以翻译成”瞟一眼“或者”偷窥“)。
  • 通过包头得到包体大小时,你一定要对 bodysize 的数值进行校验,我这里要求 bodysize 必须大于 0 且不大于 10 * 1024 * 1024(即 10 M)。当然,你可以根据实际情况决定 bodysize 的上下限(包体大小是 0 字节的包在某些业务场景下是允许的)。记住,一定要判断这个上下限,因为假设这是一个非法的客户端发来的数据,其 bodysize 设置了一个比较大的数值,例如 1 * 1024 * 1024 * 1024(即 1 G),你的逻辑会让你一直缓存该客户端发来的数据,那么很快你的服务器内存将会被耗尽,操作系统在检测到你的进程占用内存达到一定阈值时会杀死你的进程,导致服务不能再正常对外服务。如果你判断了 bodysize 字段是否满足你设置的上下限,对于非法的 bodysize,直接关闭这路连接即可。这也是服务的一种自我保护措施,避免因为非法数据包带来的损失。还有另外一种情况下 bodysize 也可能不是预期的合理值,即因为网络环境差或者某次数据解析逻辑错误,导致后续的数据错位,把不该当包头数据的数据当成了包头,这个时候解析出来的 bodysize 也可能不是合理值,同样,这种情形下也会被这段检验逻辑检测到,最终关闭连接。
  • 不知道你有没有注意到整个判断包头、包体以及处理包的逻辑放在一个 while 循环里面,这是必要的。如果没有这个 while 循环,当你一次性收到多个包时,你只会处理一个,下次接着处理就需要等到新一批数据来临时再次触发这个逻辑。这样造成的结果就是,对端给你发送了多个请求,你最多只能应答一个,后面的应答得等到对端再次给你发送数据时。这就是对粘包逻辑的正确处理。

以上逻辑和代码是最基本的粘包和半包处理机制,也就是所谓的技术上的解包处理逻辑(业务上的解包处理逻辑后面章节再介绍)。希望读者能理解它,在理解它的基础之上,我们可以给解包拓展很多功能,例如,我们再给我们的协议包增加一个支持压缩的功能,我们的包头变成下面这个样子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#pragma pack(push, 1)
//协议头
struct msg_header
{
    char     compressflag;     //压缩标志,如果为1,则启用压缩,反之不启用压缩
    int32_t  originsize;       //包体压缩前大小
    int32_t  compresssize;     //包体压缩后大小
    char     reserved[16];    //保留字段,用于将来拓展
};
#pragma pack(pop)

修改后的代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//包最大字节数限制为10M
#define MAX_PACKAGE_SIZE    10 * 1024 * 1024

void ChatSession::OnRead(const std::shared_ptr<TcpConnection>& conn, Buffer* pBuffer, Timestamp receivTime)
{
    while (true)
    {
        //不够一个包头大小
        if (pBuffer->readableBytes() < (size_t)sizeof(msg_header))
        {
            //LOGI << "buffer is not enough for a package header, pBuffer->readableBytes()=" << pBuffer->readableBytes() << ", sizeof(msg_header)=" << sizeof(msg_header);
            return;
        }

        //取包头信息
        msg_header header;
        memcpy(&header, pBuffer->peek(), sizeof(msg_header));

        //数据包压缩过
        if (header.compressflag == PACKAGE_COMPRESSED)
        {
            //包头有错误,立即关闭连接
            if (header.compresssize <= 0 || header.compresssize > MAX_PACKAGE_SIZE ||
                header.originsize <= 0 || header.originsize > MAX_PACKAGE_SIZE)
            {
                //客户端发非法数据包,服务器主动关闭之
                LOGE("Illegal package, compresssize: %lld, originsize: %lld, close TcpConnection, client: %s", header.compresssize, header.originsize, conn->peerAddress().toIpPort().c_str());
                conn->forceClose();
                return;
            }

            //收到的数据不够一个完整的包
            if (pBuffer->readableBytes() < (size_t)header.compresssize + sizeof(msg_header))
                return;

            pBuffer->retrieve(sizeof(msg_header));
            std::string inbuf;
            inbuf.append(pBuffer->peek(), header.compresssize);
            pBuffer->retrieve(header.compresssize);
            std::string destbuf;
            if (!ZlibUtil::UncompressBuf(inbuf, destbuf, header.originsize))
            {
                LOGE("uncompress error, client: %s", conn->peerAddress().toIpPort().c_str());
                conn->forceClose();
                return;
            }

            //业务逻辑处理
            if (!Process(conn, destbuf.c_str(), destbuf.length()))
            {
                //客户端发非法数据包,服务器主动关闭之
                LOGE("Process error, close TcpConnection, client: %s", conn->peerAddress().toIpPort().c_str());
                conn->forceClose();
                return;
            }
        }
        //数据包未压缩
        else
        {
            //包头有错误,立即关闭连接
            if (header.originsize <= 0 || header.originsize > MAX_PACKAGE_SIZE)
            {
                //客户端发非法数据包,服务器主动关闭之
                LOGE("Illegal package, compresssize: %lld, originsize: %lld, close TcpConnection, client: %s", header.compresssize, header.originsize, conn->peerAddress().toIpPort().c_str());
                conn->forceClose();
                return;
            }

            //收到的数据不够一个完整的包
            if (pBuffer->readableBytes() < (size_t)header.originsize + sizeof(msg_header))
                return;

            pBuffer->retrieve(sizeof(msg_header));
            std::string inbuf;
            inbuf.append(pBuffer->peek(), header.originsize);
            pBuffer->retrieve(header.originsize);
            //业务逻辑处理
            if (!Process(conn, inbuf.c_str(), inbuf.length()))
            {
                //客户端发非法数据包,服务器主动关闭之
                LOGE("Process error, close TcpConnection, client: %s", conn->peerAddress().toIpPort().c_str());
                conn->forceClose();
                return;
            }
        }// end else

    }// end while-loop
}

这段代码先根据包头的压缩标志字段判断包体是否有压缩,如果有压缩,则取出包体大小去解压,解压后的数据才是真正的业务数据。整个程序执行流程图如下:

本文节选自《C++服务器开发精髓》一书。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-07-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 高性能服务器开发 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
等保2.0高风险项判定汇总
前不久(7月14日)网上发布了《网络安全等级保护测评高风险判定指引》,并计划于2019年10月1日起施行。看到后,第一感觉,好贴心哦,还告诉你哪些是重点,要怎么改。看完后感觉,这是要我们半条命么?无论怎样,该重点关注的还是要去关注,做好安全工作。
FB客服
2019/08/09
2.4K0
等保2.0高风险项判定汇总
风险防不胜防?看YashanDB如何守护你的数据库安全(上篇)
数据库作为信息系统的核心,不仅承载着海量的关键数据,还负责向各类用户提供高效、可靠的信息服务,数据库的安全性显得尤为关键,已成为信息安全体系的重中之重。
用户11498166
2025/02/14
1250
风险防不胜防?看YashanDB如何守护你的数据库安全(上)
数据库作为信息系统的核心,不仅承载着海量的关键数据,还负责向各类用户提供高效、可靠的信息服务,数据库的安全性显得尤为关键,已成为信息安全体系的重中之重。
qiaoyikefu
2025/01/17
1280
DCMM标准之数据安全域解读(二):数据安全管理
《数据管理能力成熟度评估模型》(以下简称DCMM)是我国在数据管理领域首个正式发布的国家标准,旨在帮助企业利用先进的数据管理理念和方法,建立和评价自身数据管理能力,持续完善数据管理组织、程序和制度,充分发挥数据在促进企业向信息化、数字化、智能化发展方面的价值。
AIGC新知
2024/10/08
4030
DCMM标准之数据安全域解读(二):数据安全管理
数据库审计
中安威士数据库审计系统(简称VS-AD),是由中安威士(北京)科技有限公司开发的具有完全自主知识产权的数据库审计产品。该系统通过监控数据库的多重状态和通信内容,不仅能准确评估数据库所面临的风险,而且可以通过日志记录提供事后追查机制。主要功能包括:敏感数据发现、性能监控、风险扫描、数据活动监控等。支持旁路、直连、软件探针等多种部署方式。
数据库保护
2019/07/17
2.9K0
数据库审计
聊聊“删库”这件事 | FreeBuf甲方群话题讨论
当企业致力于防御来自外部的攻击时,内部的威胁有时也能杀个措手不及,给企业带来重大损失。前不久,链家数据库管理员删库一案最终被判刑7年,当事人因对公司积怨已久,一气之下删除了大量公司财务数据,致使该公司财务系统彻底无法访问,并影响员工的工资发放,公司为恢复数据及重新构建该系统共计花费人民币 18 万元。而两年前的微盟删库事件影响更甚,导致公司服务器故障时间长达8天,超过300家商户受到影响,市值仅一天就蒸发了10个亿。
FB客服
2022/06/08
3910
聊聊“删库”这件事 | FreeBuf甲方群话题讨论
详谈oracle数据库安全维护六大策略
数据库安全一直是我们关注的问题,网站安全稳定运营必须要有一个安全性高的数据库,如今互联网上各种病毒源源不断,数据库入侵、数据丢失等这样的情况很多,作为系统管理员,保证数据的安全是我们工作之必须,那么需要我们掌握更多的安全维护知识,今天就拿oracle数据库的安全为大家讲解一下:
jtti
2023/04/26
4640
数据库安全能力:安全威胁TOP5
在数据库的安全问题已跃至CSO的工作内容象限榜首的今天,对数据库安全的防御是艰苦的旅程,如何让针对业务安全和数据安全的攻击成为一场废鞋底的马拉松,防止恶意行为者利用漏洞威胁这个“线头”并最终扯下数据这条“线裤”的全部,让我们一起来关注在数据库安全能力建设中识别数据库的安全威胁。
FB客服
2019/11/13
1.4K0
特权访问管理(PAM)之零信任特权Zero Trust Privilege
文|centrify,译|秦陇纪,数据简化DataSimp©20190126Sat
秦陇纪
2019/07/15
2.5K0
特权访问管理(PAM)之零信任特权Zero Trust Privilege
9.存储过程安全性博客大纲(9/10)
在数据库系统中,存储过程是一种预先编写好的SQL代码集合,它被保存在数据库服务器上,可以通过指定的名称来调用执行。存储过程可以包含一系列的控制流语句,如IF条件语句、WHILE循环等,使得数据库操作更加灵活和强大。它们是数据库编程的重要组成部分,广泛应用于数据验证、业务逻辑封装和数据操作自动化等领域。
正在走向自律
2024/12/18
1480
9.存储过程安全性博客大纲(9/10)
数据库学习笔记(二)
CONNECT 权限:拥有 CONNECT 权限的用户不能创建新用户、模式、基本表,只能登录数据库
赤蓝紫
2023/01/02
5590
数据库学习笔记(二)
数据库安全之金融
近年来,随着Internet的快速发展,各个行业均已进入信息化时代,金融业在中国20世纪70年代就已起步;20世纪80年代已经进入推广应用阶段,陆续引进了美国、日本等先进的信息设备;90年代各大专业银行等信息系统纷纷升级,紧跟国际信息化脚步,引进国外先进技术,不断提高自身信息化水平;到90年代末,整个世界进入一个信息技术高速发展的互联网时代,而金融行业作为中国信息化先进水平的代表则面临着前所未有的机遇和挑战。
数据库保护
2020/01/06
1.9K0
数据库安全之金融
风险防不胜防?看YashanDB如何守护你的数据库安全(下)
上一篇文章,咱们主要讲了数据库安全机制中的 访问管理~今天继续深入聊聊 威胁监测以及 数据保护。
qiaoyikefu
2025/01/17
890
1秒内审核3万条SQL:如何用规范识别与解决数据库风险?
数据库是存储关键信息、支持业务运营和分析决策的基石。然而,数据库的复杂性——研发效率低、安全管控难、变更不稳定、数据管控难,和对其稳定性、安全性的极高要求同样使得它们成为企业 IT 结构中的一个脆弱环节。通过数据库规范的建设,NineData平台目前已支持100+规范,覆盖60种数据源,已审核800万 SQL,实现3万条 SQL 1秒审核完。详细的实践方法和细节,请参阅文章正文。
TakinTalks稳定性社区
2024/03/21
4870
1秒内审核3万条SQL:如何用规范识别与解决数据库风险?
防范攻击 加强管控 - 数据库安全的16条军规
近日的数据安全事故,引发了很多企业的普遍关注,而不少用户从彻查中确实发现自己的数据库已经被注入,这为大家上了数据安全的重要一课。 甚至有的企业要求停用PL/SQL Developer这一工具,虽然这从制度上关上了一个门,但是我们知道数据库类似的门如此之多,如何能够从根本上提升数据库管理的安全,减少数据运维风险呢? 我曾经在《数据安全警示录》一书中总结了种种数据安全风险,提出了很多预防措施和手段,在此整理其中的一些建议供大家参考,当然大家也可以从云和恩墨的安全服务中获得帮助。 我在书中提出了数据安全的五个纬度
数据和云
2018/03/06
1.7K0
防范攻击 加强管控 - 数据库安全的16条军规
数据库系统:第四章 数据库安全性
一些黑客(Hacker)和犯罪分子在用户存取数据库时猎取用户名和用户口令,然后假冒合法用户偷取、修改甚至破坏用户数据。数据库管理系统提供的安全措施主要包括用户身份鉴别、存取控制和视图等技术。
Here_SDUT
2022/08/09
1.5K0
数据库系统:第四章 数据库安全性
【信管1.14】安全(一)信息安全与审计
信息系统的安全问题是非常重要的一个问题。为什么这么说呢?我们的信息,都在网上进行传输,如果没有相应的安全措施的话,很多信息就会被窃取,甚至让你的设备中病毒。影响使用事小,如果利用你的身份信息进行一些违法活动那可就麻烦大了。就像我们之前说过的,大数据信息被人滥用是一方面,而通过窃取获得的你的各类信息再进行大数据分析统计的话,后果可想而知。
硬核项目经理
2023/03/02
1K0
【信管1.14】安全(一)信息安全与审计
安全检测 | 数据库审计系统为何如此重要?
“是的,你我从未谋面,但是你的一切我都知道,你喜欢逛的网站、爱看的电影、常叫外卖的那家餐厅......”
德迅云安全--陈琦琦
2024/05/18
3420
确保你的数据库安全:如何防止SQL注入攻击
最近,越来越多的组织和公司受到SQL注入攻击的困扰。这种攻击可以导致数据库中的敏感信息泄露,破坏数据完整性,甚至可能导致整个系统崩溃。如果您是一名数据库管理员或网站管理员,您需要了解如何保护您的数据库免受SQL注入攻击的威胁。在本文中,小德将介绍什么是SQL注入攻击,以及如何预防和识别此类攻击。
德迅云安全--陈琦琦
2024/01/13
4250
解读OWASP TOP 10
**原理:**将不受信任的数据作为命令或查询的一部分发送到解析器,会产生诸如sql注入、nosql注入、os注入和LADP注入的注入缺陷。攻击者的恶意数据可以诱使解析器在没有适当授权的情况下执行非预期的命令或的访问数据。
黑伞安全
2019/10/16
3K0
推荐阅读
相关推荐
等保2.0高风险项判定汇总
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验