TSD3060 - Utvikling av sikre webtjenester
Komplett oppsummering

Navigasjon:
Demonisering Containere Webteknologier Database CGI/REST JavaScript Sikkerhet Eksamensforberedelse

🎯 Viktige sikkerhetsprinsipper gjennom hele faget

1. Demonisering av prosesser

Hva er en demon?

En demon er en bakgrunnsprosess som kjører kontinuerlig uten brukerinteraksjon. Webtjenere, databaser og systemtjenester er typiske demoner. Målet er å skape en robust, sikker tjeneste som kan starte automatisk og håndtere feil elegant.

Detaljert demoniseringsprosess (Stevens-modellen)

1. Fork og exit forelder

pid_t pid = fork();
if (pid > 0) {
    exit(0);  // Forelder avslutter
}
// Barn fortsetter som orphan → adopteres av init
                

2. Bli session leader

if (setsid() == -1) {
    perror("setsid failed");
    exit(1);
}
// Frigjør controlling terminal og bli process group leader
                

3. Fork igjen (valgfritt, men anbefalt)

pid = fork();
if (pid > 0) {
    exit(0);  // Session leader avslutter
}
// Hindrer gjenakkvisisjon av controlling terminal
                

4. Endre arbeidsmappe

if (chdir("/") == -1) {
    perror("chdir failed");
    exit(1);
}
// Unngå å låse filesystemer
                

5. Lukk file descriptors

for (int fd = 0; fd < getdtablesize(); fd++) {
    close(fd);
}
// Eller: closefrom(0) på systemer som støtter det
                

6. Omdirigér standard I/O

int devnull = open("/dev/null", O_RDWR);
dup2(devnull, STDIN_FILENO);
dup2(devnull, STDOUT_FILENO);
dup2(devnull, STDERR_FILENO);
if (devnull > 2) close(devnull);
                

🔒 Sikkerhetshensyn ved demonisering

  • Privilegie-dropping: Start som root for å binde priviligerte porter, så skift til upriviligert bruker
  • Signal handling: Implementer ryddig shutdown på SIGTERM/SIGINT
  • Logging: Bruk syslog for strukturert logging
  • PID-fil: Lagre prosess-ID for enkel administrasjon

2. Containere og operativsystem-virtualisering

Operativsystemnivå-virtualisering

Container-teknologi deler kjerne med vertssystemet, men isolerer prosesser, filer og nettverk. Dette gir høy ytelse og ressurseffektivitet sammenlignet med full virtualisering.

Linux-mekanismer for containerisering

Namespaces - Isolering av systemressurser

# PID Namespace - isolerte prosess-IDs
unshare --pid --fork --mount-proc chroot /container/root /bin/sh

# Network Namespace - eget nettverk-stack
ip netns add container1
ip netns exec container1 ip link set dev lo up

# Mount Namespace - eget filesystem view
unshare --mount chroot /container/root /bin/sh

# User Namespace - UID/GID mapping
unshare --user --map-root-user /bin/sh
                

Control Groups (cgroups) - Ressursbegrensning

# CPU-begrensning
echo 50000 > /sys/fs/cgroup/cpu/mycontainer/cpu.cfs_quota_us
echo 100000 > /sys/fs/cgroup/cpu/mycontainer/cpu.cfs_period_us

# Minne-begrensning  
echo 1G > /sys/fs/cgroup/memory/mycontainer/memory.limit_in_bytes

# I/O begrensning
echo "8:0 1048576" > /sys/fs/cgroup/blkio/mycontainer/blkio.throttle.read_bps_device
                

Docker/Podman arkitektur

Container Engine

Docker/Podman
Orkestrerer containere

Container Runtime

runc/crun
Faktisk containerstart

Image Registry

Docker Hub/Quay
Lagring av images

Orchestration

Kubernetes/Swarm
Cluster management

Dockerfile best practices

FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

FROM node:18-alpine
RUN addgroup -g 1001 -S nodejs && adduser -S nodejs -u 1001
WORKDIR /app
COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules
COPY --chown=nodejs:nodejs . .
USER nodejs
EXPOSE 3000
CMD ["node", "server.js"]
                

