Tact 스마트 계약 보안 관행: TON 생태계의 일반적인 실수

avatar
Web3Caff
2일 전
이 기사는 기계로 번역되었습니다
원문 표시
GM(Good Morning) 与 TON 联合发布了最新的 TON 生态开发者指南,旨在帮助开发者在使用 Tact 语言编程时避免常见错误与陷阱。 작성자: Certik 표지: Photo by ilgmyzin on Unsplash TON(The Open Network)은 혁신적인 특성과 강력한 스마트 계약 성능으로 블록체인 기술의 경계를 계속 확장하고 있습니다. 이더리움 등 초기 블록체인 플랫폼의 경험과 교훈을 바탕으로, TON은 개발자들에게 더 효율적이고 유연한 개발 환경을 제공합니다. 이러한 발전을 이끄는 핵심 요소 중 하나가 바로 Tact 프로그래밍 언어입니다. Tact는 TON 체인을 위해 특별히 설계된 새로운 프로그래밍 언어로, 효율성과 간결성을 핵심 목표로 합니다. 이는 학습과 사용이 쉬우며 스마트 계약과 완벽하게 어울립니다. Tact는 정적 타입 언어로, 간단한 문법과 강력한 타입 시스템을 가지고 있습니다. 그러나 개발자들이 FunC를 사용할 때 겪었던 많은 문제들이 Tact 개발에서도 여전히 존재합니다. 이번 글에서는 감사 사례를 바탕으로 Tact 개발에서 발견된 일반적인 오류들을 분석해 보겠습니다. 목차 데이터 구조 - 선택적 주소 - 데이터 직렬화 - 부호 있는 정수 병행 - 반환 메시지 처리 - Jetton 반환 - Gas 비용 관리 결론 데이터 구조 선택적 주소 Tact 언어는 데이터 구조 선언, 디코딩, 인코딩을 단순화했습니다. 그러나 개발자들은 여전히 주의를 기울여야 합니다. 다음과 같은 예를 살펴보겠습니다. 이는 TEP-74[1] 표준에 따라 jetton 전송을 위한 내부 전송(InternalTransfer) 메시지 선언입니다. response_destination의 선언에 주목하세요. 이는 Address 타입입니다. Tact에서는 주소가 반드시 0이 아닌 주소여야 합니다. 그러나 jetton 표준 참조 구현[2]에서는 0 주소(addr_none)를 허용하는데, 이는 두 개의 0 비트로 표현됩니다. 이는 사용자나 다른 계약이 0 응답 주소로 jetton을 보내려고 시도할 수 있으며, 이 작업이 예기치 않게 실패할 수 있음을 의미합니다. 또한 사용자가 자신의 지갑에 보내는 Transfer 메시지에서 response_destination을 설정할 수 있지만, 보내는 지갑에서 받는 지갑으로의 InternalTransfer 메시지에서는 이 매개변수를 지원하지 않는다면 jetton이 "날아가" 목적지 주소에 도달하지 못하고 결국 손실될 수 있습니다. 나중에 반환된 메시지를 적절히 처리하는 방법에 대해 논의하겠습니다. 이 경우 Address?와 같이 선택적 주소를 허용하는 더 나은 구조 선언이 가능하지만, Tact에서 다음 메시지로 선택적 주소를 전달하는 것은 현재 다소 번거롭습니다. 데이터 직렬화 Tact에서 개발자는 필드의 직렬화 방식을 지정할 수 있습니다. 이 예에서 totalAmount는 coins로 직렬화되고 releasedAmount는 int257(기본값은 Int)로 직렬화됩니다. releasedAmount는 음수가 될 수 있으며 257비트를 차지합니다. 대부분의 경우 직렬화 유형을 생략해도 문제가 없지만, 데이터 통신과 관련된 경우에는 매우 중요합니다. 우리가 감사한 프로젝트의 다음 예를 보겠습니다. 이 데이터 구조는 NFT 프로젝트에서 체인 상의 get_static_data[3] 요청에 대한 응답으로 사용됩니다. 표준에 따르면 응답은 다음과 같아야 합니다. 위의 인덱스는 uint256(int257이 아님)이므로, 반환된 데이터가 호출자에 의해 잘못 해석되어 예측할 수 없는 결과가 발생할 수 있습니다. 아마도 report_static_data 처리기가 롤백되고 메시지 흐름이 중단될 것입니다. 이러한 예는 Tact를 사용할 때도 데이터 직렬화를 고려하는 것이 얼마나 중요한지 보여줍니다.

유형 정수

Int의 직렬화 유형을 지정하지 않으면 위의 예시보다 더 심각한 결과가 발생할 수 있습니다. coins와 달리 int257은 음수가 될 수 있어 프로그래머들을 당황하게 만들곤 합니다. 예를 들어 Tact의 실시간 계약에서 amount: Int를 보는 것은 매우 일반적입니다.

