自動化在提升效率的同時,也引入了新的安全挑戰。瞭解任務的複雜度和潛在風險對於設計安全的自動化流程至關重要。本文從簡單任務的 Shell 指令碼自動化出發,逐步探討如何評估風險、設計安全機制,並最終應用於複雜任務的自動化設計。過程中,我們將使用 SQL 範例說明資料函式庫操作的安全性考量,並結合 Cynefin 框架分析不同任務的複雜度,提供工程師在實踐中設計更安全可靠的自動化流程的參考。

自動化中的安全性考量

在組織中實作自動化的程度,很大程度上取決於負責實施自動化的團隊的技術成熟度和能力。然而,無論團隊的技能如何,每個組織都可以執行並受益於某種程度的自動化。自動化不僅可以減少系統中的工作量和潛在錯誤,還能在正確設計的情況下,在使用它的團隊成員中創造一種安全感。

任務中的安全性

你是否曾經執行過一個具有令人驚訝的副作用的命令?或者,也許你曾經在 Linux 系統中執行 rm -rf * 命令,並且在按下 Enter 鍵之前,需要仔細檢查你所在的目錄達 20 次之多。你對執行某個任務的舒適度與事情出錯時的潛在結果直接相關。

任務中的安全性是指,如果任務執行不正確,其結果不會造成危險後果。我將繼續以我對烹飪的恐懼為例。我總是很擔心烤雞沒有煮熟。吃未煮熟的雞肉可能會帶來危險!同樣地,我對在烤箱中烹飪雞塊沒有任何恐懼。雞塊通常是預先煮熟和冷凍的,因此,烹飪雞塊不當的後果比烹飪生雞的後果要安全得多。它們也有精確的說明,幾乎沒有任何變數。我不需要根據變數(如雞的大小)進行任何修改;我只需要遵循說明。你嘗試根據你對任務的瞭解來評估風險。當你評估一個任務時,你正在嘗試瞭解每個任務的難易程度。

為什麼安全性很重要?

因為當你開始自動化任務時,你需要考慮自動化現在執行的每個任務的潛在副作用。到某種程度,使用者對操作的控制力比以前要小。

想像一下,你正在自動化一個程式的安裝。在以前,執行安裝的人對每一個被執行的命令都有完全的控制權。他們知道被傳遞的引數和被執行的具體命令。在這些步驟被封裝成一個單一命令的世界中,他們失去了這種精細的控制,以換取更簡單的執行。以前,使用者擁有安全性的責任,但現在,由於自動化,這種責任已經轉移到了你,即開發人員身上。

設計安全性

在開發應用程式時,人們投入了大量的精力,以確保對終端使用者有深入的瞭解。整個學科都圍繞著使用者經驗和使用者介面設計而建立,以確保在應用程式設計過程中,你對最可能的使用者、他們的經驗和期望,以及他們表現出的行為型別有準確的瞭解。思考過程是,如果某件事情是危險的,而系統允許使用者這樣做,那麼這是系統的錯誤,而不是使用者的錯誤。對於終端使用者來說,這是一個很好的主張,從 Facebook 到 Microsoft Word 的應用程式都因為這一學科而變得更強大。

但是,當你開發要在生產環境中執行的系統時,你並沒有考慮到很多這樣的因素。支援應用程式的工具通常很少或根本不存在。必須執行的關鍵任務通常被留給最低層級的技術。將管理員帳戶的密碼重置這樣簡單的事情,常常被降格為手工製作的 SQL 查詢,每個人都將其儲存在他們的筆記應用程式中,以及其他用於保持系統功能所需的深奧命令。

這不僅是不可取的,而且當常見任務需要超出應用程式定義引數的步驟時,也是危險的。以密碼為例,有人可能會輕易地執行命令 UPDATE users SET PASSWORD = 'secret_value' WHERE email = 'admin@company.net'。這段程式碼看起來相當直接,很容易推斷出這條 SQL 應該有效。那就是,直到你意識到資料函式庫中的密碼欄位是經過雜湊處理的。

雜湊函式的作用

雜湊函式允許使用者將任意大小的資料值對映到另一個固定大小的資料值。密碼雜湊函式常用於將使用者的密碼對映到雜湊值以進行儲存。由於雜湊函式是單向的,瞭解雜湊值對於確定建立該雜湊值的輸入並不是很有用。大多數密碼系統會將使用者提交的密碼進行雜湊處理,然後將計算出的雜湊值與為使用者儲存的雜湊值進行比較。

UPDATE users 
SET PASSWORD = 'secret_value' 
WHERE email = 'admin@company.net';

內容解密:

