Security

Security

Basic concepts

  • Confidentiality
  • Integrity
  • Availability
  • Non-repudiation
  • Identification & Authentication
  • Authorization (Access Control)

Security

Confidentiality

  • Only authorized parties can access the information

It can be achieved by:

  • Encryption (TLS, VPN, ...)
  • Access Control (Identification, Authentication, Authorization)
  • Anonymization (GDPR)

Security

Integrity

  • The information is not altered

It can be achieved by:

  • Hashing
  • Digital Signature

Security

Availability

  • The information is available when needed

It can be achieved by:

  • Redundancy
  • Load balancing
  • Disaster recovery plan
  • Backup
  • ...

Security

Non-repudiation

  • The information cannot be denied

It can be achieved by:

  • Digital Signature
  • Timestamping
  • ...

Security

Identification & Authentication

  • Identification: Who are you?
  • Authentication: Prove it!

It can be achieved by:

  • Login / Password
  • API Key
  • JWT Token
  • 2FA
  • Biometrics
  • ...

Security

Authorization (Access Control)

  • What are you allowed to do?
  • What are you allowed to access?
  • What are you allowed to modify?
  • What are you allowed to delete?
  • ...

It can be achieved by:

  • Role-based access control (RBAC)
  • Attribute-based access control (ABAC)
  • ...

Security

101

Don't implement security yourself if there are opensource libraries and services maintained by security experts.
(Unless your REALLY know what you are doing)
When in doubt, read the RFCs or NIST documentation.

Security

Identity & Access Management (IAM)

At scale, we often use a dedicated service to manage users, roles, permissions, ...

  • Centralized management of users, roles, permissions, ...
  • Single Sign-On (SSO)
  • OAuth2
  • OpenID Connect (OIDC)
  • SAML
  • ...

Security

OAuth2 - What is it?

OAuth 2.0 is an authorization framework (not authentication!)

Purpose: Allow applications to access resources on behalf of a user without sharing credentials.

Example: A mobile app accessing your Google Drive without knowing your Google password.

Specification: RFC 6749

Security

OAuth2 - Key Actors

  • Resource Owner: The user who owns the data
  • Client: The application requesting access (your app)
  • Authorization Server: Issues tokens (Keycloak)
  • Resource Server: The API protecting resources (your API)

Security

OAuth2 - Token Types

Access Token:

  • Short-lived credential to access protected resources
  • Sent with every API request
  • Usually expires in minutes/hours

Refresh Token:

  • Long-lived credential to get new access tokens
  • Never sent to the API
  • Optional but recommended

Security

OAuth2 - Authorization Code Flow

Most secure flow for web/mobile apps:

  1. User → Client: "I want to login"
  2. Client → Authorization Server: Redirect user to login page
  3. User → Authorization Server: Enters credentials
  4. Authorization Server → Client: Returns authorization code
  5. Client → Authorization Server: Exchanges code for tokens
  6. Authorization Server → Client: Returns access token (+ refresh token)
  7. Client → Resource Server: API calls with access token

Security

OpenID Connect (OIDC)

OIDC = OAuth2 + Authentication

OAuth2 only handles authorization (what you can access)
OIDC adds authentication (who you are)

What OIDC adds:

  • ID Token: Contains user identity information (name, email, etc.)
  • UserInfo endpoint: Get additional user profile data
  • Standard scopes: openid, profile, email

Specification: OpenID Connect Core 1.0

Security

JWT (JSON Web Token)

Format: header.payload.signature

Example:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NSIsIm5hbWUiOiJKb2huIn0.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Content (after decoding):

{
  "sub": "12345",
  "name": "John Doe",
  "email": "john@example.com",
  "exp": 1735689600,
  "iat": 1735686000
}

Specification: RFC 7519

Security

JWT - How it works

  1. Header: Algorithm & token type

    {"alg": "RS256", "typ": "JWT"}
    
  2. Payload: Claims (user data, expiration, etc.)

    {"sub": "user123", "exp": 1735689600}
    
  3. Signature: Ensures integrity

    HMACSHA256(base64(header) + "." + base64(payload), secret)
    

Your API verifies the signature to trust the token!

Security

Threats

Common Security Threats (STRIDE Model):

  • Spoofing: Impersonating someone else
  • Tampering: Modifying data without authorization
  • Repudiation: Denying actions performed
  • Information disclosure: Exposing sensitive data
  • Denial of service: Making systems unavailable
  • Elevation of privilege: Gaining unauthorized access

Understanding threats helps us protect against them!

Security

OWASP API Security Top 10

OWASP (Open Web Application Security Project) publishes the top security risks for APIs.

Latest: API Security Top 10 (2023)

Understanding these risks helps you build secure APIs!

Security