이러한 작성 방식이 반드시 취약점을 의미하는 것은 아닙니다. 해당 금액(amount)은 일반적으로 JettonTransfer 메시지에 인코딩되거나 send(SendParameters{value: amount})에 전달되는데, 후자는 coins 유형을 사용하므로 음수가 허용되지 않습니다. 그러나 한 사례에서 우리는 많은 잔액을 가진 계약이 사용자가 보상, 수수료, 금액, 가격 등을 음수로 설정할 수 있도록 허용하는 것을 발견했습니다. 따라서 악의적인 행위자가 이 취약점을 악용할 수 있습니다.

동시성

이더리움 체인의 개발자들은 재진입 공격에 주의해야 합니다. 이는 현재 함수 실행이 완료되기 전에 동일한 계약의 함수를 다시 호출할 수 있는 공격입니다. 반면 TON 체인에서는 재진입 공격이 불가능합니다.

TON은 비동기 및 병렬 스마트 계약 호출을 지원하는 시스템이므로 처리 작업의 순서를 추적하기가 더 어려워질 수 있습니다. 모든 내부 메시지는 대상 계정에 의해 수신되며, 거래 결과는 거래 자체 이후에 처리되지만 그 외에는 보장이 없습니다(메시지 전달에 대한 자세한 내용은 관련 문서 [4]를 참조하세요).

메시지 3과 메시지 4 중 어느 것이 먼저 전달될지 예측할 수 없습니다.

이러한 경우 중간자 공격(Man-in-the-Middle Attack)[5]은 메시지 흐름에서 발생할 수 있는 일반적인 공격 유형입니다. 안전을 보장하기 위해 개발자는 각 메시지의 전달 시간을 1~100초로 설정해야 하며, 이 기간 동안 다른 메시지가 전달될 수 있습니다. 다음은 보안을 높이기 위한 다른 고려 사항들입니다:

1. 메시지 흐름의 후속 단계에서 사용할 계약 상태를 확인하거나 업데이트하지 마세요.

2. carry-value 패턴[6]을 사용하세요. 값에 대한 정보를 직접 보내지 말고 메시지와 함께 보내세요.

다음은 취약점이 있는 실제 사례입니다:

위 예에서는 다음과 같은 단계가 발생했습니다:

1. 사용자가 collection_jetton_wallet을 통해 NftCollection에 jetton을 보냅니다.

2.TransferNotification이 NftCollection 계약에 전송되어 received_jetton_amount가 기록됩니다.

3. 계약이 jetton을 NftCollection의 소유자에게 전달합니다.

4.Excesses 메시지가 response_destination로 NftCollection에 전송됩니다.

5. Excesses 처리기에서 NftItem이 배포되어 received_jetton_amount를 사용합니다.

여기에는 몇 가지 문제가 있습니다:

첫째, Excesses 메시지가 jetton 표준에 따라 전달된다는 보장이 없습니다. gas 비용이 충분하지 않으면 Excesses 메시지가 건너뛰어지고 메시지 흐름이 중단될 수 있습니다.

둘째, received_jetton_amount를 업데이트하고 이후에 사용하는 것은 동시 실행의 영향을 받기 쉽습니다. 다른 사용자가 동시에 다른 금액을 보내 저장된 금액을 덮어쓸 수 있으며, 이를 악용하여 이익을 얻을 수 있습니다.

동시성의 경우 TON은 전통적인 중앙집중식 다중 스레드 시스템과 유사합니다.

반환 메시지 처리

많은 계약이 반환 메시지 처리를 무시합니다. 그러나 Tact는 이 과정을 간단하고 명확하게 만듭니다:

메시지를 반환 가능 모드로 보낼지 여부를 결정할 때는 두 가지 요인을 고려해야 합니다:

1. 메시지가 실패하면 누가 추가 Toncoin을 받아야 합니까? 대상이 이 자금을 받아야 하고 보내는 계약이 아니라면 비반환 모드[7]로 메시지를 보내는 것이 좋습니다.

2. 다음 메시지가 거부되면 메시지 흐름에 어떤 일이 일어납니까? 반환된 메시지를 처리하여 일관된 상태를 복구할 수 있다면 처리하는 것이 좋습니다. 복구할 수 없다면 메시지 흐름을 수정하는 것이 좋습니다.

다음은 jetton 표준[8]의 예입니다:

1. Excesses 메시지는 비반환 모드로 전송됩니다. 계약이 toncoins를 반환할 필요가 없기 때문입니다.

2. TransferNotification 메시지도 비반환 모드로 전송됩니다. forward_ton_amount는 호출자의 것이므로 계약이 이를 보유할 필요가 없기 때문입니다.

