Summary
generate_seed() in py_order_utils/utils.py uses Python's random.random() (Mersenne Twister) to generate order salt values. Mersenne Twister is not cryptographically secure and its state can be reconstructed after observing ~624 outputs.
Current implementation
from random import random
def generate_seed() -> int:
now = datetime.now().replace(tzinfo=timezone.utc).timestamp()
return round(now * random())
Why this matters
The salt value is part of the EIP-712 signed order struct and is visible on-chain / in the orderbook. While practical exploitation is mitigated by the EIP-712 signature requirement, using a predictable PRNG for any security-adjacent value is a well-known anti-pattern (CWE-338).
Python's random module documentation explicitly states:
"The pseudo-random generators of this module should not be used for security purposes. For security or cryptographic uses, see the secrets module."
Suggested fix
import secrets
def generate_seed() -> int:
return secrets.randbelow(2**32)
This is a drop-in replacement — same return type (int), same range, cryptographically secure. No API changes needed.
Environment
- py_order_utils 0.3.2
- Python 3.12
- Found during a supply chain security audit of a trading bot using py-clob-client
Summary
generate_seed()inpy_order_utils/utils.pyuses Python'srandom.random()(Mersenne Twister) to generate order salt values. Mersenne Twister is not cryptographically secure and its state can be reconstructed after observing ~624 outputs.Current implementation
Why this matters
The salt value is part of the EIP-712 signed order struct and is visible on-chain / in the orderbook. While practical exploitation is mitigated by the EIP-712 signature requirement, using a predictable PRNG for any security-adjacent value is a well-known anti-pattern (CWE-338).
Python's
randommodule documentation explicitly states:Suggested fix
This is a drop-in replacement — same return type (
int), same range, cryptographically secure. No API changes needed.Environment