🔒 Container-sikkerhet

  • Rootless containers: Kjør container-engine som vanlig bruker
  • Minimal base images: Bruk distroless eller alpine images
  • Multi-stage builds: Unngå utviklingsverktøy i produksjon
  • Capabilities: --cap-drop ALL --cap-add NET_ADMIN (kun nødvendige)
  • Read-only filesystem: --read-only med tmpfs for temp-filer

3. Webteknologier: HTML5, XML og CSS

HTML5 evolusjon og nye muligheter

HTML5 vs HTML4/XHTML

HTML5 introduserte semantiske elementer, native multimedia-støtte, offline-kapabiliteter og kraftige JavaScript-APIer. DOCTYPE ble forenklet til <!DOCTYPE html>, og strict XHTML-syntaks ble valgfri.

HTML5 semantiske elementer

<!DOCTYPE html>
<html lang="no">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Moderne webside</title>
</head>
<body>
    <header>
        <nav><!-- Navigasjon --></nav>
    </header>
    <main>
        <article>
            <section><!-- Innholdseksjon --></section>
        </article>
        <aside><!-- Sidebar --></aside>
    </main>
    <footer><!-- Bunntekst --></footer>
</body>
</html>
                

XML og strukturell validering

XML med DTD-validering

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE users [
    <!ELEMENT users (user+)>
    <!ELEMENT user (name, email, role)>
    <!ATTLIST user id ID #REQUIRED>
    <!ELEMENT name (#PCDATA)>
    <!ELEMENT email (#PCDATA)>
    <!ELEMENT role (admin|user|guest)>
]>
<users>
    <user id="u1">
        <name>Ola Nordmann</name>
        <email>ola@example.com</email>
        <role>admin</role>
    </user>
</users>
                

⚠️ XML External Entity (XXE) sikkerhet

DTD kan referere eksterne entiteter, som kan føre til informasjonslekkasje eller denial-of-service. Deaktiver eksterne entiteter i produksjon:

# Java
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
                    

CSS og sikkerhet

CSS Grid og Flexbox for responsiv design

/* Grid layout */
.container {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: 1rem;
}

/* Flexbox for komponentlayout */
.navbar {
    display: flex;
    justify-content: space-between;
    align-items: center;
}

/* Media queries for responsivitet */
@media (max-width: 768px) {
    .container {
        grid-template-columns: 1fr;
    }
}
                

🔒 CSS-sikkerhetshensyn

  • Clickjacking: Bruk X-Frame-Options: DENY eller CSP frame-ancestors
  • CSS injection: Valider og escape brukerinnhold i CSS
  • Resource loading: Bruk CSP til å kontrollere hvilke ressurser som lastes
  • Data exfiltration: CSS kan lekke data via background-image requests

4. Database-programmering og SQLite

SQLite karakteristika

SQLite er en embedded, server-less database som lagrer data i en enkelt fil. Perfect for utviklingsmiljøer og applikasjoner med moderate krav til samtidighet. Støtter full ACID-transaksjonalitet og de fleste SQL-standarder.

JDBC-programmering og prepared statements

Sikker database-tilkobling

// Database connection med connection pooling
public class DatabaseManager {
    private static final String DB_URL = "jdbc:sqlite:app.db";
    private static HikariDataSource dataSource;
    
    static {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl(DB_URL);
        config.setMaximumPoolSize(10);
        config.setConnectionTimeout(30000);
        dataSource = new HikariDataSource(config);
    }
    
    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }
}
                

Prepared statements for SQL injection protection

public class UserDAO {
    public User findUserByEmail(String email) throws SQLException {
        String sql = "SELECT id, name, email, password_hash FROM users WHERE email = ?";
        
        try (Connection conn = DatabaseManager.getConnection();
             PreparedStatement stmt = conn.prepareStatement(sql)) {
            
            stmt.setString(1, email);
            ResultSet rs = stmt.executeQuery();
            
            if (rs.next()) {
                return new User(
                    rs.getInt("id"),
                    rs.getString("name"),
                    rs.getString("email"),
                    rs.getString("password_hash")
                );
            }
            return null;
        }
    }
    
    public boolean createUser(String name, String email, String passwordHash) throws SQLException {
        String sql = "INSERT INTO users (name, email, password_hash) VALUES (?, ?, ?)";
        
        try (Connection conn = DatabaseManager.getConnection();
             PreparedStatement stmt = conn.prepareStatement(sql)) {
            
            stmt.setString(1, name);
            stmt.setString(2, email);
            stmt.setString(3, passwordHash);
            
            return stmt.executeUpdate() > 0;
        }
    }
}
                

Transaksjoner og integritet

ACID-transaksjoner

public void transferMoney(int fromUserId, int toUserId, double amount) throws SQLException {
    Connection conn = DatabaseManager.getConnection();
    try {
        conn.setAutoCommit(false);  // Start transaksjon
        
        // Trekk fra sender
        String debitSql = "UPDATE accounts SET balance = balance - ? WHERE user_id = ? AND balance >= ?";
        try (PreparedStatement debitStmt = conn.prepareStatement(debitSql)) {
            debitStmt.setDouble(1, amount);
            debitStmt.setInt(2, fromUserId);
            debitStmt.setDouble(3, amount);
            
            if (debitStmt.executeUpdate() == 0) {
                throw new SQLException("Insufficient funds");
            }
        }
        
        // Legg til mottaker
        String creditSql = "UPDATE accounts SET balance = balance + ? WHERE user_id = ?";
        try (PreparedStatement creditStmt = conn.prepareStatement(creditSql)) {
            creditStmt.setDouble(1, amount);
            creditStmt.setInt(2, toUserId);
            creditStmt.executeUpdate();
        }
        
        conn.commit();  // Bekreft transaksjon
    } catch (SQLException e) {
        conn.rollback();  // Tilbakerull ved feil
        throw e;
    } finally {
        conn.setAutoCommit(true);
        conn.close();
    }
}
                

🔒 Database-sikkerhet

  • SQL Injection: Bruk kun prepared statements, aldri string concatenation
  • Principle of least privilege: Database-bruker kun med nødvendige permissions
  • Encryption: Krypter sensitive data (passwords, PII) før lagring
  • Backup-sikkerhet: Krypter backups og begrens tilgang
  • Connection security: Bruk TLS for database-forbindelser

5. CGI og REST API-design

Common Gateway Interface (CGI) detaljert

CGI prosessflyt

CGI er en standard for hvordan webservere utfører eksterne programmer og returnerer resultatet til klienter. Hver forespørsel starter en ny prosess, som gir isolasjon men kan være ressurskrevende ved høy trafikk.

CGI miljøvariabler og prosess

1. HTTP request → Webserver (Apache/Nginx)
2. Server parser URL og identifiserer CGI-script
3. Server setter miljøvariabler:
   - REQUEST_METHOD=GET/POST/PUT/DELETE
   - QUERY_STRING=param1=value1¶m2=value2
   - CONTENT_TYPE=application/json
   - CONTENT_LENGTH=1234
   - REMOTE_ADDR=192.168.1.100
   - HTTP_USER_AGENT=Mozilla/5.0...
4. Server starter CGI-prosess med exec()
5. CGI-script leser miljø og stdin
6. Script skriver HTTP-headers og body til stdout
7. Server sender output tilbake til klient
8. CGI-prosess termineres
                

Java CGI-eksempel

import java.util.Map;

public class HelloCGI {
    public static void main(String[] args) {
        try {
            // Les miljøvariabler
            Map<String, String> env = System.getenv();
            String method = env.get("REQUEST_METHOD");
            String query = env.get("QUERY_STRING");
            
            // Skriv HTTP-headers
            System.out.println("Content-Type: application/json");
            System.out.println("Cache-Control: no-cache");
            System.out.println();  // Blank linje mellom headers og body
            
            // Skriv JSON response
            System.out.println("{");
            System.out.println("  \"method\": \"" + method + "\",");
            System.out.println("  \"query\": \"" + (query != null ? query : "") + "\",");
            System.out.println("  \"timestamp\": " + System.currentTimeMillis());
            System.out.println("}");
            
        } catch (Exception e) {
            // Error handling
            System.out.println("Content-Type: application/json");
            System.out.println("Status: 500 Internal Server Error");
            System.out.println();
            System.out.println("{\"error\": \"" + e.getMessage() + "\"}");
        }
    }
}
                

REST API principper og design

REST constraints

  • Client-Server: Separasjon av concerns
  • Stateless: Hver request inneholder all nødvendig informasjon
  • Cacheable: Responses må være merkede som cacheable eller non-cacheable
  • Uniform Interface: Konsistent bruk av HTTP-metoder og statuskoder
  • Layered System: Kan ha proxies, load balancers, etc.
  • Code on Demand (optional): Server kan sende executable code

RESTful API design-eksempel

# Ressursbaserte URLs
GET /api/v1/users           # Hent alle brukere
GET /api/v1/users/123       # Hent bruker med ID 123
POST /api/v1/users          # Opprett ny bruker
PUT /api/v1/users/123       # Oppdater bruker 123 (full replace)
PATCH /api/v1/users/123     # Delvis oppdatering av bruker 123
DELETE /api/v1/users/123    # Slett bruker 123

# Filtering og sortering
GET /api/v1/users?role=admin&sort=name&limit=10&offset=20

# Statuskoder
200 OK                      # Vellykket GET, PUT, PATCH
201 Created                 # Vellykket POST
204 No Content              # Vellykket DELETE
400 Bad Request             # Ugyldig input
401 Unauthorized            # Ikke autentisert
403 Forbidden               # Ikke autorisert
404 Not Found               # Ressurs ikke funnet
409 Conflict                # Konflikt (duplicate key, etc.)
500 Internal Server Error   # Server-feil
                

Content negotiation og HATEOAS

# Request headers
Accept: application/json
Accept-Language: no-NO,en;q=0.8
If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT

# Response med HATEOAS links
{
  "id": 123,
  "name": "Ola Nordmann",
  "email": "ola@example.com",
  "_links": {
    "self": {"href": "/api/v1/users/123"},
    "edit": {"href": "/api/v1/users/123", "method": "PUT"},
    "delete": {"href": "/api/v1/users/123", "method": "DELETE"},
    "orders": {"href": "/api/v1/users/123/orders"}
  }
}
                

SOAP vs REST sammenligning

SOAP

  • XML-basert protokoll
  • WSDL for service description
  • Støtter WS-Security
  • Tungvekts, kompleks

REST

  • Arkitekturstil, ikke protokoll
  • Bruker HTTP-metoder direkte
  • JSON/XML payload
  • Lettvekts, enkelt

🔒 API-sikkerhet

  • Authentication: OAuth 2.0, JWT tokens, API keys
  • Authorization: Role-based access control (RBAC)
  • Rate limiting: Hindre DoS og missbruk
  • Input validation: Valider alle inputs på server-side
  • HTTPS: Krypter all kommunikasjon
  • CORS: Kontroller cross-origin requests

6. JavaScript og moderne webapplikasjoner

JavaScript språkfunksjoner og ES6+ features

Modern JavaScript patterns

// Arrow functions og destructuring
const users = await fetch('/api/users').then(r => r.json());
const {name, email, role = 'user'} = currentUser;

// Template literals og expressions
const greeting = `Hei ${name}, du har ${unreadCount} uleste meldinger`;

// Async/await for cleaner asynchronous code
async function loadUserData(userId) {
    try {
        const response = await fetch(`/api/users/${userId}`);
        if (!response.ok) {
            throw new Error(`HTTP ${response.status}: ${response.statusText}`);
        }
        const user = await response.json();
        return user;
    } catch (error) {
        console.error('Failed to load user:', error);
        throw error;
    }
}

// Modules og imports
import {validateEmail} from './utils.js';
export class UserService {
    static async createUser(userData) {
        if (!validateEmail(userData.email)) {
            throw new Error('Invalid email address');
        }
        // ... implementation
    }
}
                

DOM-manipulering og event handling

Moderne DOM-interaksjon

// Query selectors og element manipulation
const userList = document.querySelector('#user-list');
const searchInput = document.querySelector('#search');

// Event delegation for dynamisk innhold
userList.addEventListener('click', (event) => {
    if (event.target.classList.contains('delete-btn')) {
        const userId = event.target.dataset.userId;
        deleteUser(userId);
    }
});

// Debounced search input
let searchTimeout;
searchInput.addEventListener('input', (event) => {
    clearTimeout(searchTimeout);
    searchTimeout = setTimeout(() => {
        performSearch(event.target.value);
    }, 300);
});

// Dynamic content creation
function renderUser(user) {
    const userElement = document.createElement('div');
    userElement.className = 'user-card';
    userElement.innerHTML = `
        <h3>${escapeHtml(user.name)}</h3>
        <p>${escapeHtml(user.email)}</p>
        <button class="delete-btn" data-user-id="${user.id}">Slett</button>
    `;
    return userElement;
}

// XSS protection helper
function escapeHtml(text) {
    const div = document.createElement('div');
    div.textContent = text;
    return div.innerHTML;
}
                

AJAX, Fetch API og Progressive Web Apps

Fetch API med error handling

class ApiClient {
    constructor(baseUrl) {
        this.baseUrl = baseUrl;
        this.token = localStorage.getItem('authToken');
    }
    
    async request(endpoint, options = {}) {
        const url = `${this.baseUrl}${endpoint}`;
        const config = {
            headers: {
                'Content-Type': 'application/json',
                ...(this.token && {'Authorization': `Bearer ${this.token}`}),
                ...options.headers
            },
            ...options
        };
        
        const response = await fetch(url, config);
        
        if (response.status === 401) {
            this.handleUnauthorized();
            throw new Error('Unauthorized');
        }
        
        if (!response.ok) {
            const error = await response.json().catch(() => ({message: 'Unknown error'}));
            throw new Error(error.message || `HTTP ${response.status}`);
        }
        
        return response.json();
    }
    
    handleUnauthorized() {
        localStorage.removeItem('authToken');
        window.location.href = '/login';
    }
}
                

Progressive Web Apps (PWA) og Service Workers

Service Worker for caching

// service-worker.js
const CACHE_NAME = 'myapp-v1';
const urlsToCache = [
    '/',
    '/styles/main.css',
    '/scripts/app.js',
    '/offline.html'
];

// Install event - cache resources
self.addEventListener('install', (event) => {
    event.waitUntil(
        caches.open(CACHE_NAME)
            .then((cache) => cache.addAll(urlsToCache))
    );
});

// Fetch event - serve from cache or network
self.addEventListener('fetch', (event) => {
    event.respondWith(
        caches.match(event.request)
            .then((response) => {
                if (response) {
                    return response; // Return cached version
                }
                
                // Fetch from network
                return fetch(event.request).catch(() => {
                    // If network fails, return offline page for navigate requests
                    if (event.request.destination === 'document') {
                        return caches.match('/offline.html');
                    }
                });
            })
    );
});

// Push event - handle notifications
self.addEventListener('push', (event) => {
    const options = {
        body: event.data.text(),
        icon: '/icons/icon-192x192.png',
        badge: '/icons/badge-72x72.png',
        actions: [
            {action: 'open', title: 'Åpne app'},
            {action: 'close', title: 'Lukk'}
        ]
    };
    
    event.waitUntil(
        self.registration.showNotification('Ny melding', options)
    );
});
                

PWA manifest.json

{
    "name": "Min Sikre Webapplikasjon",
    "short_name": "SikkerApp",
    "description": "En sikker og moderne webapplikasjon",
    "start_url": "/",
    "display": "standalone",
    "background_color": "#ffffff",
    "theme_color": "#000000",
    "icons": [
        {
            "src": "/icons/icon-72x72.png",
            "sizes": "72x72",
            "type": "image/png"
        },
        {
            "src": "/icons/icon-192x192.png",
            "sizes": "192x192",
            "type": "image/png"
        },
        {
            "src": "/icons/icon-512x512.png",
            "sizes": "512x512",
            "type": "image/png"
        }
    ]
}
                

🔒 JavaScript-sikkerhet

  • XSS Prevention: Escape/sanitize all user input, use textContent instead of innerHTML
  • CSRF Protection: Use CSRF tokens for state-changing operations
  • Same Origin Policy: Understand and properly configure CORS
  • Content Security Policy: Restrict resource loading and inline scripts
  • Secure Storage: Don't store sensitive data in localStorage/sessionStorage

7. Cookies, sessions og autentisering

HTTP State Management

HTTP er stateless, men webapplikasjoner trenger tilstand. Cookies og sessions løser dette ved å knytte forespørsler til brukeridentitet og session-data.

Cookie-sikkerhet og attributter

Sikre cookie-attributter

# Komplett sikker cookie
Set-Cookie: sessionid=abc123; 
    Secure; 
    HttpOnly; 
    SameSite=Strict; 
    Path=/; 
    Max-Age=3600; 
    Domain=.example.com

# Forklaring av attributter:
Secure      - Kun sendt over HTTPS
HttpOnly    - Ikke tilgjengelig via JavaScript (XSS-beskyttelse)
SameSite    - Kontrollerer cross-site requests (CSRF-beskyttelse)
  - Strict: Aldri sendt med cross-site requests
  - Lax: Sendt med top-level navigation (GET)
  - None: Alltid sendt (krever Secure)
Path        - Begrenser hvilke URLer som får cookien
Max-Age     - Levetid i sekunder (alternativ til Expires)
Domain      - Hvilke domener som får cookien
                

Session Management

Java session management eksempel

@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    
    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        
        // Validate credentials (with proper password hashing)
        if (authenticateUser(username, password)) {
            HttpSession session = request.getSession(true);
            
            // Regenerate session ID to prevent session fixation
            session.invalidate();
            session = request.getSession(true);
            
            // Store user info in session
            session.setAttribute("userId", getUserId(username));
            session.setAttribute("username", username);
            session.setAttribute("loginTime", System.currentTimeMillis());
            
            // Set session timeout (30 minutes)
            session.setMaxInactiveInterval(30 * 60);
            
            // Secure cookie configuration
            Cookie sessionCookie = new Cookie("JSESSIONID", session.getId());
            sessionCookie.setHttpOnly(true);
            sessionCookie.setSecure(request.isSecure());
            sessionCookie.setPath("/");
            response.addCookie(sessionCookie);
            
            response.sendRedirect("/dashboard");
        } else {
            // Rate limiting should be implemented here
            request.setAttribute("error", "Invalid credentials");
            request.getRequestDispatcher("/login.jsp").forward(request, response);
        }
    }
}
                

