You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/anti-patterns.md
+5-2Lines changed: 5 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -83,9 +83,12 @@ Accidentally exposed fields can be a security hole.
83
83
### Solution
84
84
85
85
When writing your smart contract, look at every field and function and make sure
86
-
that require access through an [entitlement](./language/access-control.md#entitlements) (`access(E)`),
86
+
they require access through an [entitlement](./language/access-control.md#entitlements) (`access(E)`),
87
87
or use a non-public [access modifier](./language/access-control.md) like `access(self)`, `access(contract)`, or `access(account)`,
88
-
unless otherwise needed.
88
+
unless you are making a deliberate design decision to allow completely open and unrestricted access to read that field or call that function.
89
+
90
+
The only functions that should be `access(all)` are `view` functions and the only fields that can be `access(all)` are basic types like numbers or addresses.
91
+
Complex fields like arrays, dictionaries, structs, resources, or capabilities should always be `access(self)`.
89
92
90
93
## Capability-Typed public fields are a security hole
Copy file name to clipboardExpand all lines: docs/language/access-control.md
+19-7Lines changed: 19 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -12,18 +12,20 @@ In Cadence, access control is used in two ways:
12
12
A user is not able to access an object unless they own the object or have a reference to that object. This means that nothing is truly public by default.
13
13
14
14
Other accounts cannot read or write the objects in an account unless the owner of the account has granted them access by providing references to the objects.
15
+
16
+
This kind of access control is covered in [capabilities] and [capability management].
17
+
18
+
2. Access control within contracts and objects, using access modifiers (`access` keyword).
19
+
20
+
This page covers the second part of access control, using access modifiers.
15
21
16
22
:::warning
17
23
18
-
Remember that in this case, `private` refers to programmatic access to the data with a script or transaction. It is **not safe** to store secret or private information in a user's account. The raw data is still public and could be decoded.
24
+
Remember that in this case, `private` refers to programmatic access to the data with a script or transaction. It is **not safe** to store secret or private information in a user's account. The raw data is still public and could be decoded by reading the public blockchain data directly.
19
25
20
26
:::
21
-
22
-
This kind of access control is covered in [capabilities] and [capability management].
23
-
24
-
1. Access control within contracts and objects, using access modifiers (`access` keyword).
25
27
26
-
This page covers the second part of access control, using access modifiers.
28
+
## The `access` keyword
27
29
28
30
All declarations, such as [functions], [composite types], and fields, must be prefixed with an access modifier using the `access` keyword.
29
31
@@ -36,6 +38,14 @@ access(all)
36
38
fun test() {}
37
39
```
38
40
41
+
:::danger
42
+
43
+
If you prefix a function with `access(all)`, you are likely granting complete and open access for **anyone using any account to call that function.** It is critical that you properly use [entitlements] to restrict sensitive functions to the accounts that need access.
44
+
45
+
For example, if you create a vault for your users and give the `withdraw` function `access(all)`, anyone can drain that vault if they know where to find it.
46
+
47
+
:::
48
+
39
49
## Types of access control
40
50
41
51
There are five levels of access control:
@@ -46,9 +56,11 @@ There are five levels of access control:
46
56
47
57
For example, a public field in a type can be accessed on an instance of the type in an outer scope.
48
58
49
-
-**Entitled access** — the declaration is only accessible/visible to the owner of the object, or to [references] that are authorized to the required [entitlements].
59
+
-**Entitled access** — the declaration is only accessible/visible to the owner/holder of the object, or to [references] that are authorized to the required [entitlements].
50
60
51
61
A declaration is made accessible through entitlements by using the `access(E)` syntax, where `E` is a set of one or more entitlements, or a single [entitlement mapping].
62
+
63
+
An entitled field acts like an `access(all)` field ONLY if the caller actually holds the concrete resource object and not just a reference to it. In that case, an authorized reference is needed.
52
64
53
65
A reference is considered authorized to an entitlement if that entitlement appears in the `auth` portion of the reference type.
@@ -8,11 +8,148 @@ This is an opinionated list of best practices that Cadence developers should fol
8
8
9
9
Some practices listed below might overlap with advice in the [Cadence Anti-Patterns] article, which is a recommended read as well.
10
10
11
+
## Access Control
12
+
13
+
Do not use the `access(all)` modifier on fields and functions unless absolutely necessary. Prefer `access(self)`, `access(contract)`, `access(account)`, or `access(SomeEntitlement)`. Unintentionally declaring fields or functions as `access(all)` can expose vulnerabilities in your code.
14
+
15
+
When writing definitions for contracts, structs, or resources, start by declaring all your fields and functions as `access(self)`. If there is a function that needs to be accessible by external code, only declare it as `access(all)` if it is a `view` function:
16
+
17
+
```cadence
18
+
/// Simplified Bank Account implementation
19
+
access(all) resource BankAccount {
20
+
21
+
/// Fields should default to access(self) to be safe
22
+
/// and be readable through view functions
23
+
access(self) var balance: UFix64
24
+
25
+
/// It is okay to make this function access(all) because it is a view function
26
+
/// and all blockchain data is public
27
+
access(all) view fun getBalance(): UFix64 {
28
+
return self.balance
29
+
}
30
+
}
31
+
```
32
+
33
+
If there are any functions that modify state that also need to be callable from external code, use [entitlements] for the access modifiers for those functions:
34
+
35
+
```cadence
36
+
/// Simplified Vault implementation
37
+
/// Simplified Bank Account implementation
38
+
access(all) resource BankAccount {
39
+
40
+
/// Declare Entitlements for state-modifying functions
41
+
access(all) entitlement Owner
42
+
access(all) entitlement Depositor
43
+
44
+
/// Fields should default to access(self) just to be safe
45
+
access(self) var balance: UFix64
46
+
47
+
/// All non-view functions should be something other than access(all),
48
+
49
+
/// This is only callable by other functions in the type, so it is `access(self)`
50
+
access(self) fun updateBalance(_ new: UFix64) {
51
+
self.balance = new
52
+
}
53
+
54
+
/// This function is external, but should only be called by the owner
55
+
/// so we use the `Owner` entitlement
56
+
access(Owner) fun withdrawFromAccount(_ amount: UFix64): @BankAccount {
57
+
self.updateBalance(self.balance - amount)
58
+
return <-create BankAccount(balance: amount)
59
+
}
60
+
61
+
/// This is also state-modifying, so it should also be restricted with entitlements
62
+
/// In this case, we can use two entitlements to be more specific
63
+
/// about who can access (Owner OR Depositor)
64
+
access(Owner | Depositor) fun depositToAccount(_ from: @BankAccount) {
Declaring a field as [`access(all)`] only protects from replacing the field's value, but the value itself can still be mutated if it is mutable. Remember that containers, like dictionaries and arrays, are mutable and composite fields like structs and resources are still mutable through their own functions.
74
+
75
+
:::danger
76
+
77
+
This means that if you ever have a field that is a resource, struct, or capability, it should ALWAYS be `access(self)`! If it is `access(all)`, anyone could access it and call its functions, which could be a major vulnerability.
78
+
79
+
You can still allow external code to access that field, but only through functions that you have defined with `access(SomeEntitlement)`. This way, you can explicitly define how external code can access these fields.
80
+
81
+
:::
82
+
83
+
# Capabilities
84
+
85
+
## Issuing Capabilities
86
+
87
+
Don't issue and publish capabilities unless absolutely necessary. Anyone can access capabilities that are published. If public access is needed, follow the [principle of least privilege/authority]: make sure that the capability type only grants access to the fields and functions that should be exposed, and nothing else. Ideally, create a capability with a reference type that is unauthorized.
88
+
89
+
When issuing a capability, a capability of the same type might already be present. It is a good practice to check if a capability already exists with `getControllers()` before creating it. If it already exists, you can reuse it instead of issuing a new one. This prevents you from overloading your account storage and overpaying because of redundant capabilities.
90
+
91
+
```cadence
92
+
// Capability to find or issue
93
+
var flowTokenVaultCap: Capability<auth(FungibleToken.Withdraw) &FlowToken.Vault>? = nil
94
+
95
+
// Get all the capabilities that have already been issued for the desired storage path
96
+
let flowTokenVaultCaps = account.capabilities.storage.getControllers(forPath: /storage/flowTokenVault)
97
+
98
+
// Iterate through them to see if there is already one of the needed type
99
+
for cap in flowTokenVaultCaps {
100
+
if let cap = cap as? Capability<auth(FungibleToken.Withdraw) &FlowToken.Vault> {
101
+
flowTokenVaultCap = cap
102
+
break
103
+
}
104
+
}
105
+
106
+
// If no capabilities of the needed type are already present,
107
+
// issue a new one
108
+
if flowTokenVaultCap == nil {
109
+
// issue a new entitled capability to the flow token vault
When publishing a capability, a published capability might already be present. It is a good practice to check if a capability already exists with `borrow` before creating it. This function will return `nil` if the capability does not exist.
117
+
118
+
```cadence
119
+
// Check if the published capability already exists
120
+
if account.capabilities.borrow<&FlowToken.Vault>(/public/flowTokenReceiver) == nil {
121
+
// since it doesn't exist yet, we should publish a new one that we created earlier
122
+
signer.capabilities.publish(
123
+
receiverCapability,
124
+
at: /public/flowTokenReceiver
125
+
)
126
+
}
127
+
```
128
+
129
+
## Checking Capabilities
130
+
131
+
If it is necessary to handle the case where borrowing a capability might fail, the `account.check` function can be used to verify that the target exists and has a valid type:
132
+
133
+
```cadence
134
+
// check if the capability is valid
135
+
if capability.check() {
136
+
let reference = capability.borrow()
137
+
} else {
138
+
// do something else if the capability isn't valid
139
+
}
140
+
```
141
+
142
+
## Capability Access
143
+
144
+
Ensure capabilities cannot be accessed by unauthorized parties. For example, capabilities should not be accessible through a public field, including public dictionaries or arrays. Exposing a capability in such a way allows anyone to borrow it and to perform all actions that the capability allows, including `access(all)` fields and functions that aren't even in the restricted type of the capability.
145
+
11
146
## References
12
147
13
148
[References] are ephemeral values and cannot be stored. If persistence is required, store a capability and borrow it when needed.
14
149
15
-
When exposing functionality, provide the least access necessary. When creating an authorized reference, create it with only the minimal set of entitlements required to achieve the desired functionality.
150
+
When exposing functionality in an account, struct, or resource, provide the least access necessary. When creating an authorized reference with [entitlements], create it with only the minimal set of [entitlements] required to achieve the desired functionality.
151
+
152
+
# Accounts
16
153
17
154
## Account storage
18
155
@@ -28,18 +165,6 @@ Therefore, avoid passing an entitled account reference to a function, and when d
28
165
29
166
It is preferable to use capabilities over direct account storage access when exposing account data. Using capabilities allows the revocation of access and limits the access to a single value with a certain set of functionality.
30
167
31
-
## Capabilities
32
-
33
-
Don't issue and publish capabilities unless really necessary. Anyone can access capabilities that are published. If public access is needed, follow the [principle of least privilege/authority]: make sure that the capability type only grants access to the fields and functions that should be exposed, and nothing else. Ideally, create a capability with a reference type that is unauthorized.
34
-
35
-
If an entitlement is necessary to access the field or function, ensure it is only used for the particular field or function, and not also by other fields and functions. If needed, introduce a new, fine-grained entitlement.
36
-
37
-
When publishing a capability, a capability might already be present. It is a good practice to check if a capability already exists with `get` before creating it. This function will return `nil` if the capability does not exist.
38
-
39
-
If it is necessary to handle the case where borrowing a capability might fail, the `account.check` function can be used to verify that the target exists and has a valid type.
40
-
41
-
Ensure capabilities cannot be accessed by unauthorized parties. For example, capabilities should not be accessible through a public field, including public dictionaries or arrays. Exposing a capability in such a way allows anyone to borrow it and to perform all actions that the capability allows.
42
-
43
168
## Transactions
44
169
45
170
Audits of Cadence code should also include [transactions], as they may contain arbitrary code, just like in contracts. In addition, they are given full access to the accounts of the transaction's signers (i.e., the transaction is allowed to manipulate the signer's account storage, contracts, and keys).
@@ -58,14 +183,6 @@ Use [intersection types and interfaces]. Always use the most specific type possi
58
183
59
184
If given a less-specific type, cast to the more specific type that is expected. For example, when implementing the fungible token standard, a user may deposit any fungible token, so the implementation should cast to the expected concrete fungible token type.
60
185
61
-
## Access control
62
-
63
-
Declaring a field as [`access(all)`] only protects from replacing the field's value, but the value itself can still be mutated if it is mutable. Remember that containers, like dictionaries and arrays, are mutable.
64
-
65
-
Prefer non-public access to a mutable state. That state may also be nested. For example, a child may still be mutated even if its parent exposes it through a field with non-settable access.
66
-
67
-
Do not use the `access(all)` modifier on fields unless necessary. Prefer `access(self)`, or `access(contract)` and `access(account)`, when other types in the contract or account need to have access, and use entitlement-based access for other cases.
68
-
69
186
<!-- Relative links. Will not render on the page -->
70
187
71
188
[Cadence Anti-Patterns]: ./design-patterns.md
@@ -76,4 +193,5 @@ Do not use the `access(all)` modifier on fields unless necessary. Prefer `access
76
193
[transactions]: ./language/transactions.md
77
194
[principle of least privilege/authority]: https://en.wikipedia.org/wiki/Principle_of_least_privilege
78
195
[intersection types and interfaces]: ./language/types-and-type-system/intersection-types.md
Copy file name to clipboardExpand all lines: docs/tutorial/03-resources.md
+3-2Lines changed: 3 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -179,7 +179,7 @@ We've already imported the `HelloResource` contract for you and stubbed out a `t
179
179
180
180
To prepare:
181
181
182
-
1. Create a `prepare` phase with the `SaveValue` authorization [entitlement] to the user's account.
182
+
1. Create a `prepare` phase with the `SaveValue` authorization [entitlement] to the user's account. This authorizes the transaction to save values or objects anywhere in account storage. You'll learn more about entitlements in the next lesson.
183
183
1. Use `create` to create a new instance of the `HelloAsset`.
184
184
1. Save the new resource in the user's account.
185
185
1. Inside the `transaction`, stub out the `prepare` phase with the authorization [entitlement]:
@@ -213,7 +213,7 @@ Paths in the storage domain have type `StoragePath`, and paths in the public dom
213
213
214
214
Paths are **not** strings and do **not** have quotes around them.
215
215
216
-
Use the account reference with the `SaveValue` authorization [entitlement] to move the new resource into storage located in `/storage/HelloAssetTutorial`:
216
+
Next, use the account reference with the `SaveValue` authorization [entitlement] to move the new resource into storage located in `/storage/HelloAssetTutorial`:
217
217
218
218
```cadence
219
219
acct.storage.save(<-newHello, to: /storage/HelloAssetTutorial)
@@ -364,6 +364,7 @@ In real applications, you need to check the location path you are storing in to
364
364
// Existing code...
365
365
}
366
366
```
367
+
This [entitlement] makes it so you can borrow a reference to a value in the account's storage in addition to being able to save a value.
367
368
1. Add a `transaction`-level (similar to contract-level or class-level) variable to store a result `String`.
368
369
369
370
- Similar to a class-level variable in other languages, these go at the top, inside the `transaction` scope, but not inside anything else. They are accessible in both the `prepare` and `execute` statements of a transaction:
0 commit comments