Skip to content

✍️ 필사 모드: Application Security Engineering Guide — OWASP Top 10, Authentication, Encryption, DevSecOps

English
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.

Application Security Engineering Guide

In an era where software is at the heart of business, security is not a choice but a matter of survival. This guide covers the full spectrum of application security, from OWASP Top 10 to DevSecOps pipelines.


1. Why Security Matters

The Reality of Data Breaches

According to IBM's "Cost of a Data Breach Report," the average cost of a single data breach in 2024 was approximately 4.88 million dollars. The leading causes of breaches are as follows:

CausePercentageAverage Cost
Phishing attacks16%4.76M
Stolen/compromised credentials15%4.63M
Cloud misconfiguration12%4.14M
Zero-day vulnerabilities10%5.18M

Developer Responsibility for Security

Security is no longer solely the responsibility of the security team. Under the "Shift Left" paradigm, security must be embedded from the earliest stages of development.

  • Preventing vulnerabilities during the coding phase is 6 to 100 times cheaper than patching after deployment
  • Security must be considered throughout the entire SDLC (Software Development Life Cycle)
  • Security is not a feature; it is part of quality

The CIA Triad

  • Confidentiality: Only authorized users can access the data
  • Integrity: Data has not been tampered with without authorization
  • Availability: Systems and data are accessible when needed

2. OWASP Top 10 (2021)

The OWASP Top 10 is a catalog of the ten most critical security vulnerabilities in web applications. Let us examine each vulnerability and its defenses.

A01: Broken Access Control

The most common vulnerability. Unauthorized users can access other users' data or perform administrative functions.

Attack Scenario: Changing URL parameters to view another user's order history.

Defenses:

# Access control decorator in Flask
from functools import wraps
from flask import abort, g

def require_role(role):
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            if not g.current_user or g.current_user.role != role:
                abort(403)
            return f(*args, **kwargs)
        return decorated_function
    return decorator

@app.route('/admin/users')
@require_role('admin')
def admin_users():
    return get_all_users()
  • Default-deny: deny all access and explicitly allow via whitelist
  • Always validate permissions on the server side
  • Use UUIDs to prevent IDOR (Insecure Direct Object References)

A02: Cryptographic Failures

Sensitive data is transmitted in plaintext or protected with weak cryptography.

Defenses:

  • Data in transit: use TLS 1.3
  • Data at rest: encrypt with AES-256-GCM
  • Passwords: hash with bcrypt or Argon2
  • Never use weak algorithms such as MD5 or SHA-1

A03: Injection

Injection attacks targeting SQL, NoSQL, OS commands, LDAP, and more.

Vulnerable code:

# Never do this - vulnerable to SQL Injection
query = "SELECT * FROM users WHERE id = " + user_input

Safe code:

# Use parameterized queries
cursor.execute("SELECT * FROM users WHERE id = %s", (user_input,))

# Use an ORM (SQLAlchemy)
user = User.query.filter_by(id=user_input).first()

A04: Insecure Design

Security flaws at the design stage. These are architecture-level issues, not code-level bugs.

Defenses:

  • Perform Threat Modeling (STRIDE, PASTA methodologies)
  • Apply secure design principles (least privilege, defense in depth, fail secure)
  • Test business logic against security scenarios

A05: Security Misconfiguration

This includes unchanged default accounts, unnecessary features left enabled, and debug mode exposed in production.

Checklist:

  • Have default passwords been changed
  • Are unnecessary ports and services disabled
  • Do error messages avoid exposing stack traces
  • Are security headers (X-Frame-Options, X-Content-Type-Options) configured

A06: Vulnerable and Outdated Components

Using libraries or frameworks with known vulnerabilities.

Defenses:

# npm security audit
npm audit

# Python dependency audit
pip-audit

# Go vulnerability check
govulncheck ./...

A07: Identification and Authentication Failures

Weak password policies, poor session management, and brute-force attacks left unchecked. Covered in detail in Chapter 3.

A08: Software and Data Integrity Failures