Autentisering og autorisering

Modern authentication patterns

// JWT-based authentication
public class JWTUtil {
    private static final String SECRET = "your-secret-key";
    private static final int EXPIRATION = 3600; // 1 hour
    
    public static String generateToken(String userId, List<String> roles) {
        return Jwts.builder()
            .setSubject(userId)
            .claim("roles", roles)
            .setIssuedAt(new Date())
            .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION * 1000))
            .signWith(SignatureAlgorithm.HS256, SECRET)
            .compact();
    }
    
    public static Claims validateToken(String token) {
        try {
            return Jwts.parser()
                .setSigningKey(SECRET)
                .parseClaimsJws(token)
                .getBody();
        } catch (JwtException e) {
            throw new SecurityException("Invalid token: " + e.getMessage());
        }
    }
}

// Password hashing with bcrypt
public class PasswordUtil {
    private static final int COST = 12;
    
    public static String hashPassword(String plainPassword) {
        return BCrypt.hashpw(plainPassword, BCrypt.gensalt(COST));
    }
    
    public static boolean verifyPassword(String plainPassword, String hashedPassword) {
        return BCrypt.checkpw(plainPassword, hashedPassword);
    }
}
                

Role-based access control

@Component
public class SecurityService {
    
    public boolean hasPermission(String userId, String resource, String action) {
        User user = userService.findById(userId);
        Set<Role> roles = user.getRoles();
        
        for (Role role : roles) {
            for (Permission permission : role.getPermissions()) {
                if (permission.getResource().equals(resource) && 
                    permission.getAction().equals(action)) {
                    return true;
                }
            }
        }
        return false;
    }
    
