Because it is easy to extract strings from an application source code or binary, secrets should not be hard-coded. This is particularly true for
applications that are distributed or that are open-source.
In the past, it has led to the following vulnerabilities:
Secrets should be stored outside of the source code in a configuration file or a management service for secrets.
This rule detects variables/fields having a name matching a list of words (secret, token, credential, auth, api[_.-]?key) being assigned a
pseudorandom hard-coded value. The pseudorandomness of the hard-coded value is based on its entropy and the probability to be human-readable. The
randomness sensibility can be adjusted if needed. Lower values will detect less random values, raising potentially more false positives.
Ask Yourself Whether
- The secret allows access to a sensitive component like a database, a file storage, an API, or a service.
- The secret is used in a production environment.
- Application re-distribution is required before updating the secret.
There would be a risk if you answered yes to any of those questions.
Recommended Secure Coding Practices
- Store the secret in a configuration file that is not pushed to the code repository.
- Use your cloud provider’s service for managing secrets.
- If a secret has been disclosed through the source code: revoke it and create a new one.
Sensitive Code Example
let secret = "47828a8dd77ee1eb9dde2d5e93cb221ce8c32b37" // Sensitive
func main() {
callMyService(secret)
}
Compliant Solution
If you are building a mobile application, it is recommended to keep API keys in a secure backend instead of hardcoding them in the mobile
application. Requests to an API can then be proxied from the mobile application through that backend.
In order to communicate with the API, you can set up an authentication flow with the backend. The resulting access key should be kept in the
Keychain:
func storeSecret(accessKey: String) throws {
let accessControl = SecAccessControlCreateWithFlags(
nil,
kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
.biometryCurrentSet,
nil)
let query: [CFString: Any] = [
kSecClass: kSecClassGenericPassword,
kSecAttrService: "com.example.app",
kSecAttrAccount: "Example account",
kSecAttrAccessControl: accessControl as Any,
kSecValueData: accessKey.data(using: .utf8)!
]
SecItemAdd(query as CFDictionary, nil)
}
If you are building a backend application instead, it is still recommended to use a secure solution to store API keys.
One such example is AWS Secrets Manager with
the AWS SDK for Swift:
import AWSSecretsManager
func main() async throws {
let secretsClient = try SecretsManagerClient()
let input = GetSecretValueInput(secretId: "MY_SERVICE_SECRET")
let response = try await secretsClient.getSecretValue(input)
if let secret = response.secretString {
callMyService(secret)
}
}
See