天天看點

智能合約安全(基于solidity)智能合約安全

智能合約安全

整數溢出漏洞

加法溢出 乘法溢出 減法溢出

解決方法:采用openzepplin的SafeMath的library

案例一BEC:在乘法時沒有使用Safemath

重入漏洞

重入攻擊可以簡單了解為遞歸,在以太坊智能合約中,調用外部合約或Ether發送到位址,這些外部調用可能被攻擊者劫持。

(1)幾種轉币方式:

transfer()轉賬失敗傳回之前狀态,隻能傳送2300Gas和send()轉賬失敗傳回false退回之前狀态,隻能傳送2300Gas

gas().call.value():發送失敗傳回false,傳遞所有可用gas供調用(可通過gas(gas_value)限制),不能防止重入

(2)fallback函數

合約中可以有唯一的未命名函數,該函數不能有實參,不能傳回任何值。

執行時機

當外部賬戶或其他合約向該合約位址發送Ether時,fallback函數會被調用

當外部賬戶或其他合約向該合約位址發送Ether,但是内部沒有fallback函數時,會抛出異常,然後将以太币退回給發送方

當外部賬戶或其他合約調用了一個該合約中不存在的函數時,fallback函數會被調用

(3)payable辨別的函數

函數上增加payable,可接受Ether,把ether存在合約中

案例:

使用者轉賬采用call函數:

add.call.value(amount)();
balances[add] -= amount;      

如果進行的目标位址是合約位址,預設會調用該合約fallback函數。

重入漏洞成立條件:

  • 合約帶有足夠的gas
  • 有轉賬功能
  • 狀态變量在重入函數調用之後,

由代碼中可以看到,在轉賬發生之後修改狀态變量,進而導緻重入攻擊。

合約編寫原則:

1.明确transfer、send和call的差別,了解Gas機制

2.将Ether發送給外部位址時使用Solidity内置的transfer()函數轉賬時隻發送2300Gas,不足以調用另一個合約

3.確定狀态轉換發生在Ether被發送以及任何外部調用之前“檢查-生效-互動”

4.添加一個在代碼執行過程中所動合約的狀态變量,防止重入調用(常用)

5.防止調用棧攻擊,判斷調用外部合約結果

6.去掉循環處理,讓合約調用者控制循環

假充值漏洞

由于在ERC20的transfer函數中僅僅通過判斷交易事務中是否抛出了異常,來判斷該交易的狀态值status。攻擊者可以通過這個弱點加以攻擊:例如當使用者向DEX發起充值,如果交易所僅僅判斷傳回值,就以為充币成功,就存在假充值漏洞。

在轉賬代碼中,盡量摒棄if-else的判斷對,而是使用require來判斷相應的代币結果。

安全建議:

除了判斷txreceipt status之外,還應判斷充值的以太坊數量是否準确增加

*短位址漏洞

tx.orign身份認證漏洞

攻擊者将自己的合約僞裝成無代碼的私人位址,使得受害者轉賬後産生損失。tx.orign是Solidity的一個全局變量,它周遊整個調用棧并傳回最初發送調用(或事務)的帳戶的位址。在智能合約中使用此變量進行身份驗證會使合約容易受到類似網絡釣魚的攻擊。在對合約調用過程無法控制情況下不要使用tx.orign

代碼執行漏洞

關于call()、delegatecall()、callcode()

call:最常用的調用方式,call的外部調用上下文時被調用者合約,執行環境為被調用者的運作環境,調用後内置變量msg的值會修改為調用者

delegatecall:外部調用上下文是調用者合約,執行環境為調用者的運作環境,調用後内置變量msg的值不會修改為調用者,當A delegatecall B 相當于在A的上下文中執行B的方法

callcode:call的外部調用上下文是調用者合約,環境為調用者的運作環境,調用後内置變量msg的值會修改為調用者

具體的分别:https://www.cnblogs.com/wanghui-garcia/p/9619350.html

Call方法注入漏洞外界可以直接控制合約中的call方法調用的參數,按照注入位置可以分為以下三個場景:

• 參數清單可控

• <address>.call(bytes4 selection, arg1, arg2, ...)

• 方法選擇器可控

• <address>.call(bytes4selection, arg1, arg2, ...)

• Bytes可控

•<address>.call(bytesdata)

•<address>.call(msg.data)

智能合約安全(基于solidity)智能合約安全

安全建議

delegatecall建議

進行調用時發送的data或調用的合約位址可控,複雜上下文關系中調用storage變量時可能造成變量覆寫,此種情況避免使用delegatecall進行調用,使用library進行代碼複用,目前是更加優勢的選擇

call建議

作為開發者來說,需要對ERC223的實作進行排查,不要引入call注入問題,如果非要執行回調,則可以指定方法選擇器字元串,避免使用直接注入bytes的形式來進行call調用。對于一些敏感操作或者權限判斷函數,則不要輕易将合約自身的賬戶位址作為可信的位址。

