一、编码的目的在互联网中,信息的传递离不开编码,那为什么需要编码呢?
想象一个场景,一个刚学会说”爸爸、妈妈“的小孩,他和他的爸爸妈妈在玩传话游戏,正常的情况应该是这样的:但是,如果是这样的呢?很明显,传输介质(小孩)不知道爸爸传递的信息该如何说给妈妈,这就导致妈妈不知道爸爸传递了什么
好,现在小孩学会了数数字(比如只会1、2、3),那么爸爸妈妈约定好:
1是飞机
2是火车
3是汽车现在,场景变成了:![[Pasted image 20250805162234.png]]这样一来,虽然儿子不知道如何说飞机、火车、汽车,也能准确地传递信息
没错,这就是编码的目的!
切换回我们的网络世界,编码的核心目的就是为了让各种各样的数据(中文、英文、Emoji等)能以同一种标准化的数据格式来进行传输
二、常见的编码类型1、字符编码 (Character Encoding)ASCII (美国信息交换标准代码)
简介:最早、最简单的编码之一。使用7位(后来扩展到8位)二进制数表示128个(或256个)英文字母、数字和控制符号
特点:非常节省空间,但只能表示英语世界的字符
ISO-8859-1 (Latin-1)
简介:ASCII的扩展,兼容ASCII并加入了西欧语言(如德语、法语、西班牙语)所需的特殊字符
特点:解决了部分西欧语言的显示问题,但对中文、日文等亚洲语言无能力
GBK / GB2312 (国标码)
简介:中国国家标准编码,专门用于表示汉字,GB2312包含约7000个汉字,GBK是其扩展,包含超过2万个汉字
特点:专门解决简体中文的显示问题,但在国际上不通用,类似的还有日本的Shift_JIS、韩国的EUC-KR等
Unicode (统一码)
简介:一个旨在“统一”全世界所有字符的“超级字符集”,它为地球上每一种语言的每一个字符都分配了一个唯一的数字编号(码点)
UTF-8 (Unicode Transformation Format - 8-bit)
特点:当今互联网上最主流的编码方式。它是一种可变长度的编码,用1到4个字节来表示一个Unicode字符,对英文字符只用1个字节(与ASCII完全兼容),对汉字通常用3个字节,这种设计既高效又兼容,使其成为Web标准
UTF-16 / UTF-32
特点:Unicode的其他实现方式。UTF-16使用2或4个字节,UTF-32固定使用4个字节。它们在某些操作系统内部(如Windows、Java)有所应用,但在Web传输上不如UTF-8流行
2、传输编码 (Transfer Encoding)Base64
简介:一种将任意二进制数据(如图片、文件)转换成由64个基本ASCII字符(A-Z,a-z,0-9,+,/)组成的文本字符串的编码方式
应用:在电子邮件中嵌入图片附件(MIME标准);在网页中直接嵌入小图片(Data URL);在某些API(如JWT)中传输二进制签名
目的:确保二进制数据在传输过程中不会因为遇到控制字符或特殊字符而被错误解读或损坏
3、Web专用编码URL编码 (Percent-Encoding)
简介:使用百分号%后跟两位十六进制数来表示URL中的保留字符、不安全字符和非ASCII字符
目的:保证URL的语法结构不被破坏,并且兼容所有网络设备
HTML实体编码 (HTML Entities)
简介:在HTML文档中使用&开头的特殊代码来表示有特殊含义的字符
例子:
<编码为<(less than)
>编码为>(greater than)
&编码为&(ampersand)
空格编码为(non-breaking space)
目的:防止浏览器将这些字符误解为HTML标签或语法的一部分,从而避免页面结构错乱和跨站脚本攻击(XSS)
4、多媒体编码 (Multimedia Encoding)视频编码 (Video Codecs)
简介:定义了如何压缩视频画面的算法。常见的有H.264 (AVC)、H.265 (HEVC)、AV1
目的:在尽可能保持画质的同时,极大地减小视频文件的体积
音频编码 (Audio Codecs)
简介:定义了如何压缩音频信号的算法。常见的有 MP3、AAC(被广泛用于MP4和流媒体)、Opus(常用于实时通信)
5、压缩编码 (Compression Encoding)Gzip / Deflate
简介:Web服务器向浏览器发送网页内容时最常用的无损压缩算法。浏览器接收后会自动解压
应用:HTTP传输
Brotli
简介:由Google开发的更新、更高效的无损压缩算法,压缩率通常比Gzip更高。现代浏览器和服务器都已广泛支持
ZIP / RAR
简介:用户日常使用的文件归档和压缩格式,可以将多个文件和文件夹打包成一个压缩文件
三、编码与解码编码和解码必须使用同一套标准来进行
还是之前的例子,这次爸爸规定:
1是飞机
2是火车
3是汽车但是妈妈规定:
1是橘子
2是西瓜
3是菠萝这样就会出现问题:轻则信息传递错误,重则解码失败程序报错
因此,双方在传输数据之前,先要统一规定好编码类型,以免双方使用不同的编/解码方式
统一的方式很容易想到,就是双方沟通,只需要两人进行统一的约定即可
但是这样的方式,随着涉及人数的增加,会变得非常麻烦复杂,甚至可能演变成每两个人之间都会有不同的编码方式
因此,解决此问题的最好途径就是不要让”消费者“创造”商品“,而是让他们选择”商品“
换言之,就是让权威机构出台固定的几个编码方式供大家使用
这样就可以很容易实现大范围内的统一编码方式了
当然,若通信的对象只有你我二人,那么使用独立的编码方式甚至会让对话变得更加安全
在讲URL编码之前,我们先认识一下什么是URL
一、URL介绍URL(Uniform Resource Locator,统一资源定位符),通俗地来讲,他就是互联网上任何一个资源(例如网页、图片、视频、文件等)的唯一地址
我们根据他全称的三个部分来理解他
统一的 (Uniform):意味着所有URL都遵循同一个语法格式,有一个统一的标准,这保证了任何浏览器或网络工具都能正确地解析它
资源 (Resource):互联网上的任何东西都可以是一个资源,比如一张图片(.jpg)、一个视频文件(.mp4)、一份文档(.pdf)、一个API接口等等
定位符 (Locator):URL提供了找到这个资源的具体位置和访问方法
为了方便理解,我们来打个比方:
我们知道,在互联网中,IP地址能唯一地标识一台主机/服务器,我们现在将这些主机/服务器比作房子
我们还知道,应用程序(也就是我们常说的“服务”)是运行在主机的端口上的
我们要访问某主机上的某个服务其实就是访问该主机上的某个端口
我们可以把端口比作房子内部的各个房间我们要找的资源就是在某应用程序当中,可以把其中的资源比作房间里面的物品
二、URL的构成示例URL:https://www.example.com:8080/path/to/myfile.html?key1=value1#Section_1
1、协议(Protocol/Scheme)对应示例部分的https://
这部分的作用就是告诉浏览器使用什么协议来获取资源
常见的协议还有:
https://(安全的超文本传输协议) - 现代网页的标准,数据经过加密传输
http://(超文本传输协议) - 不加密的网页访问
ftp://(文件传输协议) - 用于上传和下载文件
mailto:(电子邮件) - 用于打开默认的邮件客户端
file://(文件) - 用于访问您本地计算机上的文件
2、主机名(Hostname/Host)对应示例部分:www.example.com
该部分可以是域名也可以是IP地址,但是论他们的本质其实都是IP地址
因为如果是域名的话,还需要进行DNS查询,最终将域名转换成其对应的IP地址
这部分的作用就是标识资源所在的服务器在互联网上的位置,也就是定位“房子”
3、端口(Port)对应示例部分为“:8080”
一个服务器可以提供多种服务(如网页服务、邮件服务等),端口号就是用来区分访问的是哪个服务,也就是定位“房间”
4、路径(Path)对应的示例部分为/path/to/myfile.html
我们拿Web服务来举例子
Web服务程序会被配置一个“Web根目录(Document Root)”,所有我们要在该Web服务中找的资源都在这个“Web根目录”当中,因此URL中的“路径”部分就类似文件系统中的文件路径,这部分也就是我们所说的定位“物品”环节
上面讲述的是“静态资源模型”,其实在Web服务中访问资源还有“动态资源模型”,也就是我们常说的“路由”,其写法也类似于文件路径,但是其更像是一种指令,我们写出来的路径,会在“路由表”中进行匹配,然后去实行对应的逻辑,比如:当路径匹配'/users/profile/{userId}'这种格式时,就去执行'UserController'这个模块里的'showProfile'函数,并把{userId}的值(也就是123)传给它
5、查询字符串(Query String)对应示例部分为?key1=value1
这部分的作用就是向服务器端传输额外的参数,它以?开始,由&分隔的多个“键(Key)=值(Value)”对组成
6、片段 (Fragment/Anchor)对应示例部分为#Section_1
这部分的作用是指向资源内部的一个具体位置(“锚点”),也就是说如果我们定位的资源是一个“书架”,那么所谓的锚点就是定位到该“书架”的第几行第几列
注意:这个部分只在浏览器端起作用,不会发送给服务器,即当页面加载完成后,浏览器会自动滚动到这个片段指定的位置
三、URL与浏览器URL是定位互联网上任一资源的唯一地址,但是单纯的一个URL是起不到任何作用的,他就仅仅是个“地址”而已
就像是你和你女神约会,女神约会完后给你了一张纸条,上面写着她的地址,然后你拿到纸条后就呆在自己家里看着纸条干高兴,啥行动也没有,结果最后女神把你拉黑了……
浏览器就可以看作是URL的实际执行者
我们输入URL的目的不就是为了获得特定的资源吗,我们就把URL这个“地址”交给浏览器,浏览器会做一系列的操作(URL会作为其中信息的一部分,比如建立TCP连接的时候就要根据URL中的主机地址与端口来进行,后续连接建立后还会将URL作为http请求报文中的一部分信息发送给服务器……),把你的请求传达给服务器,然后接收服务器的响应,把响应的结果显示在浏览器上
注意,这里用“一系列操作”省略了很多的技术细节,具体可以看《计算机网络》这本书去了解
浏览器对URL还有个特殊的处理过程,就是自动进行URL编码
现在,我们正式来讲URL编码
一、如何进行URL编码URL编码(URL Encoding)也称为百分比编码(Percent-Encoding)
顾名思义,其采用的就是一种“百分比编码”机制
方法如下:
取出一个需要编码的字符
获取它在特定字符集(现代Web应用中几乎总是UTF-8)中的字节表示
将每个字节的值,转换为一个两位的十六进制数
在每个两位十六进制数前加上一个百分号%
例1:编码字符+
+在ASCII/UTF-8中,字节值为43
43的十六进制表示是2B
前面加上%,得到%2B
例2:编码中文字符中
中这个字,按照UTF-8编码,由三个字节组成
这三个字节的十六进制值分别是E4,B8,AD
对每个字节进行百分比编码,得到%E4%B8%AD
二、URL编码规则编码规则会基于对字符的三种分类
1、未保留字符(Unreserved Characters)包含:
A-Z
a-z
0-9
-
_
.
~对于这些字符,编码规则为:永不编码
注意,这里的“永不编码”是一种规定,并不是说未保留字符无法得到其URL编码后的形式我们无论是通过在线编码平台也好,自己写代码也好,当我们对未保留字符进行编码的时候,得到的结果就是其本身,造成该现象的根本原因就是”永不编码“规则,那些编码函数会根据规则绕过对未保留字符的编码但是,我们可以根据“如何进行编码”去手动编码,因为我们可以无视这条规则,这样一来也是可以得到其编码后的结果的
2、保留字符(Reserved Characters)包含:
字符主要语法作用?分隔路径与查询参数&在查询参数中分隔不同的参数=在查询参数中分隔键与值/分隔主机、端口和路径的各个部分:分隔协议与主机、主机与端口#分隔主URL与片段(用于页面内锚点跳转)@在授权信息中分隔用户名密码与主机! $ ' ( ) * + , ; [ ]在URL的不同部分有其他特殊分隔含义URL中的语法与结构都是通过这些保留字符构建出来的
对于这些字符,编码规则为:可能编码
当保留字符作为语法或结构逻辑的时候,则不对其进行编码;反之,则对其进行编码
对于”什么时候会作为语法或者结构逻辑“,我们说过”浏览器是URL的执行者“,那我们可以认为只有浏览器才真正认识并使用URL,其他情况下出现的URL都是无稽之谈
因此,在浏览器中输入的URL中的保留字符,一律不编码(若要该保留字符作为非语法逻辑,就需要手动URL编码);其他情况(非浏览器输入URL的情况)则一律编码
单纯这么理解可能有点抽象,我举几个具体例子
我们先写一个后端页面(我的本地环境,用的phpstudy,文件名index.php):
$name = $_GET['name'];
echo $name;
?>
此时,我们先按f12,然后在浏览器中输入:打开”网络“此时看到的信息就是经过浏览器自动URL编码后的结果
可以看到,保留字符”&“并没有被URL编码,”&“作为正常URL语法存在
此时也可以看到页面没有打印任何数据(因为我们没传)
如果我们要求,”&“就是作为$name的值存在,要怎么做呢?
我们就需要对”&“进行URL编码
好,我们修改后端php代码:
echo urlencode("&");
?>
根据代码,我们知道,这里的URL编码过程是通过代码实现的不涉及浏览器,因此这里的”&“是非语法逻辑会被编码成%26好,回到之前的代码,即:
$name = $_GET['name'];
echo $name;
?>
此时,我们输入页面成功显示”&“
要理解这里发生了什么,就需要先补充个知识点:在PHP官方手册中的urldecode()这部分有个非常明显的”Warning(警告)“
Warning
The superglobals $_GET and $_REQUEST are already decoded. Using urldecode() on an element in $_GET or $_REQUEST could have unexpected and dangerous results.
警告
超全局变量$_GET和$_REQUEST已经是解码过的。对$_GET或$_REQUEST中的元素使用 urldecode()可能会导致意想不到的、危险的结果。
因此,我们知道,后端代码在接收到”%26“的时候,会经过一次URL解码操作
那么此时$name变量的值就是”&“
因此,我们在浏览器中就能看到”&“
3、其他字符/不安全字符其他字符也就是既不是“未保留”也不是“保留”的字符,也常常被被称为不安全字符
对于这类字符,编码规则为:一定编码
特殊情况:“%”属于不安全字符,但是浏览器中的自动URL编码不会对其进行编码
三、如何进行解码解码其实就是编码的逆过程,其步骤大致如下:
将”%“去除
将紧跟刚去除的”%“后两位视为二位十六进制数
当所有的”%“都处理完毕之后,将此时得到的字符序列拿特定的字符集去进行匹配,比如遇到E4B8AD,他就知道这是”中“字
如果,解码时指定的”字符集“与之前编码时指定的”字符集“不匹配,就会出现乱码现象
下面通过CTF例题的方式,来加深对URL编码的理解
一、CTF题目来源XCTF-Web-PHP2
二、信息搜集进入场景,只能看到一条提示信息翻译成中文就是”你能登入这个网站吗?“
一般来说,看到这个提示会想到是不是需要我们去扫目录,然后出现一个登入界面再继续深入
但是经过测试之后,本题并没有相关的登入页面,那么关键点就在于本页面上了(index.php)
根据做题经验来说,出现这样提示信息,一定是没满足一些条件,比如:http请求头中是不是需要加入信息或者修改信息、参数是否需要传递、是否前端页面有限制等等……
由于这个页面使用后端代码php写出来的,因此,我们在页面上是看不到后端代码逻辑的,只有其执行后的结果
这里就涉及到一个技巧,查看.phps
PHPS文件是PHP Source文件的缩写,顾名思义它们是PHP源代码文件,通常用于让用户(访问者)通过Web浏览器查看PHP代码的内容
这也是CTF夺旗赛常用的手段,在比赛中PHPS文件可能会被用来展示PHP代码,以便参与者分析和解决问题
查看:
三、代码审计审计代码后发现,这是一个很简单的逻辑
我们会通过get传参为变量id赋值,如果通过&_GET['id']得到的值为”admin“,则失败;若通过&_GET['id']得到的值经过一次urldecode之后得到的值为”admin“则成功获得flag
index.phps文件与index.php文件是独立的,即里面的源码并非一模一样,出题人故意将展示文件中的flag部分隐藏(用”x“代替),只是展示代码逻辑部分,具体细节被删减以免直接得到flag
四、思路总结我们知道,如果直接输入”admin“,即输入:
http://xx.xx.xx.xx:61312/?id=admin
那么经过浏览器的自动URL编码之后,得到的URL还是:
http://xx.xx.xx.xx:61312/?id=admin
在后端中,$_GET['id']会对传过来的“admin”进行解码,得到的结果当然还是”admin“,因此绕不过第一个判断,getflag失败
如果我们输入的是"%61%64%6D%69%6E",即经过手动URL编码的”admin“
对此处“手动”二词有疑惑的记得回顾知识点哦
经过浏览器的自动URL编码过后,得到的URL为:
http://xx.xx.xx.xx:61312/?id=%61%64%6D%69%6E
传输到后端,$_GET['id']会对传过来的“%61%64%6D%69%6E”进行解码,得到的结果就是“admin”,还是绕不多第一个判断,即失败
大家应该也注意到了,在第二个判断前有一次urldecode()操作,配合之前的$_GET['id']相当于会有二次解码的效果
因此,我们构造的payload应该为:“%2561%2564%256D%2569%256E”,其中“%25”是“%”URL编码后的结果
五、最终利用我们将“%2561%2564%256D%2569%256E”传给id,即
http://xx.xx.xx.xx:61312/?id=%2561%2564%256D%2569%256E
经过浏览器自动URL编码过后,得到的URL还是:
http://xx.xx.xx.xx:61312/?id=%2561%2564%256D%2569%256E
经过$_GET['id']之后得到的是$id="%61%64%6D%69%6E"
成功绕过第一个判断
再经过$_GET[id] = urldecode($_GET[id]);之后
$_GET[id]的值就成为了“admin”,符合第二个判断成功拿下flag!