- Always code with a Least Privilege Philosophy
- When writing code and configuring services, set them up to run with the fewest privileges possible.
- Firewalls:
- Open the fewest ports possible to allow the application to run
- Firewall rules should accept connections from the fewest IP addresses as possible
- Services should use internal IPs for server-to-server communication and not route over the public Internet
- Databases:
- If app has its own db that other apps do not access directly
- Have an app only db un/pw
- Otherwise, grant your component’s role the specific SELECT/UPDATE/DELETE permissions on only the specific tables your component needs
- Your component does not need the DBA role, or the ability to create new users, etc.
- Only grant EXECUTE on functions/stored-procedures your component needs
- Databases never need to be exposed to the Internet
- API Keys:
- If your code consumes a 3rd-party API key, configure that key with the fewest permissions needed for your code to do what it needs to do
- API keys should be programmatically accessed securely and not stored in code files themselves
- API key backups should be stored securely either offline or in a 1Password vault
- Never create a single shared set of credentials that will be used by many things/people
- Always create new database/API/service accounts for your new component
- Don’t reuse another component’s database credentials
- Name the credentials after your component (i.e. anycoin-transaction-builder)
- Always triage databases with your own credentials
- Your database credentials grant you many more permissions than your application’s credentials
- Query and update the database using your own credentials while leaving your application’s credentials locked down
- Implement security at all layers
- Don’t assume that because this is running on a private network it doesn’t need security.
- Don’t assume that because that thing is doing a security check that you don’t have to.
- Always code defensively, assuming that what you’ve received is malformed
- Is the variable null? Is it the right datatype? Is it within bounds of expected data?
- If it isn’t, write an error message to the log to help others debug this issue!
- Always assume user-entered data is malicious until you’ve validated it
- Reuse code where possible
- It’s possible another dev has already written code for a common task
- ShapeShift’s common libraries can help
- If one piece of your code seems like a common task, consider putting it into one of ShapeShift’s common libraries instead for a future dev
- Write code that is easy to troubleshoot in production should something not work as intended.
- One possibility: Avoid empty conditional paths. Throw exceptions or write logs on all “sad paths”
- Bad:if (some_condition) then do_something()// If some_condition fails, nothing will happen and you won’t know why!
- Good:if (some_condition) then do_something()else throw new exception(“Some condition wasn’t met!”)// Now if that assumption fails, the code will bark and allow you to debug why
- Make your logs useful, available, and secure
- Include enough information in the messages so that if something goes wrong, the logs will help you debug and track the root cause
- Always make sure your application writes logs into Datadog
- Always make sure your logs do NOT record sensitive information. Think about what your exception stack trace will show when caught upstream!!
- Use standardized logging where applicable
- Always assume the service your code consumes will go down or be unavailable
- Services, databases, 3rd-party APIs, blockchain nodes, and other associated resources should be designed with a load balanced scaling mindset
- DNS Hostnames should be referenced as opposed to IP addresses
- Use queues so that when the service comes back up your code can pick up where it was disrupted
- Always encrypt secrets
- API keys, database credentials, and other secrets should be stored encrypted and be decrypted before use according to the encryption standards listed in the Information Security Policy
- PII should be encrypted to the Membership Decryption GPG key with fingerprint AD6D 4C30 0F40 A637 8161 875B 4C73 13BB AE3E 7FAF
- Always use existing authentication schemes to authenticate our foxes
- Can you authenticate them with a Yubikey tap?
- Can you authenticate them with a GPG key?
- Can you hook into OneLogin and authenticate them with our SSO?
- Do not rely on email addresses ending in @shapeshift.com to confirm someone is an employee
- Do not rely on username/password combinations
- Never create production private keys yourself
- All production keys must be generated by Security and provided to the SREs upon deployment
- If you have concerns with how to do something securely, ask your peers, communities of practice, security, SREs, etc.
- Places to be sure you ask if you aren’t sure
- Auditing AWS Resources
- Generating key material securely
- Storing key material securely
- Wallet architecture and key management
- Access and Authorization Implementations