Lack of integrity verification in CI/CD pipelines, software updates, and serialization.

Defenses:

  • Verify software integrity via digital signatures
  • Manage SBOM (Software Bill of Materials)
  • Secure the CI/CD pipeline (code signing, access control)

A09: Security Logging and Monitoring Failures

Security events are not recorded, or if they are, they are not monitored.

Essential logging items:

import logging
import json
from datetime import datetime, timezone

security_logger = logging.getLogger('security')

def log_security_event(event_type, user_id, details):
    event = {
        "timestamp": datetime.now(timezone.utc).isoformat(),
        "event_type": event_type,
        "user_id": user_id,
        "ip_address": request.remote_addr,
        "user_agent": request.headers.get('User-Agent'),
        "details": details
    }
    security_logger.warning(json.dumps(event))

# Usage
log_security_event("LOGIN_FAILED", "user123", "Invalid password attempt 3")
log_security_event("PRIVILEGE_ESCALATION", "user456", "Attempted admin access")

A10: Server-Side Request Forgery (SSRF)

An attack that tricks the server into sending requests to attacker-specified URLs.

Defenses:

  • Apply an allowed URL whitelist
  • Block requests to internal network IP ranges (10.x, 172.16.x, 192.168.x)
  • Defend against DNS Rebinding

3. Authentication

Authentication is the process of verifying that a user is who they claim to be.

Password Hashing

Passwords must never be stored in plaintext or with reversible encryption.

# Argon2 - currently the most recommended hashing algorithm
from argon2 import PasswordHasher

ph = PasswordHasher(
    time_cost=3,       # iterations
    memory_cost=65536,  # 64MB memory
    parallelism=4,      # parallel threads
    hash_len=32,        # hash length
    salt_len=16         # salt length
)

# Hashing
hashed = ph.hash("user_password")
# Example output: $argon2id$v=19$m=65536,t=3,p=4$...

# Verification
try:
    ph.verify(hashed, "user_password")
    print("Password matches")
except Exception:
    print("Password does not match")
# bcrypt - a widely used alternative
import bcrypt

password = b"user_password"
salt = bcrypt.gensalt(rounds=12)
hashed = bcrypt.hashpw(password, salt)

# Verification
if bcrypt.checkpw(password, hashed):
    print("Password matches")

Multi-Factor Authentication (MFA)

Passwords alone are not enough. Combine at least two authentication factors.

FactorDescriptionExamples
Knowledge (Something you know)Information the user knowsPassword, PIN
Possession (Something you have)An object the user possessesSmartphone, security key
Biometric (Something you are)Physical characteristics of the userFingerprint, face recognition
# TOTP (Time-based One-Time Password) implementation
import pyotp

# Generate secret key
secret = pyotp.random_base32()

# Generate QR code URI
totp_uri = pyotp.totp.TOTP(secret).provisioning_uri(
    name="user@example.com",
    issuer_name="MyApp"
)

# Verification
totp = pyotp.TOTP(secret)
is_valid = totp.verify("123456")  # user-entered code

Passkeys (WebAuthn/FIDO2)

An authentication standard for a password-free future.

  • Based on public-key cryptography, making it resistant to phishing
  • After local authentication via biometrics or PIN, only the public key is sent to the server
  • Supported by Apple, Google, and Microsoft
// Passkey registration (browser side)
const credential = await navigator.credentials.create({
  publicKey: {
    challenge: serverChallenge,
    rp: { name: "MyApp", id: "myapp.com" },
    user: {
      id: userId,
      name: "user@example.com",
      displayName: "User"
    },
    pubKeyCredParams: [
      { alg: -7, type: "public-key" },   // ES256
      { alg: -257, type: "public-key" }  // RS256
    ],
    authenticatorSelection: {
      authenticatorAttachment: "platform",
      residentKey: "required",
      userVerification: "required"
    }
  }
});

4. Authorization

Authorization determines which resources an authenticated user can access.

RBAC (Role-Based Access Control)

The most widely used authorization model. Assign roles to users and map permissions to roles.