這段 SQL 命令看似簡單直接,但實際上存在重大安全隱患。因為資料函式庫中的密碼欄位是經過雜湊處理的,直接將密碼設為 'secret_value' 將導緻密碼驗證失敗。正確的做法應該是將密碼進行雜湊處理後再儲存。

  1. 安全性風險:直接修改密碼欄位可能導致安全漏洞,因為大多數系統會對密碼進行雜湊處理。
  2. 正確做法:應該使用適當的雜湊函式對密碼進行處理後再儲存,例如使用 bcrypt 或 PBKDF2 等演算法。
  3. 程式邏輯:這段 SQL 命令需要與後端的密碼管理邏輯相結合,確保密碼的安全性。

自動化安全性的最佳實踐

  1. 風險評估:在自動化任務之前,仔細評估任務失敗可能帶來的風險。
  2. 設計安全機制:在自動化指令碼中加入安全檢查和確認步驟,避免因誤操作導致嚴重後果。
  3. 測試和驗證:徹底測試自動化指令碼,確保其在各種情況下都能正確執行。
  4. 持續監控:對自動化任務進行持續監控,及時發現並修復潛在問題。

透過遵循這些最佳實踐,可以顯著提高自動化任務的安全性,減少潛在風險,並提升整體系統的穩定性和可靠性。

安全自動化設計的關鍵要素

在設計自動化系統時,瞭解操作員的需求和限制至關重要。操作員可能是系統的管理員或維護人員,他們需要與系統互動以執行特定任務。為了確保系統的安全性和可靠性,必須考慮操作員的觀點和需求。

絕不能假設操作員具備完整知識

在設計自動化系統時,絕不能假設操作員具備與開發人員相同的知識水平。操作員可能不熟悉系統的內部工作原理或技術細節。因此,系統的設計應當清晰、直觀,並提供足夠的資訊和指導,以幫助操作員完成任務。

取得操作員的觀點

為了設計出有效的自動化系統,必須瞭解操作員的觀點和需求。這可以透過與操作員進行訪談、觀察他們的工作流程等方式來實作。透過取得操作員的反饋和意見,可以更好地瞭解他們的需求和挑戰,從而設計出更符合他們需求的系統。

始終確認高風險操作

在設計自動化系統時,必須始終確認高風險操作。例如,在執行刪除或修改資料等高風險操作時,系統應當提示操作員確認其意圖,以避免意外錯誤。

# 示例:確認刪除操作
read -p "您確定要刪除該檔案嗎?(yes/no)" -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]
then
    # 執行刪除操作
    rm -rf /path/to/file
else
    echo "操作已取消"
fi

內容解密:

此指令碼用於確認刪除操作的意圖。首先,它會提示操作員輸入 yesno 以確認是否要刪除檔案。如果操作員輸入 yes,則執行刪除操作;否則,操作將被取消。

避免意外副作用

在設計自動化系統時,應當避免意外副作用。例如,如果一個指令碼需要關閉應用程式才能執行備份,那麼最好提示操作員關閉應用程式,而不是自動關閉它。

# 示例:備份指令碼
echo "請先關閉應用程式,然後再繼續備份操作"
read -p "是否已關閉應用程式?(yes/no)" -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]
then
    # 執行備份操作
    tar -czf backup.tar.gz /path/to/data
else
    echo "請先關閉應用程式,然後再試"
fi

內容解密:

此指令碼用於執行備份操作。首先,它會提示操作員關閉應用程式。如果操作員確認已關閉應用程式,則執行備份操作;否則,將提示操作員先關閉應用程式。

任務複雜度

不同的任務具有不同的複雜度。瞭解任務的複雜度有助於設計出更有效的自動化系統。可以使用 Cynefin 框架等決策工具來瞭解任務的複雜度和不確定性。

自動化任務評估與複雜度分析

在進行自動化時,瞭解任務的複雜度是至關重要的。Cynefin 框架提供了一個方法來評估任務的複雜度,將其分為簡單、複雜和混亂三種型別。本文將重點討論前兩種型別,因為它們在自動化中具有重要的意義。

簡單任務

簡單任務是指那些變數少且易於理解的任務。這些任務的執行步驟是明確的,並且可以根據變數的不同進行調整。例如,安裝新軟體就是一個簡單任務。安裝步驟可能會根據作業系統的型別和版本而有所不同,但這些變數的影響是眾所周知的,可以事先進行檔案記錄,以便他人參考。

簡單任務範例:資料函式庫軟體安裝