    public void requirePermission(String userId, String resource, String action) {
        if (!hasPermission(userId, resource, action)) {
            throw new AccessDeniedException(
                String.format("User %s lacks permission %s:%s", userId, resource, action)
            );
        }
    }
}

// Usage in controllers
@RestController
public class UserController {
    
    @Autowired
    private SecurityService securityService;
    
    @DeleteMapping("/users/{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable String id, Authentication auth) {
        securityService.requirePermission(auth.getName(), "users", "delete");
        userService.deleteUser(id);
        return ResponseEntity.noContent().build();
    }
}
                

🔐 Overordnede sikkerhetsprinsipper

Defense in Depth

  • Nettverk: Firewall, VPN, network segmentation
  • Host: OS hardening, antivirus, intrusion detection
  • Applikasjon: Input validation, authentication, authorization
  • Data: Encryption at rest, in transit, field-level encryption

Threat Modeling

  • STRIDE: Spoofing, Tampering, Repudiation, Information Disclosure, Denial of Service, Elevation of Privilege
  • OWASP Top 10: Kjente sårbarheter som Injection, Broken Authentication, Sensitive Data Exposure
  • Risk Assessment: Likelihood × Impact = Risk Level

Secure Development Lifecycle

  • Secure by Design: Sikkerhet fra start, ikke nachspiel
  • Code Review: Peer review med sikkerhetsfokus
  • Security Testing: SAST, DAST, penetration testing
  • Incident Response: Plan for håndtering av sikkerhetsbrudd