OWASP API Top 10 - Overview

  1. Broken Object Level Authorization (BOLA)
  2. Broken Authentication
  3. Broken Object Property Level Authorization
  4. Unrestricted Resource Consumption
  5. Broken Function Level Authorization (BFLA)
  6. Unrestricted Access to Sensitive Business Flows
  7. Server Side Request Forgery (SSRF)
  8. Security Misconfiguration
  9. Improper Inventory Management
  10. Unsafe Consumption of APIs

Security

#1 Broken Object Level Authorization (BOLA)

The Problem: Users can access objects they shouldn't own.

Example:

GET /api/appointments/123
Authorization: Bearer <token-for-user-456>

❌ API returns appointment 123 even though it belongs to user 789!

Prevention:

  • Always check: "Does this user OWN this resource?"
  • Don't rely on IDs being secret
  • Implement ownership checks in every endpoint

Security

BOLA Prevention - Code Example

❌ Vulnerable:

app.get('/api/appointments/:id', authenticate, async (req, res) => {
  const appointment = await db.getAppointment(req.params.id);
  res.json(appointment); // ❌ No ownership check!
});

✅ Secure:

app.get('/api/appointments/:id', authenticate, async (req, res) => {
  const appointment = await db.getAppointment(req.params.id);
  if (appointment.userId !== req.user.id) {
    return res.status(403).json({ error: 'Forbidden' });
  }
  res.json(appointment);
});

Security

#2 Broken Authentication

The Problem: Weak authentication mechanisms.

Common Issues:

  • Weak password policies
  • No rate limiting on login endpoints
  • Credentials in URLs (/api/login?password=123)
  • Tokens don't expire
  • No token revocation

Prevention:

  • Use established standards (OAuth2, OIDC)
  • Implement rate limiting
  • Short-lived tokens + refresh tokens
  • Strong password policies

Security

#3 Broken Object Property Level Authorization

The Problem: Users can read/write fields they shouldn't.

Example - Mass Assignment:

// User sends: {"name": "John", "isAdmin": true}
app.patch('/api/users/:id', async (req, res) => {
  await db.updateUser(req.params.id, req.body); // ❌
});

User just made themselves admin!

Prevention:

  • Whitelist allowed fields
  • Separate DTOs for input/output
  • Don't expose sensitive fields (password hashes, internal IDs)

Security

#4 Unrestricted Resource Consumption

The Problem: No limits on API usage → DoS attacks.

Common Issues:

  • No pagination limits (GET /api/users returns 1M users)
  • No rate limiting (1000 requests/second)
  • File upload size unlimited
  • Expensive operations without throttling

Prevention:

  • Implement rate limiting (express-rate-limit)
  • Pagination with max limits (max 100 items per page)
  • Upload size limits
  • Request timeouts

Security

#5 Broken Function Level Authorization (BFLA)

The Problem: Regular users can access admin functions.

Example:

DELETE /api/users/123
Authorization: Bearer <regular-user-token>

❌ Regular user deletes another user!

Prevention:

  • Check user roles/permissions for every endpoint
  • Use middleware for role-based access control
  • Principle of least privilege
  • Don't rely on hiding buttons in frontend

Security

BFLA Prevention - Code Example

✅ With RBAC:

const requireRole = (role) => (req, res, next) => {
  if (!req.user.roles.includes(role)) {
    return res.status(403).json({ error: 'Forbidden' });
  }
  next();
};

app.delete('/api/users/:id',
  authenticate,
  requireRole('admin'),  // ✅ Check role
  async (req, res) => {
    await db.deleteUser(req.params.id);
    res.json({ success: true });
  }
);

Security

#8 Security Misconfiguration

The Problem: Insecure default configurations.

Common Issues:

  • Default credentials (admin/admin)
  • Detailed error messages in production
  • CORS misconfigured (Access-Control-Allow-Origin: *)
  • Missing security headers
  • Debug mode enabled in production

Prevention:

  • Change default credentials
  • Generic error messages in production
  • Proper CORS configuration
  • Security headers (Helmet.js)
  • Disable debug mode

Security

API Security Best Practices - Summary

✅ Always authenticate requests
✅ Always authorize actions (BOLA, BFLA checks)
✅ Validate all inputs (never trust user data)
✅ Use HTTPS everywhere
✅ Rate limiting on all endpoints
✅ Pagination with max limits
✅ Whitelist allowed fields (no mass assignment)
✅ Security headers (HSTS, CSP, X-Frame-Options)
✅ Log security events (failed logins, permission denials)
✅ Keep dependencies updated (npm audit)

Security

Session

  • Generate Safe Session Identifiers
  • Don't Expose Session Identifiers
  • Protect Your Cookies with their attributes: Domain, Path, HttpOnly, and Secure.

Security