安裝資料函式庫軟體的步驟會根據作業系統的不同而有所不同。如果使用的是根據 RedHat 的作業系統,可能需要下載並安裝 RPM 套件。如果使用的是 Windows Server,則可能需要下載 MSI 安裝程式。儘管安裝方法不同,但步驟都是明確且易於理解的。

# 在 RedHat 系統上安裝資料函式庫軟體
sudo yum install -y database.rpm

# 在 Windows Server 上安裝資料函式庫軟體
msiexec /i database.msi /quiet

內容解密:

  1. sudo yum install -y database.rpm:這行指令用於在 RedHat 系統上安裝資料函式庫軟體。-y 引數表示自動確認安裝,無需手動干預。
  2. msiexec /i database.msi /quiet:這行指令用於在 Windows Server 上安裝資料函式庫軟體。/quiet 引數表示靜默安裝,無需手動干預。

複雜任務

複雜任務涉及多個步驟,需要不同的專業知識,但一旦完成,任務往往是可重複的。例如,手動將資料函式庫伺服器從次要角色提升到主要角色就是一個複雜任務。這類別任務需要根據不同的決策點執行不同的子任務。

複雜任務範例:資料函式庫伺服器升級

將資料函式庫伺服器從次要角色提升到主要角色涉及多個決策點。如果次要資料函式庫伺服器未與主要伺服器完全同步,則需要執行一系列操作來調整升級步驟。

-- 檢查次要資料函式庫伺服器是否與主要伺服器同步
SHOW SLAVE STATUS;

-- 如果同步,執行升級操作
STOP SLAVE;
RESET SLAVE ALL;
-- 其他升級步驟...

內容解密:

  1. SHOW SLAVE STATUS;:這行指令用於檢查次要資料函式庫伺服器的同步狀態。
  2. STOP SLAVE;RESET SLAVE ALL;:這些指令用於停止並重置次要資料函式庫伺服器的複製功能,是升級過程中的一部分。

複雜任務

複雜任務通常涉及多個變數,每個變數都可能影響其他變數的效果。微調資料函式庫效能就是一個典型的複雜任務。需要考慮資料函式庫工作負載、伺服器資源、流量模式以及速度與可還原性之間的權衡。

複雜任務範例:資料函式庫效能微調

微調資料函式庫效能需要考慮多個因素,包括但不限於組態引數、索引最佳化、查詢最佳化等。

-- 調整資料函式庫組態引數
SET GLOBAL innodb_buffer_pool_size = 1073741824;

-- 建立索引以最佳化查詢效能
CREATE INDEX idx_column_name ON table_name (column_name);

內容解密:

  1. SET GLOBAL innodb_buffer_pool_size = 1073741824;:這行指令用於調整 InnoDB 緩衝池的大小,以最佳化資料函式庫效能。
  2. CREATE INDEX idx_column_name ON table_name (column_name);:這行指令用於在指定欄位上建立索引,以提高查詢效率。

如何評估任務複雜度

在評估任務複雜度時,需要考慮執行任務的人員的專業知識和經驗,而不是自己的。這可以透過跨功能協作來實作。例如,編寫佈署軟體的時,目標讀者的專業知識和經驗將影響的詳細程度。

瞭解任務的複雜度可以幫助我們思考如何透過自動化來安全地執行任務。即使是複雜的任務,如果失敗的風險較低,也可以透過試錯來學習和改進。

風險與安全性的考量

在評估自動化任務時,不僅要考慮任務的複雜度,還要考慮其潛在的風險和安全性。對於風險較低的任務,可以採取更靈活的自動化方法,而對於風險較高的任務,則需要更高的確定性和安全性措施。

風險評估範例:資料刪除操作

刪除資料函式庫中的資料是一個高風險操作,需要謹慎處理。

-- 刪除資料前進行備份
mysqldump -u username -p database_name > backup.sql

-- 刪除資料
DELETE FROM table_name WHERE condition;

內容解密:

  1. mysqldump -u username -p database_name > backup.sql:這行指令用於備份資料函式庫,以防止資料丟失。
  2. DELETE FROM table_name WHERE condition;:這行指令用於刪除符合特定條件的資料,需要謹慎操作以避免資料丟失。

總之,瞭解和評估任務的複雜度和風險是自動化過程中至關重要的一步。透過恰當的分析和設計,可以實作安全、高效的自動化解決方案。

自動化簡單任務以提升流程安全性

自動化簡單任務是將安全性引入流程和系統的絕佳起點。簡單任務通常是有條理且容易被編碼成指令碼語言的。如果這是您第一次嘗試自動化任務,建議始終從小處著手,保持簡單。同時,專注於經常執行的任務,這樣您就可以持續獲得對自動化的反饋。讓您的第一次自動化嘗試成為每季度執行一次的任務,並不會給您太多學習和調整過程的機會。