# RBAC implementation example
PERMISSIONS = {
    "admin": ["read", "write", "delete", "manage_users"],
    "editor": ["read", "write"],
    "viewer": ["read"],
}

def check_permission(user_role, required_permission):
    role_permissions = PERMISSIONS.get(user_role, [])
    return required_permission in role_permissions

# Usage
if check_permission(current_user.role, "delete"):
    delete_resource(resource_id)
else:
    raise PermissionError("You do not have delete permissions")

ABAC (Attribute-Based Access Control)

Provides fine-grained access control based on user attributes, resource attributes, and environmental attributes.

# ABAC policy example
def evaluate_policy(user, resource, action, environment):
    # Allow access only during business hours
    if environment["hour"] < 9 or environment["hour"] > 18:
        return False

    # Only allow access to documents in the same department
    if user["department"] != resource["department"]:
        return False

    # Managers can perform all actions, regular employees can only read
    if action == "write" and user["level"] < 3:
        return False

    return True

OAuth 2.0 Scopes

In OAuth 2.0, scopes limit the range of resources a client can access.

# OAuth 2.0 scope definition example
scopes:
  read:profile: "Read user profile"
  write:profile: "Update user profile"
  read:orders: "View order history"
  admin:users: "User management (admin only)"

JWT Security Best Practices

JWT is convenient but can cause serious security issues if misused.

import jwt
from datetime import datetime, timedelta, timezone

# JWT creation (RS256 recommended)
def create_token(user_id, role):
    payload = {
        "sub": user_id,
        "role": role,
        "iat": datetime.now(timezone.utc),
        "exp": datetime.now(timezone.utc) + timedelta(minutes=15),
        "iss": "myapp.com",
        "aud": "myapp.com"
    }
    return jwt.encode(payload, PRIVATE_KEY, algorithm="RS256")

# JWT verification
def verify_token(token):
    try:
        payload = jwt.decode(
            token,
            PUBLIC_KEY,
            algorithms=["RS256"],  # Explicitly specify algorithm (prevent none attack)
            issuer="myapp.com",
            audience="myapp.com"
        )
        return payload
    except jwt.ExpiredSignatureError:
        raise AuthError("Token has expired")
    except jwt.InvalidTokenError:
        raise AuthError("Invalid token")

JWT Security Checklist:

  • Use RS256 instead of HS256 (asymmetric signing)
  • Set short expiration times (under 15 minutes)
  • Manage Refresh Tokens on the server side
  • Explicitly validate the algorithm to prevent alg: none attacks

5. Encryption in Practice

Symmetric Encryption: AES-256-GCM

Uses the same key for encryption and decryption. Primarily used for data at rest.

from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os

# Key generation (256-bit)
key = AESGCM.generate_key(bit_length=256)

# Encryption
aesgcm = AESGCM(key)
nonce = os.urandom(12)  # 96-bit nonce
plaintext = b"Sensitive data here"
aad = b"additional authenticated data"

ciphertext = aesgcm.encrypt(nonce, plaintext, aad)

# Decryption
decrypted = aesgcm.decrypt(nonce, ciphertext, aad)

Asymmetric Encryption: RSA and ECDSA

from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives import hashes, serialization

# ECDSA key pair generation
private_key = ec.generate_private_key(ec.SECP256R1())
public_key = private_key.public_key()

# Signing
from cryptography.hazmat.primitives.asymmetric import utils
signature = private_key.sign(
    b"message to sign",
    ec.ECDSA(hashes.SHA256())
)

# Verification
public_key.verify(
    signature,
    b"message to sign",
    ec.ECDSA(hashes.SHA256())
)

TLS Configuration Best Practices

# Nginx TLS configuration
server {
    listen 443 ssl http2;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers on;

    # HSTS header
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

    # OCSP Stapling
    ssl_stapling on;
    ssl_stapling_verify on;
}

Key Management and HSM

  • Never hardcode keys in source code
  • Use managed services like AWS KMS, Google Cloud KMS, or Azure Key Vault
  • HSM (Hardware Security Module) processes keys within the hardware to prevent extraction
  • Establish and automate key rotation policies