8. Eksamensforberedelse og sentrale konsepter

🎯 Typiske spørsmålstyper fra tidligere eksamener

1. Konseptforklaringer (20-30%)

  • "Hva er hensikten med demonisering?"
  • "Forklar hvordan chroot() bidrar til sikkerhet"
  • "Hva menes med operativsystemnivå-virtualisering?"
  • "Hvordan fungerer informasjonskapsler i HTTP?"

2. Sikkerhetsvurderinger (25-35%)

  • "Hvorfor er port 80 et sikkerhetsproblem?"
  • "Begrunn hvorfor DTD kan/ikke kan øke sikkerhet"
  • "Vurder autentiseringsmekanismen i systemet"
  • "Hvilke sikkerhetsproblemer ser du i denne koden?"

3. Kodeforståelse og -analyse (20-30%)

  • Gitt komplette systemer med flere filer
  • "Gi en detaljert beskrivelse av koden"
  • "Forklar hvilken rolle hver fil har"
  • "Hva vil denne JavaScript-koden skrive ut?"

4. Implementering og forbedring (15-25%)

  • "Hvordan ville du implementert passordoppdatering?"
  • "Foreslå forbedringer til autentiseringen"
  • "Vis hvordan du bygger et container-bilde"

🔒 Viktige sikkerhetsprinsipper å mestre