Authorization

  • Always check the user's permissions on the backend. (Don't rely on the frontend to hide buttons)
  • Deny by Default. Principle of least privilege.
  • Authorize Actions on Resources. (Don't just check if the user is authenticated)

Security

RBAC (Role-Based Access Control)

  • Assign roles to users
  • Assign permissions to roles

Security

An example: Keycloak

  • Open Source Identity and Access Management
  • OAuth2 & OpenID Connect (OIDC)
  • SAML
  • LDAP
  • ...

Security

An example: Keycloak

To deploy Keyloak locally, we can use Docker:

docker run -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak:22.0.5 start-dev

Follow the Keycloak getting started: https://www.keycloak.org/getting-started/getting-started-docker and create:

  • a realm
  • a client with authentication method "confidential"
  • a user

Security

Keycloak - Key Concepts

Realm: Isolated space for managing users, clients, roles

  • Think of it as a tenant or workspace
  • Example: practicemanager-realm

Client: Your application that wants to authenticate users

  • Can be a frontend app, backend API, or both
  • Example: practicemanager-api

User: People who authenticate

Role: Permission labels assigned to users

  • Example: admin, doctor, patient

Security

Keycloak - Creating a Realm

  1. Access Keycloak admin console: http://localhost:8080
  2. Login with admin credentials (admin/admin)
  3. Click dropdown in top-left corner
  4. Click "Create Realm"
  5. Realm name: practicemanager
  6. Click "Create"

Best Practice: Use separate realms for dev, staging, production

Security

Keycloak - Client Types

Public Client:

  • Cannot keep secrets (e.g., frontend SPA in browser)
  • No client secret
  • Less secure
  • Example: React/Angular app

Confidential Client:

  • Can keep secrets (e.g., backend server)
  • Has a client secret
  • More secure
  • Example: Node.js API, Java backend

For API security: Use confidential client

Security

Keycloak - Creating a Client

  1. In your realm, go to "Clients""Create client"
  2. Client ID: practicemanager-api
  3. Name: PracticeManager API
  4. Click "Next"
  5. Client authentication: ON (makes it confidential)
  6. Authentication flow: Enable "Standard flow"
  7. Click "Next"
  8. Valid redirect URIs: http://localhost:3000/*
  9. Web origins: http://localhost:3000
  10. Click "Save"

Security

Keycloak - Client Settings Important!

After creating the client, note:

Client ID: Used to identify your app
Client Secret: Found in "Credentials" tab

  • ⚠️ Keep this secret! Never commit to Git
  • Store in environment variables

Valid Redirect URIs: Where Keycloak can redirect after login
Web Origins: CORS configuration

Security

Keycloak - Creating Users

  1. Go to "Users""Add user"
  2. Fill in:
    • Username: drsmith
    • Email: drsmith@example.com
    • First/Last name: Dr. John Smith
  3. Click "Create"
  4. Go to "Credentials" tab
  5. Click "Set password"
  6. Enter password, turn off "Temporary"
  7. Click "Save"

Security

Keycloak - OAuth2/OIDC Endpoints

Keycloak exposes standard endpoints:

Discovery endpoint (all endpoints listed here):

http://localhost:8080/realms/practicemanager/.well-known/openid-configuration

Authorization endpoint: Where users login
Token endpoint: Exchange code for tokens
Userinfo endpoint: Get user profile
Logout endpoint: End session

Your app will use these to implement OAuth2/OIDC flow!

Security

┌─────────────┐         ┌──────────────┐
│   Browser   │────────▶│   Keycloak   │
│  (Frontend) │◀────────│ (Auth Server)│
└─────────────┘         └──────────────┘
      │                        │
      │                   Issues JWT
      │                        │
      ▼                        ▼
┌─────────────┐         ┌──────────────┐
│  Your API   │◀────────│  Validates   │
│  (Backend)  │  JWT    │  JWT Token   │
└─────────────┘         └──────────────┘
  1. User logs in via Keycloak
  2. Keycloak issues JWT token
  3. Frontend/Backend receives token
  4. API validates token on each request

Security

Keycloak - Best Practices

✅ Use HTTPS in production (not http)
✅ Keep client secrets safe (environment variables)
✅ Use short-lived access tokens (5-15 minutes)
✅ Use refresh tokens for long sessions
✅ Separate realms for dev/staging/prod
✅ Regular backups of Keycloak database
✅ Strong password policies for users
✅ Enable MFA (Multi-Factor Authentication) for admins

Assignment

Secure the API you created in the previous assignment with Keycloak.

Requirements:

  1. Deploy Keycloak locally with Docker
  2. Create a realm for your application
  3. Create a confidential client for your API
  4. Create at least 2 test users
  5. Explore the OIDC endpoints (use the discovery endpoint)
  6. Read the Keycloak documentation on protecting APIs