6. Secure Coding

Input Validation

All external input is untrusted.

import re
from pydantic import BaseModel, validator, constr

class UserInput(BaseModel):
    username: constr(min_length=3, max_length=30, pattern=r'^[a-zA-Z0-9_]+$')
    email: str
    age: int

    @validator('email')
    def validate_email(cls, v):
        pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
        if not re.match(pattern, v):
            raise ValueError('Invalid email format')
        return v

    @validator('age')
    def validate_age(cls, v):
        if v < 0 or v > 150:
            raise ValueError('Invalid age')
        return v

Output Encoding (XSS Prevention)

# Escape HTML output
from markupsafe import escape

user_input = '<script>alert("XSS")</script>'
safe_output = escape(user_input)
# Result: &lt;script&gt;alert("XSS")&lt;/script&gt;

Parameterized Queries

# Using SQLAlchemy ORM
from sqlalchemy import text

# Safe approach
result = db.session.execute(
    text("SELECT * FROM users WHERE email = :email"),
    {"email": user_email}
)

CORS Configuration

# Flask-CORS configuration
from flask_cors import CORS

CORS(app, resources={
    r"/api/*": {
        "origins": ["https://myapp.com", "https://admin.myapp.com"],
        "methods": ["GET", "POST", "PUT", "DELETE"],
        "allow_headers": ["Content-Type", "Authorization"],
        "max_age": 86400
    }
})

CSP (Content Security Policy)

# CSP header configuration
@app.after_request
def set_csp(response):
    response.headers['Content-Security-Policy'] = (
        "default-src 'self'; "
        "script-src 'self' 'nonce-abc123'; "
        "style-src 'self' 'unsafe-inline'; "
        "img-src 'self' data: https:; "
        "connect-src 'self' https://api.myapp.com; "
        "frame-ancestors 'none'; "
        "base-uri 'self'"
    )
    return response

7. API Security

Rate Limiting

# Using Flask-Limiter
from flask_limiter import Limiter

limiter = Limiter(
    app,
    default_limits=["200 per day", "50 per hour"]
)

@app.route("/api/login", methods=["POST"])
@limiter.limit("5 per minute")
def login():
    # login logic
    pass

@app.route("/api/data")
@limiter.limit("100 per hour")
def get_data():
    # data retrieval logic
    pass

API Key Management

  • Pass API keys via headers (never in URL parameters)
  • Limit the permission scope per key
  • Maintain a history of key issuance and revocation
  • Monitor usage and detect anomalous patterns

mTLS (Mutual TLS Authentication)

A mechanism where both server and client verify each other's certificates.

# mTLS configuration on Kubernetes Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/auth-tls-verify-client: "on"
    nginx.ingress.kubernetes.io/auth-tls-secret: "default/ca-secret"
spec:
  tls:
    - hosts:
        - api.myapp.com
      secretName: tls-secret

HMAC Signing

Guarantees both integrity and authentication of requests simultaneously.

import hmac
import hashlib
import time

def create_hmac_signature(secret_key, method, path, body, timestamp):
    message = f"{method}\n{path}\n{timestamp}\n{body}"
    signature = hmac.new(
        secret_key.encode(),
        message.encode(),
        hashlib.sha256
    ).hexdigest()
    return signature

# Verification (with timestamp check)
def verify_request(secret_key, method, path, body, timestamp, signature):
    # Only allow requests within 5 minutes (replay attack prevention)
    if abs(time.time() - float(timestamp)) > 300:
        return False

    expected = create_hmac_signature(secret_key, method, path, body, timestamp)
    return hmac.compare_digest(expected, signature)

8. Container Security

Image Scanning

# Scan container image vulnerabilities with Trivy
trivy image myapp:latest

# Filter by severity
trivy image --severity HIGH,CRITICAL myapp:latest

# Use in CI/CD (fail build if vulnerabilities found)
trivy image --exit-code 1 --severity CRITICAL myapp:latest