1. Minimal privilegier (Principle of Least Privilege)

  • Implementering: Start som root → bind port → drop privileges
  • Container user namespaces
  • Linux capabilities i stedet for full root
# Eksempler:
# Webtjener som www-data bruker
# Container med USER 1000:1000
# --cap-drop ALL --cap-add NET_ADMIN
                    

2. Defense in Depth

Lag på lag: Nettverk → OS → Container → App → Data

Eksempel web-app:
- Firewall (nettverk)
- SELinux/AppArmor (OS)
- Container isolation (runtime)  
- Input validation (app)
- Encrypted storage (data)
                    

3. Input Validation

  • Aldri stol på klient-data: Validate server-side
  • Strukturell validering: DTD, XML Schema
  • SQL injection prevention: Prepared statements
  • Shell injection: Unngå system() med brukerinput

4. Fail Secure

  • Default deny: Eksplisitt tillat, ikke eksplisitt forby
  • Eksempler: Firewall DROP, file permissions 640, authentication reject

💻 Teknologier og kommandoer å kunne

Linux/Container-kommandoer

# Namespace isolation
unshare --pid --net --mount --fork chroot /new/root /bin/sh

# Container management
podman run -d --name web -p 8080:80 nginx
podman exec -it web /bin/sh
podman build -t myapp .

