✍️ 필사 모드: Application Security Engineering Guide — OWASP Top 10, Authentication, Encryption, DevSecOps
EnglishApplication 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:
| Cause | Percentage | Average Cost |
|---|---|---|
| Phishing attacks | 16% | 4.76M |
| Stolen/compromised credentials | 15% | 4.63M |
| Cloud misconfiguration | 12% | 4.14M |
| Zero-day vulnerabilities | 10% | 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.
| Factor | Description | Examples |
|---|---|---|
| Knowledge (Something you know) | Information the user knows | Password, PIN |
| Possession (Something you have) | An object the user possesses | Smartphone, security key |
| Biometric (Something you are) | Physical characteristics of the user | Fingerprint, 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: <script>alert("XSS")</script>
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 /app/dist ./dist
COPY /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).
| Phase | Tool | Purpose |
|---|---|---|
| Coding | IDE security plugins | Real-time vulnerability detection |
| Commit | Pre-commit hooks | Prevent secret leaks |
| Build | SAST | Static code analysis |
| Test | DAST | Dynamic security testing |
| Dependencies | SCA | Third-party library vulnerabilities |
| Deploy | Image scanning | Container vulnerabilities |
| Operations | RASP, WAF | Runtime 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:
- Reconnaissance: Identify the target system's tech stack and network architecture
- Mapping: Identify the Attack Surface
- Discovery: Combine automated tools and manual testing
- Exploitation: Verify the real-world risk of discovered vulnerabilities
- Reporting: Report technical details and business impact
Useful Tools
| Tool | Purpose |
|---|---|
| Burp Suite | Web proxy, vulnerability scanner |
| Nmap | Network scanning |
| sqlmap | SQL Injection automation |
| Nuclei | Template-based vulnerability scanning |
| Metasploit | Penetration 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:
- Incident Overview: date and time, type, and impact scope
- Timeline: chronological record from initial breach to discovery
- Technical Analysis: attack vector, exploited vulnerabilities, and attacker behavior
- Impact Analysis: type and volume of compromised data, business impact
- Response Actions: measures taken and their effectiveness
- Prevention: short-term and long-term improvements
- 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:
- Defense in Depth: If one security layer fails, other layers continue to defend
- Least Privilege: Grant only the minimum permissions necessary
- Fail Secure: Design systems so that failures maintain security
- Zero Trust: Verify every request regardless of network location
- 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...