65 lines
2.2 KiB
Markdown
65 lines
2.2 KiB
Markdown
|
|
# Security — <Project>
|
||
|
|
|
||
|
|
```
|
||
|
|
Status: Current
|
||
|
|
Owner: <who maintains this>
|
||
|
|
Last reviewed: <YYYY-MM-DD>
|
||
|
|
Governs: authentication, secret handling, data at rest and in transit
|
||
|
|
Review trigger: Any new secret, any new external service, any change to auth or storage
|
||
|
|
```
|
||
|
|
|
||
|
|
## What this protects, and from whom
|
||
|
|
|
||
|
|
<Name the assets and the adversary. "Security" with no threat model produces
|
||
|
|
effort spent in the wrong places — a hobby project's real adversaries are a
|
||
|
|
leaked repository and a stolen laptop, not a nation state, and the controls
|
||
|
|
differ.>
|
||
|
|
|
||
|
|
| Asset | Where it lives | What would it cost to lose |
|
||
|
|
| --- | --- | --- |
|
||
|
|
| <> | <> | <> |
|
||
|
|
|
||
|
|
## Secrets
|
||
|
|
|
||
|
|
- **Nothing secret is committed.** Not in source, not in config, not in a test
|
||
|
|
fixture, not in a screenshot.
|
||
|
|
- Secrets arrive from the environment or a secret store, and are read at the
|
||
|
|
boundary rather than passed around.
|
||
|
|
- A key stored beside the data it protects protects nothing. If data is
|
||
|
|
encrypted at rest, say here where the key lives and how it is rotated.
|
||
|
|
|
||
|
|
**Check before every release:** no private keys, keystores, `.env` files,
|
||
|
|
service-account JSON, or real credentials in the tracked tree — including in the
|
||
|
|
history, which a `git log -p` search covers and a directory listing does not.
|
||
|
|
|
||
|
|
## Authentication and authorisation
|
||
|
|
|
||
|
|
<Who can do what, and where that is enforced. If the answer is "in the UI", it
|
||
|
|
is not enforced — say so and fix it.>
|
||
|
|
|
||
|
|
## Data in transit
|
||
|
|
|
||
|
|
<TLS everywhere it applies. Certificate pinning if it applies. What is sent to
|
||
|
|
third parties, and what is deliberately not.>
|
||
|
|
|
||
|
|
## Third parties
|
||
|
|
|
||
|
|
| Service | What it receives | Why that is acceptable |
|
||
|
|
| --- | --- | --- |
|
||
|
|
| <> | <> | <> |
|
||
|
|
|
||
|
|
Every row is a decision to send someone else's data somewhere. An empty table is
|
||
|
|
a good table.
|
||
|
|
|
||
|
|
## Deliberately out of scope
|
||
|
|
|
||
|
|
<What is not defended against, and why that is a reasonable call. Writing it
|
||
|
|
down converts an unknown gap into a known one, which is the whole difference.>
|
||
|
|
|
||
|
|
## Logging
|
||
|
|
|
||
|
|
Logs are read by people and shipped to places. Nothing sensitive goes into one —
|
||
|
|
not a token, not a credential in a URL, not the contents of a user's record. An
|
||
|
|
error's *name* is almost always enough; its message, from a failed HTTP request,
|
||
|
|
is often the request URL.
|