# Capabilities
docker run --cap-drop ALL --cap-add NET_ADMIN alpine

# User namespaces
podman run --user 1000:1000 alpine id
                

Database-sikkerhet

-- Prepared statement (conceptual)
PREPARE stmt FROM 'SELECT * FROM users WHERE id = ?';
EXECUTE stmt USING @user_id;

-- SQLite foreign keys
PRAGMA foreign_keys=ON;

-- User creation with minimal privileges
CREATE USER 'webapp'@'localhost' IDENTIFIED BY 'secure_password';
GRANT SELECT, INSERT, UPDATE ON webapp.* TO 'webapp'@'localhost';
                

HTTP og Web-sikkerhet

# Secure headers
HTTP/1.1 200 OK
Set-Cookie: sessionid=abc123; HttpOnly; Secure; SameSite=Strict
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Access-Control-Allow-Origin: https://trusted-domain.com
                

🔗 Sammenhenger mellom temaer

  1. Container → CGI: Container isolerer CGI-script fra vert
  2. JavaScript → CORS: Same Origin Policy krever CORS for API-kall
  3. Database → Prepared statements: Forhindrer SQL injection
  4. Cookies → Sessions: Stateful behavior over stateless HTTP
  5. chroot → Container: Container utvider chroot-konseptet
  6. Privilegier → Capabilities: Finkornet alternativ til root/non-root

📚 Studietips for eksamen