在資料科學領域,有效地處理和分析不同格式的資料至關重要。本文將探討如何利用命令列工具,像是 grep、sed、cut 以及專門處理 JSON 的 jq,來萃取 XML 和 JSON 資料中的關鍵資訊。此外,我們也會示範如何使用 sort、uniq、head 和 tail 等命令,對資料進行排序、統計和分析,並將這些技巧應用於網站伺服器日誌分析,以識別潛在的資安風險和異常流量。透過結合這些強大的命令列工具,可以更有效率地管理和分析大量的資料,並從中獲得有價值的洞察。
資料處理與分析
在進行資料分析之前,瞭解如何處理不同格式的資料是至關重要的。本章節將探討如何使用命令列工具處理XML和JSON格式的資料,並將分散的資料彙總到一起進行分析。
處理XML資料
XML(Extensible Markup Language)是一種允許使用者自定義標籤和元素來描述資料的語言。範例6-8展示了一個XML檔案的例子。
XML範例:book.xml
<book title="Cybersecurity Ops with bash" edition="1">
<author>
<firstName>Paul</firstName>
<lastName>Troncone</lastName>
</author>
<author>
<firstName>Carl</firstName>
<lastName>Albing</lastName>
</author>
</book>
提取XML資料
要從XML檔案中提取資料,可以使用grep命令。以下命令用於查詢所有的firstName元素:
$ grep -o '<firstName>.*<\/firstName>' book.xml
<firstName>Paul</firstName>
<firstName>Carl</firstName>
若要跨多行查詢,可以結合使用-z和-P選項,並在正規表示式中使用(?s):
$ grep -Pzo '(?s)<author>.*?<\/author>' book.xml
<author>
<firstName>Paul</firstName>
<lastName>Troncone</lastName>
</author>
<author>
<firstName>Carl</firstName>
<lastName>Albing</lastName>
</author>
移除XML標籤
要移除XML標籤並提取內容,可以將grep的輸出傳遞給sed:
$ grep -Po '<firstName>.*?<\/firstName>' book.xml | sed 's/<[^>]*>//g'
Paul
Carl
sed表示式解析
s/expr/other/:用於替換表示式。<[^>]*>:匹配XML標籤。<:匹配字面上的<。[^>]*:匹配任何不是>的字元零次或多次。>:匹配字面上的>。
處理JSON資料
JSON(JavaScript Object Notation)是一種簡單的資料交換格式,由物件、陣列和名稱/值對組成。範例6-9展示了一個JSON檔案的例子。
JSON範例:book.json
{
"title": "Cybersecurity Ops with bash",
"edition": 1,
"authors": [
{
"firstName": "Paul",
"lastName": "Troncone"
},
{
"firstName": "Carl",
"lastName": "Albing"
}
]
}
提取JSON資料
要從JSON檔案中提取資料,可以使用grep命令。以下命令用於提取firstName鍵值對:
$ grep -o '"firstName": ".*"' book.json
"firstName": "Paul"
"firstName": "Carl"
若要僅顯示值,可以將輸出傳遞給cut和tr:
$ grep -o '"firstName": ".*"' book.json | cut -d " " -f2 | tr -d '\"'
Paul
Carl
使用jq處理JSON
jq是一種輕量級的命令列JSON處理器。以下命令用於取得title鍵的值:
$ jq '.title' book.json
"Cybersecurity Ops with bash"
列出所有作者的名字:
$ jq '.authors[].firstName' book.json
"Paul"
"Carl"
資料彙總
在分析資料之前,通常需要將分散在不同檔案和格式的資料彙總到一起。
使用find彙總資料
假設有多個檔案中包含系統資訊,可以使用find命令查詢並彙總包含特定主機名的資料:
find /data -type f -exec grep '{}' -e 'ProductionWebServer' \; -exec cat '{}' >> ProductionWebServerAgg.txt \;
使用join彙總資料
若有兩個檔案分享一列共同的資料,可以使用join命令將它們合併。範例6-10和6-11展示了兩個可以合併的檔案。
ips.txt
ip,OS
10.0.4.2,Windows 8
10.0.4.35,Ubuntu 16
10.0.4.107,macOS
10.0.4.145,macOS
user.txt
user,ip
jdoe,10.0.4.2
jsmith,10.0.4.35
msmith,10.0.4.107
tjones,10.0.4.145
由於兩個檔案都包含IP位址這一共同欄位,因此可以使用join命令合併它們。
資料分析:從雜亂資料中萃取價值
在前面的章節中,我們使用指令碼收集資料並準備進行分析。現在,我們需要理解這些資料的意義。在分析大量資料時,通常會從廣泛的範圍開始,並隨著對資料理解的加深不斷縮小搜尋範圍。
本章節將使用網站伺服器日誌中的資料作為指令碼的輸入,這僅僅是為了演示。這些指令碼和技術可以輕易地修改以適用於幾乎任何型別的資料。
正在使用的命令
我們將介紹 sort、head 和 uniq 命令,以限制需要處理和顯示的資料。範例 7-1 中的檔案將用於命令範例。
範例 7-1. file1.txt
12/05/2017 192.168.10.14 test.html
12/30/2017 192.168.10.185 login.html
sort 命令
sort 命令用於將文字檔案重新排列成數字和字母順序。預設情況下,sort 將按升序排列行,從數字開始,然後是字母。大寫字母將排在相應的小寫字母之前,除非另有指定。
常見命令選項
-r:按降序排序。-f:忽略大小寫。-n:使用數字排序,使 1、2、3 都排在 10 之前(在預設的字母排序中,2 和 3 會出現在 10 之後)。-k:根據行中的部分資料(鍵)進行排序。欄位由空白分隔。-o:將輸出寫入指定的檔案。
命令範例
要按檔名欄位對 file1.txt 進行排序,而忽略 IP 位址欄位,可以使用以下命令:
sort -k 2 file1.txt
您還可以對欄位的子集進行排序。要按 IP 位址的第二個八位元組進行排序:
sort -k 1.5,1.7 file1.txt
這將使用第一個欄位的第 5 到第 7 個字元進行排序。
uniq 命令
uniq 命令過濾掉彼此相鄰的重複行。要刪除檔案中的所有重複行,請務必在使用 uniq 之前對檔案進行排序。
常見命令選項
-c:列印行的重複次數。-f:在比較之前忽略指定的欄位數量。例如,-f 3將忽略每行的前三個欄位。欄位由空格分隔。-i:忽略字母大小寫。預設情況下,uniq是區分大小寫的。
網站伺服器存取日誌熟悉化
本章的大部分範例將使用 Apache 網站伺服器存取日誌。這種日誌記錄了向網站伺服器發出的頁面請求、請求時間以及發出請求的使用者。Apache Combined Log Format 檔案的範例如範例 7-2 所示。本文中參照的完整日誌檔案為 access.log,可以從本文的網頁下載。
範例 7-2. access.log 範例
192.168.0.11 - - [12/Nov/2017:15:54:39 -0500] "GET /request-quote.html HTTP/1.1" 200 7326 "http://192.168.0.35/support.html" "Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.0"
表 7-1. Apache 網站伺服器 Combined Log Format 欄位說明
| 欄位編號 | 描述 |
|---|---|
| 1 | 請求頁面的主機 IP 位址 |
| 2 | RFC 1413 Ident 識別協定識別碼(如果不存在,則為 -) |
| 3 | HTTP 認證使用者 ID(如果不存在,則為 -) |
| 4–5 | 日期、時間和 GMT 偏移量(時區) |
| 6–7 | 請求的頁面 |
| 8 | HTTP 協定版本 |
| 9 | 網站伺服器傳回的狀態碼 |
| 10 | 傳回的檔案大小(位元組) |
| 11 | 參照頁面 |
| 12+ | 識別瀏覽器的使用者代理 |
HTTP 狀態碼
表 7-1 中的狀態碼(欄位 9)通常非常具有資訊價值,可以讓您瞭解網站伺服器如何回應任何給定的請求。常見的狀態碼如表 7-2 所示。
表 7-2. HTTP 狀態碼
| 狀態碼 | 描述 |
|---|---|
| 200 | OK |
| 401 | Unauthorized |
| 404 | Page Not Found |
| 500 | Internal Server Error |
| 502 | Bad Gateway |
如需完整的狀態碼列表,請參閱 Hypertext Transfer Protocol (HTTP) Status Code Registry。
練習題與解答指引
- 給設定檔案
tasks.txt,使用cut命令提取第 1(Image Name)、2(PID)和 5(Mem Usage)欄位。
cut -d ';' -f 1,2,5 tasks.txt
- 給設定檔案
procowner.txt,使用join命令與前一題的tasks.txt合併。
join -t ';' -1 2 -2 1 procowner.txt tasks.txt
- 使用
tr命令將tasks.txt中的所有分號替換為 tab 字元,並將檔案列印到螢幕上。
tr ';' '\t' < tasks.txt
- 編寫一個命令,提取
book.json中所有作者的名字和姓氏。
# 這需要根據 book.json 的實際格式來決定命令,例如使用 jq:
jq '.authors[] | .first_name + " " + .last_name' book.json
更多資源和這些問題的答案,請造訪 Cybersecurity Ops 網站。
資料排序與分析
在進行資料分析時,從觀察極端值開始往往能獲得有價值的洞察。極端值可能包括出現頻率最高或最低的專案、最大或最小的資料傳輸量等。以網站伺服器日誌檔案為例,不尋常的高頁面存取次數可能指示掃描活動或拒絕服務攻擊,而某個主機下載的位元組數異常高可能意味著網站克隆或資料外洩。
使用命令列工具進行資料排序
為了控制資料的排列和顯示,可以在命令管道的末端使用 sort、head 和 tail 命令。例如:
... | sort -k 2.1 -rn | head -15
這條命令將指令碼的輸出傳給 sort 命令,然後將排序後的輸出傳給 head 命令,列印前 15 行。sort 命令使用第二欄的第一個字元作為排序鍵(-k 2.1),進行反向排序(-r),並將值按照數字順序排序(-n)。使用數字排序的原因是避免將數字按照字母順序排列(如 2 出現在 19 和 20 之間)。
內容解密:
sort -k 2.1 -rn:對輸入資料的第二欄進行反向數字排序。-k 2.1指定排序鍵為第二欄的第一個字元。-r表示反向排序。-n表示按照數字順序排序。
head -15:輸出排序後的前 15 行。- 如果需要輸出最後幾行,可以使用
tail -15。
- 如果需要輸出最後幾行,可以使用
統計資料中的事件發生次數
網站伺服器日誌可能包含數以萬計的記錄。透過統計每個頁面被存取的次數或存取來源的 IP 位址,可以更好地瞭解網站的一般活動。有趣的記錄包括:
- 對特定頁面傳回大量 404 狀態碼的請求,可能指示斷鏈。
- 來自單一 IP 的大量 404 請求,可能指示探測活動。
- 傳回大量 401 狀態碼的請求,尤其是來自同一個 IP,可能指示嘗試繞過驗證。
使用指令碼統計事件發生次數
為了檢測這類別活動,需要提取關鍵欄位(如來源 IP 位址)並統計它們在檔案中出現的次數。這裡使用了 cut 命令提取欄位,然後將輸出傳給自定義指令碼 countem.sh。
countem.sh 指令碼解析
#!/bin/bash -
# Cybersecurity Ops with bash
# countem.sh
# Description:
# Count the number of instances of an item using bash
# Usage:
# countem.sh < inputfile
declare -A cnt # assoc. array
while read id xtra
do
let cnt[$id]++
done
# now display what we counted
for id in "${!cnt[@]}"
do
printf '%d %s\n' "${cnt[$id]}" "$id"
done
內容解密:
declare -A cnt:宣告一個關聯陣列cnt,用於儲存不同 ID 出現的次數。-A選項表示宣告一個關聯陣列。
while read id xtra:從輸入讀取資料,id表示第一個欄位,xtra表示剩餘的所有欄位。- 這種寫法確保即使輸入行中有多個欄位,也只將第一個欄位賦給
id,其餘賦給xtra。
- 這種寫法確保即使輸入行中有多個欄位,也只將第一個欄位賦給
let cnt[$id]++:對id出現的次數進行計數。- 如果
id第一次出現,cnt[$id]的初始值為空,被視為 0 然後加 1。
- 如果
for id in "${!cnt[@]}":遍歷所有不同的id值。${!cnt[@]}取得關聯陣列cnt的所有鍵。
printf '%d %s\n' "${cnt[$id]}" "$id":輸出每個id出現的次數。- 使用引號確保即使
id中包含空格也能正確輸出。
- 使用引號確保即使
使用 awk 的替代方案
如果使用的是 bash 4.0 以下版本,可以使用 awk 指令碼替代,如下所示:
# Cybersecurity Ops with bash
# countem.awk
# Description:
# Count the number of instances of an item using awk
# Usage:
# countem.awk < inputfile
awk '{ cnt[$1]++ }
END { for (id in cnt) {
printf "%d %s\n", cnt[id], id
}
}'
內容解密:
cnt[$1]++:對第一欄的值進行計數。$1表示輸入行的第一欄。
END { ... }:在所有輸入行處理完後執行其中的程式碼。- 用於輸出計數結果。
使用範例
可以透過以下命令統計導致 404 錯誤的 HTTP 請求來源 IP 位址次數:
$ awk '$9 == 404 {print $1}' access.log | bash countem.sh
1 192.168.0.36
2 192.168.0.37
1 192.168.0.11
內容解密:
awk '$9 == 404 {print $1}' access.log:從access.log中提取傳回 404 狀態碼的請求來源 IP 位址。$9 == 404表示第九欄的值為 404。{print $1}表示輸出第一欄,即來源 IP 位址。
bash countem.sh:對提取出的 IP 位址進行計數。
透過這些技術,可以有效地分析和監控網站伺服器日誌,從而發現潛在的安全問題。
網站日誌分析與流量統計
網站管理員經常需要分析伺服器日誌以瞭解訪客行為、偵測異常流量,並找出潛在的安全威脅。Apache 或 Nginx 等網頁伺服器會記錄每一次的請求資訊,包括 IP 位址、請求時間、請求資源、HTTP 狀態碼及傳輸的位元組數等。這些資訊對於網站管理員來說是非常寶貴的資源。
統計特定 HTTP 狀態碼的請求
要分析哪些 IP 位址對伺服器發出了最多的 404 請求,可以使用 awk 命令過濾日誌檔,並結合 countem.sh 指令碼進行統計。
$ awk '$9 == "404" {print $1}' access.log | bash countem.sh | sort -rn
111 192.168.0.37
55 192.168.0.36
51 192.168.0.11
42 192.168.0.14
28 192.168.0.26
內容解密:
$9 == "404":檢查日誌的第九欄(HTTP 狀態碼)是否為 404。{print $1}:輸出第一欄,即發出請求的 IP 位址。bash countem.sh:將輸出傳遞給countem.sh,統計每個 IP 位址出現的次數。sort -rn:按次數進行反向排序(由多到少)。
分析特定 IP 的請求行為
進一步分析 IP 位址 192.168.0.37 的請求內容,可以使用以下命令:
$ awk '$1 == "192.168.0.37" {print $0}' access.log | cut -d' ' -f7 | bash countem.sh
1 /uploads/2/9/1/4/29147191/31549414299.png?457
14 /files/theme/mobile49c2.js?1490908488
1 /cdn2.editmysite.com/images/editor/theme-background/stock/iPad.html
1 /uploads/2/9/1/4/29147191/2992005_orig.jpg
14 /files/theme/custom49c2.js?1490908488
內容解密:
$1 == "192.168.0.37":過濾出來源 IP 為192.168.0.37的日誌。{print $0}:輸出完整的日誌行。cut -d' ' -f7:提取第七欄,即請求的 URL 路徑。bash countem.sh:統計每個 URL 被存取的次數。
偵測異常請求模式
當檢查到 IP 位址 192.168.0.36 時,發現它幾乎存取了網站上的每個頁面一次,這種行為可能表明是網站爬蟲或複製工具在運作。進一步檢查 User-Agent 字串可以確認這一點:
$ awk '$1 == "192.168.0.36" {print $0}' access.log | cut -d' ' -f12-17 | uniq
"Mozilla/4.5 (compatible; HTTrack 3.0x; Windows 98)
內容解密:
cut -d' ' -f12-17:提取包含 User-Agent 字串的欄位。uniq:去除重複的行,以顯示唯一的 User-Agent 字串。 結果顯示該 IP 使用了 HTTrack,一款網站下載工具。
統計傳輸的總位元組數
如果要統計每個 IP 位址所接收的總位元組數,可以使用 summer.sh 指令碼:
$ cut -d' ' -f1,10 access.log | bash summer.sh | sort -k 2.1 -rn
192.168.0.36 4371198
192.168.0.37 2575030
192.168.0.11 2537662
192.168.0.14 2876088
192.168.0.26 665693
內容解密:
cut -d' ' -f1,10:提取第一欄(IP 位址)和第十欄(傳輸位元組數)。bash summer.sh:將提取的資料傳給summer.sh,對每個 IP 的位元組數進行累加。sort -k 2.1 -rn:按第二欄(總位元組數)進行排序。