參考資料
- ASCIIコード表
目錄
- 一. 業務背景
- 二. 遇到的問題
- 三. 分析
- 3.1 url編碼的前置知識
- 3.2 出現控制字符的`transactionid`分析
- 3.3 16進制分析
- 四. 從文本中查找控制字符所在的行
- 五. 控制字符一覽
一. 業務背景
?在項目中,業務請求對應著下URL
http://www.test.com/?transactionid=uuid_jksje%0Eiuyh&item=20250635
① 其中transactionid=uuid_jksje%0Eiuyh
代表每次交易所產生的訂單編號,后臺接收到請求之后,會從URL中獲取相應的參數,然后將其打印到日志中,最終會產生類似于下面這種日志
140 2024/07/08 12:35:01.547 c1server2 5485 [INFO] SPLREQUEST seqNo=11459,eventController=PMT.payinfoforprc.test.search,transactionid=uuid_jksje%0Eiuyh,code=MPLE2002
110 2024/07/08 12:34:56.457 c1server1 5892 [INFO] MYCODE2005 測試方法被調用。(method=selectSvc_Test_Id param cpId=16xx2 status=OK) userid=adminUser
150 2024/07/08 12:35:02.231 c1server3 5634 [INFO] SPLREQUEST seqNo=11460,eventController=PMT.payinfoforprc.test.search,transactionid=uuid_8934jsklsdf23,code=MPLE1001
120 2024/07/08 12:34:57.235 c1server1 5675 [INFO] MYCODE2005 測試方法被調用。(method=selectSvc_Test_Id param cpId=16xx2 status=OK) userid=adminUser
② 服務器中,有一個batch每天夜里執行,從每天的日志中抽取數據做成csv文件,然后傳送給其他系統用于數據分析。
做成的csv數據的例子如下所示
20250725,1,,,uuid_8934jsklsdf23,MPLE1001,0
20250819,1,,,uuid_klj0345ljhnhl,MPLE1001,0
20250819,1,,,uuid_672342njkjhjs,MPLE1001,0
二. 遇到的問題
最近接到子系統反饋,部分csv文件存在亂碼問題。
使用Linux命令來重現的話,類似于這種情況
?正常情況下的csv文件
apluser@ubuntu24:~$ cat <(echo -e "20250635,1,,,uuid_jksje%0Eiuyh,MPLE2002,0\n20250725,1,,,uuid_8934jsklsdf23,MPLE1001,0") | nkf -w
20250635,1,,,uuid_jksje%0Eiuyh,MPLE2002,0
20250725,1,,,uuid_8934jsklsdf23,MPLE1001,0
😓接到反饋的csv文件
apluser@ubuntu24:~$ cat <(echo -e "20250635,1,,,uuid_jksje\x0Eiuyh,MPLE2002,0\n20250725,1,,,uuid_8934jsklsdf23,MPLE1001,0") | nkf -w
20250635,1,,,uuid_jksjeャヘミフナイーーイャー
イーイオーキイオャアャャャ゜クケウエイウャヘミフナアーーアャー
🤔經過調查發現日志文件中的transactionid
部分混入了控制字符\x0E
,從而導致csv文件中也混入了控制字符\x0E
,進而導致亂碼問題的發生。也就是說日志中的transactionid的值應該是uuid_jksje%0Eiuyh
,但實際上transactionid給變成了uuid_jksje\x0Eiuyh
。
也就是說transactionid中的%0E
居然給轉換為\x0E
。
三. 分析
3.1 url編碼的前置知識
?以下字符可以在 URL 中直接使用,而無需編碼:
- 英文字母:
A-Z、a-z
- 數字:
0-9
- 部分符號:
- _ . ~
?任何 非 ASCII 字符(ASCII 碼 > 127) 都必須編碼,例如:
中國
→%E4%B8%AD%E5%9B%BD
日本
→%E6%97%A5%E6%9C%AC
- 😀 →
%F0%9F%98%80
?這些字符在 URL 中有特殊含義,如果用于數據內容,則必須編碼
字符 | 說明 | URL 編碼 |
---|---|---|
: | 冒號 | %3A |
/ | 斜杠 | %2F |
? | 問號 | %3F |
# | 井號 | %23 |
[ | 左方括號 | %5B |
] | 右方括號 | %5D |
@ | 艾特 | %40 |
! | 感嘆號 | %21 |
$ | 美元符號 | %24 |
& | 和號 | %26 |
' | 單引號 | %27 |
( | 左括號 | %28 |
) | 右括號 | %29 |
* | 星號 | %2A |
+ | 加號 | %2B |
, | 逗號 | %2C |
; | 分號 | %3B |
= | 等號 | %3D |
?例如京東的手機搜索頁面的URL
https://search.jd.com/Search?keyword=手機
?我們直接通過復制網頁的URL,得到的是如下內容
https://search.jd.com/Search?keyword=%E6%89%8B%E6%9C%BA
可以看到 手機 被轉換成 %E6%89%8B%E6%9C%BA
?通過JavaScript進一步驗證
3.2 出現控制字符的transactionid
分析
?下圖展示了混有控制字符的日志
?通過下面的linux命令可以進行再現
echo -e
選項的作用是啟用轉義序列,讓\n
(換行)、\t
(制表符)等特殊字符生效。- 此處的作用是讓
\x0E
當做轉義字符生效
echo -e "seqNo=11459,eventController=PMT.payinfoforprc.test.search,transactionid=uuid_jksje\x0Eiuyh,code=MPLE2002" > 文件名.txt
如果通過JavaScript的decodeURIComponent()
函數來進行轉換的話,也可以得到相同的效果
decodeURIComponent("uuid_jksje%0Eiuyh") # uuid_jksje\x0Eiuyh
也就說,后臺對下面日志輸出的時候,應當將"uuid_jksje%0Eiuyh
視為普通的文本,而不是將其解析為uuid_jksje\x0Eiuyh
。
就算解析為uuid_jksje\x0Eiuyh
,也應當將其視為普通文輸出到日志中,而不應當將\x0E
視作轉義字符進行轉義。
3.3 16進制分析
將正常的transactionid和混有控制字符的transactionid的日志部分截取出來,放在文本編輯器中,通過16進制進行查看,
- 正常的transactionid中的
%0E
所對應的16進制分別是20,30,45 - 而混有控制字符的transactionid卻把普通文本的
0E
給解析為16進制的0e
🧐文本編輯器使用的是notepadd++
,需要下載HEX-Editor
插件進行查看
四. 從文本中查找控制字符所在的行
?創建文件
touch TEMP_T01.dat
echo -e "20250635,1,,,uuid_jksje\x0Eiuyh,MPLE2002,0" > TEMP_T01.dat
echo -e "20250725,1,,,uuid_8934jsklsdf23,MPLE1001,0" >> TEMP_T01.dat
echo -e "20250819,1,,,uuid_klj0345ljhnhl,MPLE1001,0" >> TEMP_T01.dat
echo -e "20250635,1,,,uuid_yumkg\x0Eimbt,MPLE2002,0" >> TEMP_T01.dat
echo -e "20250819,1,,,uuid_672342njkjhjs,MPLE1001,0" >> TEMP_T01.dat
?使用notepad++查看創建的文件
grep -P "\x0E"
:精確查找\x0E
的控制字符cat -v | grep '\^N'
:將\x0E
的控制字符表示為^N
# 按照指定的控制字符 \x0E 進行查找
$ grep -P "\x0E" TEMP_T01.dat
20250635,1,,,uuid_jksjeiuyh,MPLE2002,0
20250635,1,,,uuid_yumkgimbt,MPLE2002,0# cat -v 會將控制字符特殊表示,其中 \x0E 會被表示為 ^N
$ cat -v TEMP_T01.dat | grep '\^N'
20250635,1,,,uuid_jksje^Niuyh,MPLE2002,0
20250635,1,,,uuid_yumkg^Nimbt,MPLE2002,0
?按照控制字符的范圍進行查找
grep -P '[\x00-\x1F]'
grep -E '[[:cntrl:]]'
# 按照控制字符的范圍進行查找
$ grep -P '[\x00-\x1F]' TEMP_T01.dat
20250635,1,,,uuid_jksjeiuyh,MPLE2002,0
20250635,1,,,uuid_yumkgimbt,MPLE2002,0# 按照控制字符的范圍進行查找
$ grep -E '[[:cntrl:]]' TEMP_T01.dat
20250635,1,,,uuid_jksjeiuyh,MPLE2002,0
20250635,1,,,uuid_yumkgimbt,MPLE2002,0
五. 控制字符一覽
10進制 | 16進制 | code | 全拼 | 意思 |
---|---|---|---|---|
0 | 00 | NUL | Null | 空文字 |
1 | 01 | SOH | Start Of Heading | ヘッダ開始 |
2 | 02 | STX | Start Of Text | テキスト開始 |
3 | 03 | ETX | End Of Text | テキスト終了 |
4 | 04 | EOT | End Of Transmission | 伝送終了 |
5 | 05 | ENQ | Enquiry | 問い合わせ |
6 | 06 | ACK | Acknowledgement | 肯定応答 |
7 | 07 | BEL | Bell | 警告音を鳴らす |
8 | 08 | BS | Back Space | 一文字後退 |
9 | 09 | HT | Horizontal Tabulation | 水平タブ |
10 | 0a | LF / NL | Line Feed / New Line | 改行 |
11 | 0b | VT | Vertical Tabulation | 垂直タブ |
12 | 0c | FF / NP | Form Feed / New Page | 改ページ |
13 | 0d | CR | Carriage Return | 行頭復帰 |
14 | 0e | SO | Shift Out | シフトアウト(多バイト文字終了) |
15 | 0f | SI | Shift In | シフトイン(多バイト文字開始) |
16 | 10 | DLE | Data Link Escape | データリンク拡張(バイナリ通信開始) |
17 | 11 | DC1 | Device Control 1 | 裝置制御1 |
18 | 12 | DC2 | Device Control 2 | 裝置制御2 |
19 | 13 | DC3 | Device Control 3 | 裝置制御3 |
20 | 14 | DC4 | Device Control 4 | 裝置制御4 |
21 | 15 | NAK | Negative Acknowledgement | 否定応答 |
22 | 16 | SYN | Synchronous idle | 同期 |
23 | 17 | ETB | End of Transmission Block | 伝送ブロック終了 |
24 | 18 | CAN | Cancel | 取り消し |
25 | 19 | EM | End of Medium | 記録媒體終端 |
26 | 1a | SUB / EOF | Substitute / End Of File | 文字置換 / ファイル終端 |
27 | 1b | ESC | Escape | エスケープ(特殊文字開始) |
28 | 1c | FS | File Separator | ファイル區切り |
29 | 1d | GS | Group Separator | グループ區切り |
30 | 1e | RS | Record Separator | レコード區切り |
31 | 1f | US | Unit Separator | ユニット區切り |
32 | 20 | SPC | Space | 空白文字 |
127 | 7f | DEL | Delete | 一文字削除 |