開始自動化的最簡單方法是專注於自動化的小目標,然後慢慢地在這些目標上進行擴充套件。如果您一開始就關注自動化的最終願景,您可能會很快被潛在的問題和障礙所淹沒。一個簡單的目標可能是透過單一命令列實用程式執行多步驟任務。

舉例來說,讓我們回到本章節開頭提到的Gloria。假設她的處理流程如下:

  1. 取得所有處於失敗狀態的訂單列表。
  2. 驗證這些訂單既是失敗訂單,又是被支付處理器取消的訂單。
  3. 將這些訂單更新到新狀態。
  4. 驗證這些訂單是否已移動到新狀態。
  5. 向使用者顯示結果。

這些步驟看似簡單直接,但如果某一步驟被遺漏,仍然可能導致問題。如果操作員在步驟1中發出了錯誤的SQL命令怎麼辦?如果操作員更新了錯誤的訂單到新狀態怎麼辦?這些步驟可以輕易地被編碼成一個簡單的shell指令碼,作為自動化的第一次嘗試。看看下面我將稱之為update-orders.sh的程式碼:

#!/bin/bash
echo "INFO: 查詢失敗訂單"
psql -c 'select * from orders where state = "failed" and payment_state = "cancelled"'
echo "INFO: 更新訂單"
psql -c 'update orders set state = "cancelled" where order_id in (SELECT * from orders where state = "failed" and payment_state = "cancelled")'
echo "訂單已更新。該狀態下應該不再有訂單"
psql -c 'select count(*) from orders where state = "failed" and payment_state = "cancelled"'

內容解密:

此指令碼執行了一系列操作來更新處於失敗狀態且被取消的訂單。首先,它查詢並列出當前處於失敗狀態且被取消的訂單。接著,它更新這些訂單的狀態為已取消。最後,它再次查詢以確認該狀態下的訂單數量應該為零。這個過程透過自動化減少了人為錯誤的可能性。

這個指令碼看似非常簡單,但它提供了一定程度的一致性,讓操作員在其執行過程中感到舒適和安全。它還提供了可重複性。每次有人執行這個過程時,您都可以對所採取的步驟和結果充滿信心。即使是這樣一個簡單的任務,執行單一的update-orders.sh命令似乎比之前的五步驟過程更容易、更可取。

評估任務或整體問題的複雜度

在考慮自動化任務時,您可能會混淆是否應該嘗試評估個別、離散任務的複雜度還是整體問題本身的複雜度。例如,我應該評估烹飪雞肉的任務複雜度?還是評估導致我整體解決方案——烹飪雞肉——的四個個別任務的複雜度?我會專注於評估個別任務。通常,問題將根據其中最複雜的任務被歸類別。如果您有一個包含四個底層任務的問題,並且您評估其中三個任務為簡單,最後一個任務為複雜,那麼整體問題將被視為複雜。

加強自動化的健壯性

敏銳的讀者可能會問自己:“我怎麼知道其中一個步驟不會失敗?”這是一個合理問題。但如果在手動執行時步驟失敗了怎麼辦?您可能會讓操作員停止並執行某種初始故障排除。您也可以對自動化執行相同的操作,在每個步驟後提示使用者是否繼續。

修改後的指令碼如下。它現在有點長,但它讓您在指令碼的關鍵部分有機會離開,如果操作員看到前一個命令輸出中有不正確的東西。再次記住,您專注於入門,而不是讓自動化贏得駭客馬拉松競賽。

#!/bin/bash
echo "INFO: 查詢失敗訂單"
psql -c 'select * from orders where state = "failed" and payment_state = "cancelled"'
response=""
while [ "$response" != "Y" ] && [ "$response" != "N" ]; do
  echo "這個訂單數量看起來正確嗎?(Y 或 N)"
  read response
done
if [ "$response" == 'Y' ];then
  echo "INFO: 更新訂單"
  psql -c 'update orders set state = "cancelled" where order_id in (SELECT * from orders where state = "failed" and payment_state = "cancelled")'
  echo "訂單已更新。該狀態下應該不再有訂單"
  psql -c 'select count(*) from orders where state = "failed" and payment_state = "cancelled"'
else
  echo "放棄命令"
  exit 1
fi

內容解密:

此修改後的指令碼在執行關鍵步驟前增加了使用者確認機制。在列出失敗訂單後,它會詢問使用者是否繼續更新訂單。如果使用者確認,則繼續執行更新操作;否則,指令碼將放棄並離開。這種機制增強了自動化的健壯性,避免了因盲目執行而導致的潛在錯誤。