# Secure Design Principles — DevSecOps

Source: https://www.skillbyai.com/en/devsecops/dso-secure-design

> Apply least privilege, defence in depth, secure defaults and fail-closed behaviour when designing services.

## Principles that survive every framework

Tools change every few years; design principles do not. **Least privilege**: every user, service and CI job gets only the permissions it needs, for only as long as it needs them. A reporting service needs read access to one table, not admin on the database. **Defence in depth**: assume any single control will fail, so layer them: input validation, parameterised queries, a low-privilege database user and network rules together. **Secure defaults**: the safe option should be the one you get without doing anything, such as private buckets and TLS on by default. **Fail closed**: if the authorisation service is unreachable, deny the request instead of allowing it. **Minimise attack surface**: remove unused endpoints, ports, packages and features, because code that does not exist cannot be exploited. **Separation of duties**: the person who writes a change should not be the only one who approves and deploys it.

## A bank locker room

A bank does not rely on one lock. There is a guard, a register, a locked strong room, and your locker needs both your key and the bank's key. That is defence in depth plus separation of duties.

## Fail closed in practice

If the permission check itself errors, the function denies access instead of silently allowing it.

```python
def can_delete(user, check_permission):
    try:
        return check_permission(user, "invoice:delete")
    except Exception:
        return False  # fail closed: errors never grant access

def flaky_check(user, perm):
    raise TimeoutError("authz service unreachable")

print(can_delete("asha", flaky_check))
print(can_delete("asha", lambda u, p: u == "asha"))
```

Output:

```
False
True
```

## Interview trap

'Fail safe' is ambiguous. For a door lock in a fire it may mean 'open'; for access control it should mean **deny**. Say *fail closed* when you mean deny.

**Quiz:** A CI job only needs to push images to one registry repository. What does least privilege suggest?

- [ ] Give it organisation-wide admin so it never fails
- [ ] Share a developer's personal token with it
- [x] Grant push rights on that single repository, ideally with short-lived credentials
- [ ] Disable authentication on the registry for CI

*Answer:* Grant push rights on that single repository, ideally with short-lived credentials. Least privilege limits both scope (one repository, push only) and time (short-lived credentials).