Minimal-Privilege Dockerfile

# Multi-stage build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build

# Production image
FROM node:20-alpine
RUN addgroup -g 1001 -S appgroup && \
    adduser -S appuser -u 1001 -G appgroup
WORKDIR /app
COPY --from=builder --chown=appuser:appgroup /app/dist ./dist
COPY --from=builder --chown=appuser:appgroup /app/node_modules ./node_modules

# Run as non-root user
USER appuser
EXPOSE 3000
CMD ["node", "dist/server.js"]

Rootless Containers

  • Run containers without root privileges
  • Set runAsNonRoot and readOnlyRootFilesystem in securityContext
  • Drop unnecessary Linux Capabilities
# Kubernetes Pod Security Context
apiVersion: v1
kind: Pod
metadata:
  name: secure-app
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1001
    fsGroup: 1001
  containers:
    - name: app
      image: myapp:latest
      securityContext:
        allowPrivilegeEscalation: false
        readOnlyRootFilesystem: true
        capabilities:
          drop:
            - ALL

OPA/Gatekeeper

Manage Kubernetes cluster policies as code.

# Policy to deny privileged containers
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sPSPPrivilegedContainer
metadata:
  name: deny-privileged
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Pod"]
    excludedNamespaces:
      - kube-system

9. DevSecOps Pipeline

Pipeline Overview

DevSecOps is an approach that integrates Development (Dev), Security (Sec), and Operations (Ops).

PhaseToolPurpose
CodingIDE security pluginsReal-time vulnerability detection
CommitPre-commit hooksPrevent secret leaks
BuildSASTStatic code analysis
TestDASTDynamic security testing
DependenciesSCAThird-party library vulnerabilities
DeployImage scanningContainer vulnerabilities
OperationsRASP, WAFRuntime security

SAST (Static Application Security Testing)

# Run SonarQube in GitHub Actions
name: Security Scan
on: [push, pull_request]

jobs:
  sonarqube:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: SonarQube Scan
        uses: sonarsource/sonarqube-scan-action@master
        env:
          SONAR_TOKEN: SONAR_TOKEN_PLACEHOLDER
          SONAR_HOST_URL: SONAR_HOST_PLACEHOLDER

DAST (Dynamic Application Security Testing)

# Dynamic security scanning with OWASP ZAP
docker run -t zaproxy/zap-stable zap-baseline.py \
  -t https://myapp.com \
  -r report.html

SCA (Software Composition Analysis)

# Dependabot configuration (.github/dependabot.yml)
version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"
    open-pull-requests-limit: 10
  - package-ecosystem: "pip"
    directory: "/"
    schedule:
      interval: "weekly"

SBOM (Software Bill of Materials)

# Generate SBOM with Syft
syft myapp:latest -o spdx-json > sbom.json

# Scan vulnerabilities based on SBOM with Grype
grype sbom:sbom.json

Secret Detection in Pre-commit

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/Yelp/detect-secrets
    rev: v1.4.0
    hooks:
      - id: detect-secrets
        args: ['--baseline', '.secrets.baseline']
  - repo: https://github.com/zricethezav/gitleaks
    rev: v8.18.0
    hooks:
      - id: gitleaks

10. Penetration Testing Fundamentals

Penetration Testing Methodology

Testing phases based on the OWASP Testing Guide:

  1. Reconnaissance: Identify the target system's tech stack and network architecture
  2. Mapping: Identify the Attack Surface
  3. Discovery: Combine automated tools and manual testing
  4. Exploitation: Verify the real-world risk of discovered vulnerabilities
  5. Reporting: Report technical details and business impact

Useful Tools

ToolPurpose
Burp SuiteWeb proxy, vulnerability scanner
NmapNetwork scanning
sqlmapSQL Injection automation
NucleiTemplate-based vulnerability scanning
MetasploitPenetration testing framework

Bug Bounty Programs

