# On-Chain Data Privacy Illusion and Unauthorized Secret Access
## Description
* The `Vault` struct stores sensitive information in the `secret` field, with the expectation that access control through `get_secret()` will keep this data private
* The fundamental issue is that all on-chain state is publicly readable by anyone with access to the blockchain, regardless of access control functions. The `secret` field is stored in plain text on-chain and can be accessed directly through state reads, completely bypassing the `get_secret()` authorization check
```java
struct Vault has key {
secret: String // @> This is stored on-chain in plain text, accessible to everyone
}
#[view]
public fun get_secret(caller: address): String acquires Vault {
assert!(caller == @owner, NOT_OWNER); // @> Access control is meaningless for on-chain data
let vault = borrow_global<Vault>(@owner);
vault.secret
}
```
## Risk
**Likelihood**:
* Any user with blockchain access can read the global state directly using `borrow_global<Vault>(@owner)`
* Block explorers and indexing services automatically expose all on-chain state data
* Full nodes maintain complete state history, making all historical secrets permanently accessible
* State dumps and debugging tools provide direct access to struct fields
**Impact**:
* Complete exposure of confidential information to unauthorized parties
* Violation of user privacy expectations and potential regulatory compliance issues
* Permanent data exposure (blockchain immutability means secrets cannot be retroactively hidden)
* Loss of trust in the application's security model
## Proof of Concept
* Add this test to `secret_vault.move` file and see the results
```java
#[test(owner = @0xcc)]
fun test_secret_leak(owner: &signer) acquires Vault {
use aptos_framework::account;
account::create_account_for_test(signer::address_of(owner));
// Owner sets a secret
let secret = b"I Love ...";
set_secret(owner, secret);
// Attacker reads the state directly (bypasses get_secret)
let vault = borrow_global<Vault>(@0xcc);
let stolen_secret = &vault.secret;
debug::print(stolen_secret); // Successfully prints the "secret"
}
```
## Recommended Mitigation
Here are recommended mitigations
```diff
- struct Vault has key {
- secret: String
- }
+ // Option 1: Remove on-chain storage entirely
+ // Store sensitive data off-chain with on-chain references only
+
+ struct Vault has key {
+ secret_hash: vector<u8>, // Store hash for verification
+ // other non-sensitive metadata
+ }
+
+ // Option 2: Use commitment scheme
+ public entry fun commit_secret(caller: &signer, commitment: vector<u8>) {
+ let secret_vault = Vault { secret_hash: commitment };
+ move_to(caller, secret_vault);
+ }
+
+ public fun verify_secret(caller: address, secret: vector<u8>): bool acquires Vault {
+ let vault = borrow_global<Vault>(caller);
+ // Verify against commitment/hash
+ aptos_hash::sha3_256(secret) == vault.secret_hash
+ }
```
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.