條件競争漏洞

相關的兩筆交易順序依賴,由于後一筆交易的gasprice更高而優先打包,導緻安全問題的發生。特别是在tranfer中在approve之前采用一個gas費更高的提币代碼就會導緻在授權之前取走原本授權的以太币。

案例:

在一個抽獎的系統裡,例如攻擊者監控大家送出的答案,在答案正确的情況下,自己送出一個gas費更大的正确結果,使得自己的答案更快打包,獲得獎勵。

安全建議:

1.approve()合約所有者在修改授權代币時,先把授權值改為0,再把授權值改為需要設定的值。

require((_Value == 0)||(allowance[msg.sender][_spender]==0))
allowance[msg.sender][_spender] = _value      

2.采用decreaseApprove和increaseApprove

智能合約安全(基于solidity)智能合約安全

未驗證傳回值錯誤

在使用send()過程中,如果沒有對send()進行傳回判定,那麼在外部調用合約中的fallback函數傳回錯誤時導緻傳回值為false。但是并不中斷交易的過程。導緻轉賬失敗。

安全建議:

1.采用withdrawal模式,每個使用者都需要調用一個獨立的函數來處理從合約中發送以太币的問題,獨立處理發送交易失敗的結果。

2.在外部轉賬過程中應該盡量使用transfer()函數,避免使用send函數,如果需要必須檢測傳回值。

拒絕服務漏洞

攻擊者通過消耗合約資源,讓使用者短暫的退出不可操作的合約,把以太币鎖在被攻擊的合約中

攻擊方式出現在合約所有者希望在其投資者之間配置設定代币及在合約中可以看到distribute的場景。

1.當在合約中使用數組時,合約被人循環周遊增加數組的大小,讓for循環執行的大小超出區塊的gas費,導緻distribute函數無法執行

2.另一種常見模式是所有者在合約中具有特定權限,并且必須執行一些任務才能使合約進入下一個狀态。例如,ICO 合約要求所有者 finalize() 簽訂合約,然後才可以轉讓代币

3.合約被編寫成為了進入新的狀态需要将 Ether 發送到某個位址,或者等待來自外部來源的某些輸入。這些模式也可能導緻 DOS 攻擊:當外部調用失敗時,或由于外部原因而被阻止時。在發送 Ether 的例子中,使用者可以建立一個不接受 Ether 的合約。如果合約需要将 Ether 發送到這個位址才能進入新的狀态,那麼合約将永遠不會達到新的狀态,因為 Ether 永遠不會被發送到合約

解決1:在第一個例子中,合約不應該周遊可以被外部使用者人為操縱的資料結構。建議使用 withdrawal 模式,每個投資者都會調用取出函數獨立取出代币。

解決2:

改變合約的狀态需要權限使用者參與。在這樣的例子中(隻要有可能),如果

owner

已經癱瘓,可以使用自動防故障模式。一種解決方案是将

owner

設為一個多簽名合約。另一種解決方案是使用一個時間鎖,其中 [13]行 上的需求可以包括在基于時間的機制中,例如

require(msg.sender == owner || now > unlockTime)

,那麼在由

unlockTime

指定的一段時間後,任何使用者都可以調用函數,完成合約。這種緩解技術也可以在第三個例子中使用。如果需要進行外部調用才能進入新狀态,請考慮其可能的失敗情況;并添加基于時間的狀态進度,防止所需外部調用遲遲不到來。

時間戳依賴漏洞

區塊時間戳曆來被用于各種應用,例如随機數的函數、鎖定一段時間的資金、以及各種基于時間變更狀态的條件語句。礦工有能力稍微調整時間戳,如果在智能合約中錯誤地使用區塊時間戳,可以證明這是相當危險的。

礦工可以根據自己的意願調整時間戳。在這種特殊情況下,如果合約中有足夠的 Ether,挖出某個區塊的礦工将被激勵選擇一個

block.timestamp

now

對 15 取餘為

的時間戳。

預防技術

區塊時間戳不應該用于熵源或産生随機數——也就是說,它們不應該是遊戲判定勝負或改變重要狀态(如果假定為随機)的決定性因素(無論是直接還是通過某些推導)。

時效性強的邏輯有時是必需的;即解鎖合約(時間鎖定),幾周後完成 ICO 或到期強制執行。有時建議使用

block.number

和平均區塊時間來估計時間;即,10 秒的區塊時間運作 1 周,約等于,60480 個區塊。是以,指定區塊編号來更改合約狀态可能更安全,因為礦工無法輕松操縱區塊編号。

如果合約不是特别關心礦工對區塊時間戳的操縱,這可能是不必要的,但是在開發合約時應該注意這一點。

區塊鍊智能合約審計工具:

Solgraph

mythril

SECURIFY

繼續閱讀