Programs that reward external security researchers for finding vulnerabilities in your services.

  • HackerOne: The world's largest bug bounty platform
  • Bugcrowd: Crowdsourced security testing
  • Self-managed: Clearly defining scope and reward criteria is essential

CTF (Capture The Flag) Recommendations

CTF platforms for building security skills:

  • OverTheWire: Step-by-step wargames from beginner to advanced
  • Hack The Box: Hands-on penetration testing environments
  • PicoCTF: Educational CTF suitable for beginners
  • PortSwigger Web Security Academy: Free web security learning platform

11. Incident Response

Incident Response Procedure (NIST SP 800-61)

Phase 1: Preparation

  • Form an Incident Response Team (IRT)
  • Develop and rehearse a response plan
  • Define communication channels and escalation procedures

Phase 2: Detection and Analysis

  • Detect anomalies via SIEM (Security Information and Event Management)
  • Analyze logs and identify IoCs (Indicators of Compromise)
  • Determine the scope and impact of the incident

Phase 3: Containment, Eradication, Recovery

# Emergency response example: revoke leaked API keys
# 1. Immediately revoke affected keys
aws iam delete-access-key --user-name compromised-user --access-key-id AKIA...

# 2. Invalidate all related sessions
aws iam put-user-policy --user-name compromised-user \
  --policy-name DenyAll --policy-document file://deny-all.json

# 3. Issue new credentials and distribute securely

Phase 4: Post-Incident Activity

  • Write a post-mortem report
  • Reconstruct the timeline
  • Perform Root Cause Analysis
  • Develop and implement measures to prevent recurrence

Digital Forensics Basics

  • Principle of evidence preservation: analyze copies, never the originals
  • Chain of Custody: document the handling history of all evidence
  • Understand collection methods: memory dumps, disk imaging, network traffic capture
  • Key tools: Volatility (memory analysis), Autopsy (disk analysis), Wireshark (network analysis)

Incident Report Structure

A thorough incident report includes the following:

  1. Incident Overview: date and time, type, and impact scope
  2. Timeline: chronological record from initial breach to discovery
  3. Technical Analysis: attack vector, exploited vulnerabilities, and attacker behavior
  4. Impact Analysis: type and volume of compromised data, business impact
  5. Response Actions: measures taken and their effectiveness
  6. Prevention: short-term and long-term improvements
  7. Lessons Learned: improvements identified during the response process

Conclusion

Application security is not a single tool or process. It is a culture and a journey. Here are the core principles:

  1. Defense in Depth: If one security layer fails, other layers continue to defend
  2. Least Privilege: Grant only the minimum permissions necessary
  3. Fail Secure: Design systems so that failures maintain security
  4. Zero Trust: Verify every request regardless of network location
  5. Security is Culture: Every team member maintains security awareness and continues learning

Security threats evolve constantly. Use this guide as a starting point and combine continuous learning with practical application.

Quiz: Application Security Knowledge Check

Q1. Which vulnerability ranked first in OWASP Top 10 (2021)?

A: Broken Access Control. A vulnerability where unauthorized users can access other users' data or perform administrative functions.

Q2. Why should you not use MD5 for password hashing?

A: MD5 is too fast, making it vulnerable to brute-force attacks. Use algorithms intentionally designed to be slow, such as bcrypt or Argon2.

Q3. How can you prevent the alg: none attack in JWT?

A: Explicitly specify the allowed algorithms during token verification. Do not rely on library defaults; explicitly specify algorithms=["RS256"].

Q4. What is the difference between SAST and DAST?

A: SAST (Static) analyzes source code without executing it to find vulnerabilities. DAST (Dynamic) simulates attacks from the outside against a running application. Both should be used complementarily.

Q5. What is the most effective way to defend against SSRF attacks?

A: Apply a whitelist of allowed URLs and block requests to internal network IP ranges (10.x, 172.16.x, 192.168.x, 127.0.0.1). DNS Rebinding attacks should also be defended against.

현재 단락 (1/506)

In an era where software is at the heart of business, security is not a choice but a matter of survi...

작성 글자: 0원문 글자: 21,432작성 단락: 0/506