在資料科學領域,有效地處理和分析不同格式的資料至關重要。本文將探討如何利用命令列工具,像是 grepsedcut 以及專門處理 JSON 的 jq,來萃取 XML 和 JSON 資料中的關鍵資訊。此外,我們也會示範如何使用 sortuniqheadtail 等命令,對資料進行排序、統計和分析,並將這些技巧應用於網站伺服器日誌分析,以識別潛在的資安風險和異常流量。透過結合這些強大的命令列工具,可以更有效率地管理和分析大量的資料,並從中獲得有價值的洞察。

資料處理與分析

在進行資料分析之前,瞭解如何處理不同格式的資料是至關重要的。本章節將探討如何使用命令列工具處理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"

若要僅顯示值,可以將輸出傳遞給cuttr

$ 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命令合併它們。

資料分析:從雜亂資料中萃取價值

在前面的章節中,我們使用指令碼收集資料並準備進行分析。現在,我們需要理解這些資料的意義。在分析大量資料時,通常會從廣泛的範圍開始,並隨著對資料理解的加深不斷縮小搜尋範圍。

本章節將使用網站伺服器日誌中的資料作為指令碼的輸入,這僅僅是為了演示。這些指令碼和技術可以輕易地修改以適用於幾乎任何型別的資料。

正在使用的命令

我們將介紹 sortheaduniq 命令,以限制需要處理和顯示的資料。範例 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 位址
2RFC 1413 Ident 識別協定識別碼(如果不存在,則為 -)
3HTTP 認證使用者 ID(如果不存在,則為 -)
4–5日期、時間和 GMT 偏移量(時區)
6–7請求的頁面
8HTTP 協定版本
9網站伺服器傳回的狀態碼
10傳回的檔案大小(位元組)
11參照頁面
12+識別瀏覽器的使用者代理

HTTP 狀態碼

表 7-1 中的狀態碼(欄位 9)通常非常具有資訊價值,可以讓您瞭解網站伺服器如何回應任何給定的請求。常見的狀態碼如表 7-2 所示。

表 7-2. HTTP 狀態碼

狀態碼描述
200OK
401Unauthorized
404Page Not Found
500Internal Server Error
502Bad Gateway

如需完整的狀態碼列表,請參閱 Hypertext Transfer Protocol (HTTP) Status Code Registry。

練習題與解答指引

  1. 給設定檔案 tasks.txt,使用 cut 命令提取第 1(Image Name)、2(PID)和 5(Mem Usage)欄位。
cut -d ';' -f 1,2,5 tasks.txt
  1. 給設定檔案 procowner.txt,使用 join 命令與前一題的 tasks.txt 合併。
join -t ';' -1 2 -2 1 procowner.txt tasks.txt
  1. 使用 tr 命令將 tasks.txt 中的所有分號替換為 tab 字元,並將檔案列印到螢幕上。
tr ';' '\t' < tasks.txt
  1. 編寫一個命令,提取 book.json 中所有作者的名字和姓氏。
# 這需要根據 book.json 的實際格式來決定命令,例如使用 jq:
jq '.authors[] | .first_name + " " + .last_name' book.json

更多資源和這些問題的答案,請造訪 Cybersecurity Ops 網站。

資料排序與分析

在進行資料分析時,從觀察極端值開始往往能獲得有價值的洞察。極端值可能包括出現頻率最高或最低的專案、最大或最小的資料傳輸量等。以網站伺服器日誌檔案為例,不尋常的高頁面存取次數可能指示掃描活動或拒絕服務攻擊,而某個主機下載的位元組數異常高可能意味著網站克隆或資料外洩。

使用命令列工具進行資料排序

為了控制資料的排列和顯示,可以在命令管道的末端使用 sortheadtail 命令。例如:

... | sort -k 2.1 -rn | head -15

這條命令將指令碼的輸出傳給 sort 命令,然後將排序後的輸出傳給 head 命令,列印前 15 行。sort 命令使用第二欄的第一個字元作為排序鍵(-k 2.1),進行反向排序(-r),並將值按照數字順序排序(-n)。使用數字排序的原因是避免將數字按照字母順序排列(如 2 出現在 19 和 20 之間)。

內容解密:

  1. sort -k 2.1 -rn:對輸入資料的第二欄進行反向數字排序。

    • -k 2.1 指定排序鍵為第二欄的第一個字元。
    • -r 表示反向排序。
    • -n 表示按照數字順序排序。
  2. 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

內容解密:

  1. declare -A cnt:宣告一個關聯陣列 cnt,用於儲存不同 ID 出現的次數。

    • -A 選項表示宣告一個關聯陣列。
  2. while read id xtra:從輸入讀取資料,id 表示第一個欄位,xtra 表示剩餘的所有欄位。

    • 這種寫法確保即使輸入行中有多個欄位,也只將第一個欄位賦給 id,其餘賦給 xtra
  3. let cnt[$id]++:對 id 出現的次數進行計數。

    • 如果 id 第一次出現,cnt[$id] 的初始值為空,被視為 0 然後加 1。
  4. for id in "${!cnt[@]}":遍歷所有不同的 id 值。

    • ${!cnt[@]} 取得關聯陣列 cnt 的所有鍵。
  5. 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
}
}'

內容解密:

  1. cnt[$1]++:對第一欄的值進行計數。

    • $1 表示輸入行的第一欄。
  2. 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

內容解密:

  1. awk '$9 == 404 {print $1}' access.log:從 access.log 中提取傳回 404 狀態碼的請求來源 IP 位址。

    • $9 == 404 表示第九欄的值為 404。
    • {print $1} 表示輸出第一欄,即來源 IP 位址。
  2. 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

內容解密:

  1. $9 == "404":檢查日誌的第九欄(HTTP 狀態碼)是否為 404。
  2. {print $1}:輸出第一欄,即發出請求的 IP 位址。
  3. bash countem.sh:將輸出傳遞給 countem.sh,統計每個 IP 位址出現的次數。
  4. 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. $1 == "192.168.0.37":過濾出來源 IP 為 192.168.0.37 的日誌。
  2. {print $0}:輸出完整的日誌行。
  3. cut -d' ' -f7:提取第七欄,即請求的 URL 路徑。
  4. 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)

內容解密:

  1. cut -d' ' -f12-17:提取包含 User-Agent 字串的欄位。
  2. 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

內容解密:

  1. cut -d' ' -f1,10:提取第一欄(IP 位址)和第十欄(傳輸位元組數)。
  2. bash summer.sh:將提取的資料傳給 summer.sh,對每個 IP 的位元組數進行累加。
  3. sort -k 2.1 -rn:按第二欄(總位元組數)進行排序。