3. 반면 BurnNotification은 반환 가능 모드로 전송됩니다. jetton 마스터 계약에 의해 반환되면 지갑이 잔액을 복구하여 total_supply를 일관되게 유지해야 하기 때문입니다.4. InternalTransfer도 반환 가능합니다. 수신자가 자금을 거부하면 발신자의 지갑이 잔액을 업데이트해야 합니다. 다음 사항을 기억하세요:

1. 반환 메시지는 256비트[9] 원시 메시지만 수신합니다. 메시지 식별 후 유효 데이터는 224비트뿐입니다. 따라서 실패한 작업에 대한 제한된 정보만 얻을 수 있으며, 일반적으로 coins로 저장된 금액입니다.

2. gas 비용이 충분하지 않으면 반환 메시지가 전달되지 않습니다.3. 반환 메시지 자체는 다시 반환될 수 없습니다.

Jetton 반환

경우에 따라 취소 및 반환 메시지 처리가 옵션이 아닐 수 있습니다. 가장 일반적인 예는 TransferNotification을 통해 도착한 jetton을 반환하면 jetton이 영원히 잠길 수 있는 경우입니다. 대신 try-catch 블록[10]을 사용하여 처리해야 합니다.

예를 들어 보겠습니다. EVM에서 거래가 취소되면 모든 결과가 롤백됩니다(gas는 제외). 그러나 TVM에서 "거래"는 일련의 메시지로 분해되므로 그 중 하나만 롤백하면 "계약 그룹" 상태가 일치하지 않을 수 있습니다.

이 문제를 해결하려면 모든 조건을 수동으로 확인하고 긴급 상황에서 수정 메시지를 주고받아야 합니다. 그러나 예외 없이 유효 페이로드를 구문 분석하는 것이 매우 번거로우므로 try-catch 블록을 사용하는 것이 가장 좋습니다.

다음은 일반적인 Jetton 수신 코드 예시입니다:

gas 비용이 부족하면 jetton을 다시 보내는 것도 작동하지 않습니다. 또한 우리 계약의 실제 jetton 지갑이 아닌 sender()의 "지갑"을 통해 jetton을 반환한다는 점에 유의해야 합니다. 이는 누구나 수동으로 TransferNotification 메시지를 보내 우리를 속일 수 있기 때문입니다.

Gas 관리

TON 계약을 감사할 때 가장 일반적인 문제 중 하나는 gas 관리 문제입니다. 주요 이유는 다음과 같습니다:

1. gas 제어 부족으로 인한 문제:

메시지 흐름 실행 불완전:일부 작업은 성공하지만 gas 부족으로 인해 다른 부분은 롤백됩니다. 예를 들어 jetton 지갑에서 보상 획득 작업은 완료되지만 jetton 마스터 계약에서 지분 소각 작업이 무시되면 전체 계약 그룹이 일치하지 않게 됩니다.

사용자가 자신의 계약 잔액을 인출할 수 있음:또한 계약에 과도한 Toncoin이 누적될 수 있습니다.2. TON 계약 개발자가 gas를 관리하고 제어하기 어려움:Tact 개발자는 테스트를 통해 gas 소비량을 파악하고 개발 과정에서 메시지 흐름을 업데이트할 때마다 해당 값을 업데이트해야 합니다.

우리가 제안하는 접근 방식은 다음과 같습니다:

1. "진입점"을 식별하세요: 이는 모든 외부 메시지(최종 사용자 또는 다른 계약(예: Jetton 지갑)에서 오는)를 수락할 수 있는 메시지 처리기입니다.

2. 각 진입점에 대해 가능한 모든 경로를 그리고 gas 소비를 계산하세요. printTransactionFees()를 사용하세요(Blueprint[11]와 함께 제공되는 @ton/sandbox

GM(Good Morning) 자동화된 마켓 마이커(AMM) 파일코인(FIL) 후오비 토큰(HT) 미나 프로토콜(MINA) 옵티미즘(OP) 알위브(AR) 온톨로지(ONT) 앰프(AMP) 플레이댑(PLA) Ronin(RON) 온톨로지가스(ONG) 엔케이엔(NKN) 트론(TRON) 다이(Dai) 톤코인(Toncoin) 블록 링크(Chainlink) 초당 거래 수(TPS) 이더리움 가상 머신(EVM) 초기 바운티 공개(IBO) 미디엄(Medium) 홀더 관점 점유율 온체인 대량 클레임 이더 잔액 커뮤니티 감사

출처
면책조항: 상기 내용은 작자의 개인적인 의견입니다. 따라서 이는 Followin의 입장과 무관하며 Followin과 관련된 어떠한 투자 제안도 구성하지 않습니다.
라이크
즐겨찾기에 추가
코멘트