> For the complete documentation index, see [llms.txt](/llms.txt).

# Account management Snap security guidelines

Refer to the following security guidelines when [creating an account management Snap](/snaps/features/custom-evm-accounts/create-account-snap/).

see also

- [Custom EVM accounts](/snaps/features/custom-evm-accounts/)
- [Create an account management Snap](/snaps/features/custom-evm-accounts/create-account-snap/)
- [Create an account management companion dapp](/snaps/features/custom-evm-accounts/create-companion-dapp/)
- [Keyring API reference](/snaps/reference/keyring-api/)

## Do not add secret information to account objects[​](#do-not-add-secret-information-to-account-objects "Direct link to Do not add secret information to account objects")

Ensure that you do not store any secret information in [account objects](/snaps/reference/keyring-api/account-management/objects/#keyringaccount), since they are exposed to dapps and MetaMask. For example:

- ❌ **Do NOT do this:**  
```  
const account: KeyringAccount = {  
  id: uuid(),  
  options: {  
    privateKey: '0x01234...78', // !!! DO NOT DO THIS !!!  
  },  
  address,  
  methods: [  
    EthMethod.PersonalSign,  
    EthMethod.Sign,  
    EthMethod.SignTransaction,  
    EthMethod.SignTypedDataV1,  
    EthMethod.SignTypedDataV3,  
    EthMethod.SignTypedDataV4,  
  ],  
  type: EthAccountType.Eoa,  
}  
```
- ✅ **Do this instead:**  
_Store any secret information that you need in the Snap's state:_  
```  
await snap.request({  
  method: 'snap_manageState',  
  params: {  
    operation: 'update',  
    newState: {  
      // Your Snap's state here.  
      privateKey: '0x01234...78',  
    },  
  },  
})  
```

## Limit the methods exposed to dapps[​](#limit-the-methods-exposed-to-dapps "Direct link to Limit the methods exposed to dapps")

By default, MetaMask enforces the following restrictions on calling [Account Management API](/snaps/reference/keyring-api/account-management/) methods on your Snap based on the caller origin:

| Method                                                                                                        | MetaMask origin | Dapp origin |
| ------------------------------------------------------------------------------------------------------------- | --------------- | ----------- |
| [keyring_listAccounts](/snaps/reference/keyring-api/account-management/#keyring%5Flistaccounts)               | ✅               | ✅           |
| [keyring_getAccount](/snaps/reference/keyring-api/account-management/#keyring%5Fgetaccount)                   | ✅               | ✅           |
| [keyring_createAccount](/snaps/reference/keyring-api/account-management/#keyring%5Fcreateaccount)             | ❌               | ✅           |
| [keyring_filterAccountChains](/snaps/reference/keyring-api/account-management/#keyring%5Ffilteraccountchains) | ✅               | ✅           |
| [keyring_updateAccount](/snaps/reference/keyring-api/account-management/#keyring%5Fupdateaccount)             | ❌               | ✅           |
| [keyring_deleteAccount](/snaps/reference/keyring-api/account-management/#keyring%5Fdeleteaccount)             | ✅               | ✅           |
| [keyring_exportAccount](/snaps/reference/keyring-api/account-management/#keyring%5Fexportaccount)             | ❌               | ✅           |
| [keyring_listRequests](/snaps/reference/keyring-api/account-management/#keyring%5Flistrequests)               | ✅               | ✅           |
| [keyring_getRequest](/snaps/reference/keyring-api/account-management/#keyring%5Fgetrequest)                   | ✅               | ✅           |
| [keyring_submitRequest](/snaps/reference/keyring-api/account-management/#keyring%5Fsubmitrequest)             | ✅               | ❌           |
| [keyring_approveRequest](/snaps/reference/keyring-api/account-management/#keyring%5Fapproverequest)           | ❌               | ✅           |
| [keyring_rejectRequest](/snaps/reference/keyring-api/account-management/#keyring%5Frejectrequest)             | ✅               | ✅           |

For example, a dapp is not allowed to call the `keyring_submitRequest` method of your Snap, and MetaMask is not allowed to call the `keyring_createAccount` method of your Snap.

MetaMask also enforces that only dapps allowlisted in the Snap's manifest file using the [endowment:keyring](/snaps/reference/permissions/#endowmentkeyring) permission can call these methods.

important

We recommend further restricting the methods exposed to dapps according to your Snap's functionality. For example, if your Snap does not support account deletion via dapps, your Snap should reject calls to the `keyring_deleteAccount` method originating from dapps.

Your Snap can also impose varying restrictions depending on the calling dapp. For example, Dapp 1 may have access to a different set of methods than Dapp 2. The following is an example of implementing such logic:

```
const permissions: Record<string, string[]> = {
  'https://<Dapp 1 domain>': [
    // List of allowed methods for Dapp 1.
  ],
  'https://<Dapp 2 domain>': [
    // List of allowed methods for Dapp 2.
  ],
}

if (origin !== 'metamask' && !permissions[origin]?.includes(request.method)) {
  // Reject the request.
}

```

Both dapps must be allowlisted in the Snap's manifest file.

## Ensure the redirect URL cannot be manipulated[​](#ensure-the-redirect-url-cannot-be-manipulated "Direct link to Ensure the redirect URL cannot be manipulated")

If your Snap implements an [asynchronous transaction flow](/snaps/features/custom-evm-accounts/#asynchronous-transaction-flow), ensure that the redirect URL is valid and cannot be manipulated, otherwise the user can be redirected to a malicious website.

```
async submitRequest(request: KeyringRequest): Promise<SubmitRequestResponse> {
  // Your Snap's custom logic.
  return {
    pending: true,
    redirect: {
      message: "Please continue in the dapp.",
      url: "https://<dapp domain>/sign?tx=1234", // !!! ENSURE THIS IS A SAFE URL !!!
    },
  };
}

```

important

Only HTTPS URLs are allowed in the `url` field, and the provided URL is checked against a [list of blocked domains](https://github.com/MetaMask/eth-phishing-detect). However, for development purposes, HTTP URLs are allowed on Flask. MetaMask also requires the redirect URL to be within a site allowlisted in the Snap's manifest file.

## Remove all debug code from your production Snap[​](#remove-all-debug-code-from-your-production-snap "Direct link to Remove all debug code from your production Snap")

Ensure that all debug code is removed from your production Snap. Exposing debug code can lead to multiple security vulnerabilities. For example, secret information can be logged to the console, or a malicious dapp can bypass a security check.

## Remove sensitive information from errors[​](#remove-sensitive-information-from-errors "Direct link to Remove sensitive information from errors")

Ensure that all error messages returned by your Snap are sanitized. Failing to sanitize error messages can lead to exposing secrets or other sensitive information to dapps or to MetaMask.

For example:

- ❌ **Do NOT do this:**  
```  
// If inputSecretValue contains invalid hexadecimal characters, its value  
// will be added to the error thrown by toBuffer.  
const privateKey = toBuffer(inputSecretValue)  
// Use privateKey here.  
```
- ✅ **Do this instead:**  
```  
try {  
  const privateKey = toBuffer(inputSecretValue)  
  // Use privateKey here.  
} catch (error) {  
  throw new Error('Invalid private key')  
}  
```

## Expose Account Management API methods using the `onKeyringRequest` entry point[​](#expose-account-management-api-methods-using-the-onkeyringrequest-entry-point "Direct link to expose-account-management-api-methods-using-the-onkeyringrequest-entry-point")

The [onRpcRequest](/snaps/reference/entry-points/#onrpcrequest) entry point is a general-purpose entry point and has no restrictions on the methods that can be called. Ensure that you only expose Account Management API methods using the [onKeyringRequest](/snaps/reference/entry-points/#onkeyringrequest) entry point, which has extra security checks.

For example:

- ❌ **Do NOT do this:**  
```  
export const onRpcRequest: OnRpcRequestHandler = async ({  
  //           ~~~           ~~~  
  origin,  
  request,  
}) => {  
  return handleKeyringRequest(keyring, request)  
}  
```
- ✅ **Do this instead:**  
```  
export const onKeyringRequest: OnKeyringRequestHandler = async ({  
  //           ~~~~~~~           ~~~~~~~  
  origin,  
  request,  
}) => {  
  // Any custom logic or extra security checks here.  
  return handleKeyringRequest(keyring, request)  
}  
```

## Do not fetch remote code from inside your Snap[​](#do-not-fetch-remote-code-from-inside-your-snap "Direct link to Do not fetch remote code from inside your Snap")

Ensure that your Snap is self-contained and does not fetch code from external sources. Otherwise, a compromised server can use this mechanism to inject malicious code into your Snap.
