Security is not a feature that is "added at the end": it is a cross-cutting property that must be present from the very first line of the design. This is what security by design means. A system that works, scales, and is always available is worthless if an attacker can steal users' data or impersonate their identity. In this lesson you will learn the fundamental principles of security (defense in depth and least privilege), the essential difference between authentication and authorization, the modern protocols that implement them (OAuth2, OIDC, and JWT), the most frequent vulnerabilities according to OWASP, and finally how to protect data through encryption in transit and at rest.
Professional note: the content of this lesson is educational about architecture. Any real security implementation in a production system must be reviewed by security specialists and comply with applicable regulations (for example GDPR/LOPDGDD).
Contents
- Principles: defense in depth and least privilege
- Authentication vs. Authorization
- OAuth2, OpenID Connect, and JWT
- OWASP Top 10 summarized
- Encryption in transit and at rest
- Common mistakes and tips
- Exercises
- Principles: defense in depth and least privilege
Two principles underpin any secure architecture.
Defense in depth consists of not relying on a single barrier, but placing multiple independent layers of security. If one fails, the others keep protecting. It is the principle of the medieval castle: moat, wall, gates, towers.
graph LR
A[Internet] --> F[Firewall / WAF]
F --> G[API Gateway + Auth]
G --> S[Validation in the service]
S --> D[(DB with minimal permissions)]Each layer of the diagram protects independently: even if an attacker gets through the firewall, they still have to get past the gateway's authentication, the service's validation, and the database's permissions.
Least privilege establishes that each user, service, or process should have only the permissions strictly necessary for its function, not one more. A service account that only reads reports must not be able to drop tables.
-- BAD: the application uses an all-powerful user GRANT ALL PRIVILEGES ON *.* TO 'app'@'%'; -- GOOD: only the permissions it really needs, on the specific database GRANT SELECT, INSERT, UPDATE ON store.* TO 'app'@'10.0.%';
In the "BAD" example, if the application is compromised, the attacker has total control of all databases. In the "GOOD" one, they can only read from and write to store, cannot even delete (DELETE), and only from the internal network 10.0.%. The potential damage is contained.
- Authentication vs. Authorization
These two concepts are constantly confused, but they are distinct:
| Authentication (AuthN) | Authorization (AuthZ) | |
|---|---|---|
| Question | Who are you? | What can you do? |
| Verifies | Identity | Permissions |
| Occurs | First | After (already identified) |
| Example | Log in with username and password | "Can this user delete invoices?" |
| Typical result | An identity token | Allow or deny the operation |
The mnemonic: AuthN = authenticNation (who you are); AuthZ = authoriZation (what you can do). You always authenticate first and authorize afterward.
The most common authorization models are:
- RBAC (Role-Based Access Control): permissions are assigned to roles (admin, editor, reader) and users are given roles. Simple and very widespread.
- ABAC (Attribute-Based Access Control): the decision is based on attributes (department, time, location, seniority). More flexible and granular, but more complex.
- OAuth2, OpenID Connect, and JWT
These three terms are often mixed up. The key to not getting confused:
| Technology | What it is for | Answers |
|---|---|---|
| OAuth 2.0 | Delegated authorization (granting access to resources) | What can this app do on your behalf? |
| OpenID Connect (OIDC) | Authentication (layer on top of OAuth2) | Who is the user? |
| JWT | Token format (carries the info) | (Not a protocol, it's a container) |
OAuth 2.0 allows an application to access resources on the user's behalf without knowing their password (for example, "Log in with Google"). OIDC is built on top of OAuth2 and adds the piece it was missing: identifying the user, by means of an ID Token. JWT (JSON Web Token) is the most common format in which these tokens travel.
A JWT has three parts separated by dots: header.payload.signature.
// Header: algorithm and type
{ "alg": "RS256", "typ": "JWT" }
// Payload (claims): the information, NOT encrypted, only Base64-encoded
{
"sub": "user-123", // subject: user identifier
"name": "Ana García",
"roles": ["editor"], // used for authorization
"iat": 1718000000, // issued at: when it was issued
"exp": 1718003600 // expiration: when it expires (1 hour later)
}A critical point that confuses beginners: the payload of a JWT is not encrypted, only Base64-encoded. Anyone can read it. What the signature (the third part) guarantees is integrity and authenticity: that no one has modified the content and that it was issued by whoever it claims to be. That is why you must never put secrets in a JWT.
// Validate a JWT signed with RS256 (public/private key)
Claims claims = Jwts.parserBuilder()
.setSigningKey(publicKey) // We verify the signature with the issuer's public key
.build()
.parseClaimsJws(receivedToken) // Throws an exception if the signature or expiration is invalid
.getBody();
String userId = claims.getSubject(); // We extract the "sub"
List<String> roles = claims.get("roles", List.class); // and the roles to authorizeThis code verifies that the token has not been tampered with (thanks to the signature) or expired (exp), and only then extracts the identifier and the roles. If validation fails, it throws an exception and the request is rejected.
- OWASP Top 10 summarized
OWASP (Open Worldwide Application Security Project) periodically publishes the Top 10 most critical web security risks. It is required reading. Summary of the main categories:
| # | Category | What it consists of | Key mitigation |
|---|---|---|---|
| A01 | Broken access control | Accessing resources without permission | Server-side authorization, deny by default |
| A02 | Cryptographic failures | Sensitive data poorly protected | Encrypt in transit and at rest, don't invent cryptography |
| A03 | Injection (SQL, etc.) | Malicious data treated as code | Parameterized queries, validate inputs |
| A04 | Insecure design | Design flaws, not code flaws | Threat modeling from the design stage |
| A05 | Security misconfiguration | Default values, exposed errors | Hardening, review configuration |
| A06 | Vulnerable components | Outdated libraries with CVEs | Update, scan dependencies |
| A07 | Authentication failures | Weak sessions, brute force | MFA, attempt limits, secure session management |
| A08 | Integrity failures | Unverified data/code | Signatures, verify the supply chain |
| A09 | Logging/monitoring failures | Not detecting attacks | Security logs, alerts, observability |
| A10 | SSRF | Forcing the server to request internal URLs | Validate and restrict request destinations |
The classic example is SQL injection (A03):
// BAD: concatenating the user's input directly into the query
String sql = "SELECT * FROM users WHERE email = '" + email + "'";
// If email = "' OR '1'='1", the query returns ALL users.
// GOOD: parameterized query; the engine treats "email" as data, never as code
PreparedStatement ps = conn.prepareStatement(
"SELECT * FROM users WHERE email = ?");
ps.setString(1, email); // The driver escapes the value safelyIn the "BAD" version, an attacker can inject SQL into the email field and manipulate the query. In the "GOOD" one, the ? is a placeholder: the engine never interprets the user's value as SQL instructions, eliminating the vulnerability at its root.
- Encryption in transit and at rest
Data must be protected in its two states:
- In transit: while it travels over the network. It is protected with TLS (the padlock of HTTPS). It prevents someone who intercepts the traffic from reading it (man in the middle).
- At rest: while it is stored on disk, in a database, or in backups. It is protected by encrypting the storage or specific columns.
| In transit | At rest | |
|---|---|---|
| Threat | Interception on the network | Theft of the disk or backup |
| Technology | TLS / HTTPS | Disk/column encryption (AES) |
| Example | https:// connection |
Database with transparent encryption (TDE) |
# Force HTTPS and redirect all HTTP traffic in Nginx
server {
listen 80;
return 301 https://$host$request_uri; # Any HTTP request is redirected to HTTPS
}
server {
listen 443 ssl;
ssl_protocols TLSv1.2 TLSv1.3; # Only modern, secure versions of TLS
ssl_certificate /etc/ssl/cert.pem;
ssl_certificate_key /etc/ssl/key.pem;
}This snippet ensures that content is never served over unencrypted HTTP: the first block redirects (code 301) all port 80 traffic to HTTPS. The second configures TLS allowing only versions 1.2 and 1.3 (the older ones have known vulnerabilities).
An essential rule about passwords: they are never encrypted or stored in plaintext; slow, salted hash functions are applied (bcrypt, scrypt, Argon2). This way, even if the database is stolen, the original passwords cannot be recovered.
Common Mistakes and Tips
- Trusting only the client. Validation and authorization in the browser or mobile app can be bypassed trivially. Everything is validated and authorized on the server.
- Believing the JWT is encrypted. It is not. Do not put sensitive data in the payload; anyone can read it.
- Inventing your own cryptography. It almost always ends badly. Use standard libraries and protocols, reviewed and maintained.
- Storing passwords with MD5/SHA or in cleartext. Use hash functions designed for passwords (bcrypt, Argon2) with salt.
- Error messages that are too detailed. Saying "the user does not exist" versus "incorrect password" helps the attacker. Respond generically.
- Not updating dependencies. Outdated libraries (A06) are one of the most frequent attack vectors. Scan and update.
- Tip: think like an attacker. At every data input ask yourself "what happens if this is malicious?".
Exercises
-
AuthN vs. AuthZ. A user logs in correctly but, when trying to access the administration panel, receives a 403 (Forbidden) error. Did authentication or authorization fail? Justify it.
-
Spot the vulnerability. Review this code and state which OWASP Top 10 risk it contains and how you would fix it:
String query = "SELECT * FROM products WHERE name = '" + search + "'"; statement.executeQuery(query); -
Encryption decision. A messaging application sends messages between users and stores them in its database. What types of encryption does it need and why each one?
Solutions
-
Authorization (AuthZ) failed, not authentication. The 403 (Forbidden) error means the system knows who the user is (they authenticated fine, it is not a 401), but that user does not have permissions to access the administration panel. If authentication had failed, the code would be 401 (Unauthorized/not identified).
-
It is SQL injection (A03): the
searchvariable is concatenated directly into the query, allowing an attacker to manipulate the SQL. Fix with a parameterized query:PreparedStatement ps = conn.prepareStatement( "SELECT * FROM products WHERE name = ?"); ps.setString(1, search); ps.executeQuery();The
?prevents the user's value from being interpreted as SQL code. -
It needs both. Encryption in transit (TLS/HTTPS) so no one can intercept the messages while they travel over the network between the device and the server. And encryption at rest to protect the messages stored in the database against theft of the disk or a backup. (The most demanding apps add end-to-end encryption, where not even the server itself can read them.)
Conclusion
You have learned that security is designed from the start through layers (defense in depth) and minimal permissions, that authenticating (who you are) is distinct from authorizing (what you can do), that OAuth2/OIDC/JWT are the standard pieces of modern identity, that the OWASP Top 10 catalogs the risks you must know, and that data is encrypted both in transit and at rest. Security is never a finished product: it is a continuous process of prevention, detection, and improvement.
And to detect both attacks and performance problems, we need to see what is happening inside the system. In the next lesson, Observability: Logging, Metrics, and Tracing, we will learn to instrument the application to understand its behavior in production.
Application Architecture Course
Module 1: Fundamentals of Application Architecture
- What Is Application Architecture?
- The Role of the Software Architect
- Quality Attributes and Non-Functional Requirements
- Architectural Decisions and Trade-offs
- Architecture Documentation: Views and the C4 Model
Module 2: Design Principles and Tactics
- Coupling, Cohesion and Separation of Concerns
- SOLID Principles Applied to Architecture
- DRY, KISS, YAGNI and Other Design Principles
- Architectural Tactics for Quality Attributes
- Managing Technical Debt
Module 3: Architectural Styles and Patterns
- Monolithic Architecture
- Layered Architecture (N-Tier)
- Client-Server Architecture
- Hexagonal Architecture (Ports and Adapters)
- Clean and Onion Architecture
Module 4: Distributed Architectures and Microservices
- Introduction to Distributed Systems
- Microservices Architecture
- Service Decomposition and Bounded Contexts
- API Gateway, Service Discovery and Inter-Service Communication
- Resilience Patterns: Circuit Breaker, Retry and Bulkhead
- The CAP Theorem and Data Consistency
Module 5: Event-Driven Architectures and Messaging
- Fundamentals of Event-Driven Architecture
- Asynchronous Messaging: Queues and Brokers
- Event Patterns: Event Sourcing and CQRS
- Managing Distributed Transactions: The Saga Pattern
- Real-Time Data Streaming
Module 6: Domain-Driven Design (DDD)
- Core DDD Concepts
- Strategic Design: Bounded Contexts and Ubiquitous Language
- Tactical Design: Entities, Aggregates and Repositories
- Context Mapping
Module 7: Data and Persistence
- Persistence Strategies: SQL vs NoSQL
- Data Access Patterns: Repository, Unit of Work and DAO
- Database per Service and Distributed Data Management
- Caching and Invalidation Strategies
Module 8: Cloud Architecture and Deployment
- Cloud Computing Fundamentals (IaaS, PaaS, SaaS)
- Containers and Orchestration with Docker and Kubernetes
- Serverless Architecture
- Cloud-Native Design Patterns
- Infrastructure as Code (IaC)
Module 9: Quality, Security and Observability
- Scalability: Horizontal vs Vertical and Load Balancing
- High Availability and Fault Tolerance
- Security by Design and Authentication/Authorization
- Observability: Logging, Metrics and Tracing
- Performance and Load Testing
