Application Security Best Practices 2025
Did you know that 87% of organizations experienced an application security breach in the past 12 months? Comprehensive guide to application security and data protection 2025 - from OWASP Top 10 through DevSecOps, automated security testing, web application security to GDPR/SOC2/ISO27001 compliance. Practical solutions for your production environment.

Why application security is non-negotiable in 2025
You wake up, check your phone, and discover your application has been breached. Customer data is out in the open, and Twitter is already talking about it. In 2024, the average cost of a single data breach hit $4.45 million according to the IBM Security Report.
And 43% of cyber attacks target small and medium businesses. Even Microsoft, Tesla, and SolarWinds have been hit. 60% of companies affected by a serious breach close within 6 months. Application security isn't optional anymore.
In 2025, the threats look different than even two years ago. Supply chain attacks jumped by 742% (Sonatype 2024), AI-powered threats are going mainstream, and regulatory requirements (GDPR, NIS2, DORA) now carry real enforcement with fines in the millions of euros. Reactive patching won't cut it. You need a proactive security-first approach.
Key areas of application security 2025:
- ✓OWASP Top 10 2025 – new vulnerabilities: Insecure Design, Supply Chain, SSRF
- ✓DevSecOps integration – security in every CI/CD stage, shift-left approach
- ✓Automated security testing – SAST/DAST/SCA in pipeline, continuous vulnerability scanning
- ✓API security – authentication, rate limiting, input validation, OAuth 2.1/OIDC
- ✓Compliance frameworks – GDPR, SOC2, ISO27001 requirements and automated auditing
This article is based on OWASP official guidelines, NIST Cybersecurity Framework, CIS Benchmarks, and industry reports from Gartner, Forrester, and Verizon DBIR. If you're interested in web application security in the context of cloud infrastructure, check out our cloud solutions. For system architecture, I also recommend the article about microservices vs monolith and Kubernetes for beginners.
OWASP Top 10 2025 - changes and new threats
OWASP Top 10 is the industry standard for web application security. The 2025 version introduces new vulnerability categories and reshuffles priorities based on data breach analysis from thousands of companies. Here is what changed and what to do about it.
A01:2025 - Broken Access Control
Still #1. 94% of applications tested in 2024 had access control vulnerabilities. That's nearly every application. The attack is simple: someone changes an ID in the URL and suddenly they can see another user's salary, documents, or account details.
Here is the problem and its fix in Node.js:
// ❌ Vulnerable - no proper authorization check
app.get('/api/users/:id/salary', (req, res) => {
const salary = db.getSalary(req.params.id);
res.json({ salary }); // anyone can see anyone's salary!
});
// ✅ Secure - proper authorization
app.get('/api/users/:id/salary', authenticate, (req, res) => {
if (req.user.id !== req.params.id && !req.user.isAdmin) {
return res.status(403).json({ error: 'Forbidden' });
}
const salary = db.getSalary(req.params.id);
res.json({ salary });
});Mitigation: Implement role-based access control (RBAC), deny by default, validate ownership, audit access logs.
A02:2025 - Cryptographic Failures
Formerly "Sensitive Data Exposure". Weak encryption, hardcoded secrets, cleartext transmission. These mistakes can cost millions in GDPR fines and destroy customer trust.
I still see this in 2025. Here is the wrong way and the right way to handle passwords:
// ❌ Vulnerable - MD5 is not cryptographically secure
const crypto = require('crypto');
const hash = crypto.createHash('md5').update(password).digest('hex');
// ✅ Secure - bcrypt with proper salt rounds
const bcrypt = require('bcrypt');
const saltRounds = 12;
const hash = await bcrypt.hash(password, saltRounds);
// ✅ Encryption at rest (AES-256-GCM)
const algorithm = 'aes-256-gcm';
const key = crypto.randomBytes(32); // from Key Vault in production
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv(algorithm, key, iv);
const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);Standards: TLS 1.3, AES-256-GCM, bcrypt/Argon2, Azure Key Vault/AWS KMS for key management.
A03:2025 - Injection
SQL Injection, NoSQL Injection, Command Injection. These are old attacks that still work. Injection dropped from #1 (2021) to #3 thanks to wider ORM and parameterized query adoption. But if you're still concatenating strings in SQL queries, stop and fix it now.
Creating the vulnerability is easy, and so is fixing it:
// ❌ Vulnerable - SQL Injection
const query = `SELECT * FROM users WHERE email = '${userInput}'`;
// userInput: ' OR '1'='1' --
// ✅ Secure - Parameterized query (Node.js/pg)
const query = 'SELECT * FROM users WHERE email = $1';
const result = await pool.query(query, [userInput]);
// ✅ Secure - ORM (TypeORM)
const user = await userRepository.findOne({
where: { email: userInput }
});
// ✅ Command Injection prevention
const { exec } = require('child_process');
// ❌ exec(`ping ${userInput}`); // Dangerous!
// ✅ Use allowlist validation
if (!/^[0-9.]+$/.test(userInput)) {
throw new Error('Invalid IP address');
}A04:2025 - Insecure Design (NEW)
This is a NEW category. It covers fundamental security flaws baked into the architecture from the start: no threat modeling, insecure by design. No patch will fix these. You need to redesign. It's like building a house without foundations: paint won't help.
Example of an unsecured password reset (I've seen this in production):
// ❌ Insecure Design - unlimited password reset attempts
app.post('/reset-password', async (req, res) => {
const { email, code, newPassword } = req.body;
if (await verifyResetCode(email, code)) {
await updatePassword(email, newPassword);
}
}); // Brute force attack possible!
// ✅ Secure Design - rate limiting + account lockout
const rateLimit = require('express-rate-limit');
const resetLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // 5 attempts
skipSuccessfulRequests: true
});
app.post('/reset-password', resetLimiter, async (req, res) => {
const { email, code, newPassword } = req.body;
const attempts = await getResetAttempts(email);
if (attempts >= 3) {
await lockAccount(email, '1 hour');
return res.status(429).json({ error: 'Too many attempts' });
}
// ... verification logic
});Prevention: Threat modeling (STRIDE/DREAD), security requirements in design phase, peer review.
A08:2025 - Software and Data Integrity Failures (NEW)
Supply chain attacks, insecure deserialization, unsigned updates. SolarWinds, Log4Shell fall here.
// package.json - dependency verification
{
"dependencies": {
"express": "^4.18.2"
},
"overrides": {
"express": {
"qs": "6.11.0" // Force specific version with security fix
}
}
}
// ✅ npm audit in CI/CD
npm audit --production --audit-level=high
// ✅ Dependency scanning (Snyk/Dependabot)
// .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "daily"
open-pull-requests-limit: 10
allow:
- dependency-type: "all"
// ✅ Subresource Integrity for CDN
<script
src="https://cdn.example.com/lib.js"
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/ux..."
crossorigin="anonymous">
</script>A10:2025 - Server-Side Request Forgery (NEW)
SSRF attacks allow attackers to execute requests from the server to internal resources. Cloud metadata endpoints (AWS/Azure) are often targets.
// ❌ Vulnerable - no URL validation
app.get('/fetch-url', async (req, res) => {
const url = req.query.url;
const response = await fetch(url); // SSRF!
// url = http://169.254.169.254/latest/meta-data/iam/security-credentials/
res.send(await response.text());
});
// ✅ Secure - URL allowlist + validation
const ALLOWED_DOMAINS = ['api.trusted-domain.com'];
app.get('/fetch-url', async (req, res) => {
const url = new URL(req.query.url);
// Block internal IPs
if (url.hostname === 'localhost' ||
url.hostname === '127.0.0.1' ||
url.hostname.startsWith('192.168.') ||
url.hostname.startsWith('10.') ||
url.hostname === '169.254.169.254') { // AWS metadata
return res.status(403).json({ error: 'Forbidden' });
}
// Allowlist check
if (!ALLOWED_DOMAINS.includes(url.hostname)) {
return res.status(403).json({ error: 'Domain not allowed' });
}
const response = await fetch(url.toString());
res.send(await response.text());
});Pro Tip: OWASP ZAP automated scanning
OWASP ZAP (Zed Attack Proxy) is a free open-source DAST tool. CI/CD pipeline integration: Docker image owasp/zap2docker-stable, baseline scan in every PR, full scan weekly. Detects 90%+ OWASP Top 10 vulnerabilities automatically.

DevSecOps - security in your CI/CD pipeline
Remember when security was only checked right before production? Finding critical vulnerabilities the day before release was a disaster. DevSecOps fixes that.
DevSecOps is a shift-left approach where you test security at every stage of development instead of bolting it on at the end. The Gartner 2024 report backs this up: organizations with mature DevSecOps practices have 60% fewer security incidents and 50% faster remediation time.
Here is how to implement security automation in your pipeline:
Security gates in GitHub Actions
If you're using GitHub Actions, you can add automatic security checks to every pull request. Here is a workflow you can copy to your project:
name: Security Checks
on: [pull_request]
jobs:
security-scan:
runs-on: ubuntu-latest
steps:
# SAST - Static code analysis
- name: Run Semgrep
uses: returntocorp/semgrep-action@v1
with:
config: >-
p/security-audit
p/owasp-top-ten
p/ci
# SCA - Dependency vulnerability scan
- name: Run Snyk
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --severity-threshold=high
# Secret scanning
- name: Gitleaks scan
uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Container scanning
- name: Trivy container scan
uses: aquasecurity/trivy-action@master
with:
image-ref: 'myapp:latest'
severity: 'CRITICAL,HIGH'
exit-code: '1' # Fail pipeline on vulnerabilitiesAzure DevOps security pipeline
Multi-stage security validation:
stages:
- stage: SecurityScan
jobs:
- job: SAST
steps:
- task: CredScan@3 # Microsoft Credential Scanner
inputs:
outputFormat: 'pre'
- task: SonarCloudPrepare@1
inputs:
SonarCloud: 'SonarCloud-Connection'
organization: 'my-org'
projectKey: 'my-project'
- task: DotNetCoreCLI@2
inputs:
command: 'build'
- task: SonarCloudAnalyze@1
- task: SonarCloudPublish@1
inputs:
pollingTimeoutSec: '300'
- job: DependencyScan
steps:
- task: NuGetCommand@2
inputs:
command: 'restore'
- task: WhiteSource@21 # Mend SCA
inputs:
cwd: '$(System.DefaultWorkingDirectory)'
projectName: 'MyApp'
- job: ContainerScan
steps:
- task: Docker@2
inputs:
command: 'build'
Dockerfile: '**/Dockerfile'
- task: AquaScanner@4
inputs:
image: 'myapp:latest'
scanner: 'aqua'
connection: 'AquaConnection'Security policy as code
OPA (Open Policy Agent) for infrastructure security:
# policy.rego - Kubernetes security policies
package kubernetes.admission
deny[msg] {
input.request.kind.kind == "Pod"
not input.request.object.spec.securityContext.runAsNonRoot
msg := "Containers must not run as root"
}
deny[msg] {
input.request.kind.kind == "Pod"
container := input.request.object.spec.containers[_]
not container.securityContext.readOnlyRootFilesystem
msg := sprintf("Container %v must use read-only root filesystem", [container.name])
}
deny[msg] {
input.request.kind.kind == "Service"
input.request.object.spec.type == "LoadBalancer"
not input.request.object.metadata.annotations["service.beta.kubernetes.io/azure-load-balancer-internal"]
msg := "LoadBalancer services must be internal only"
}
# Test policies in CI/CD
opa test policy.rego policy_test.rego
opa exec --decision kubernetes/admission --bundle policy/ kubernetes-manifest.yamlInfrastructure as Code security scanning
Checkov for Terraform/CloudFormation/Kubernetes:
# .github/workflows/iac-security.yml
name: IaC Security Scan
on: [push]
jobs:
checkov:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run Checkov
uses: bridgecrewio/checkov-action@master
with:
directory: terraform/
framework: terraform
output_format: cli
soft_fail: false # Fail build on security issues
- name: tfsec scan
uses: aquasecurity/tfsec-action@v1.0.0
with:
working_directory: terraform/
# Example Terraform security check results:
# ❌ CRITICAL: S3 bucket encryption not enabled
# ❌ HIGH: Security group allows 0.0.0.0/0 ingress
# ❌ MEDIUM: IAM policy allows wildcard actionsShift-Left Metrics
Forrester research shows: fixing a vulnerability in production costs 30x more than fixing it in the development phase. DevSecOps ROI: average 40% reduction in security incidents, 50% faster remediation time, 70% reduction in compliance audit effort.
Automated security testing - SAST, DAST, SCA
Which security testing tool should you choose? All three. Proper security testing requires three complementary approaches: SAST (code analysis), DAST (runtime testing), and SCA (dependency scanning). Each catches different types of vulnerabilities, and using only one leaves big gaps in your defenses.
If you're working with TypeScript, my article on TypeScript vs JavaScript covers how type safety contributes to security.
SAST - Static Analysis
Analyzes source code without execution. Fast, early detection.
# Semgrep - SAST for multiple languages
semgrep --config=auto src/
# SonarQube
sonar-scanner \
-Dsonar.projectKey=myapp \
-Dsonar.sources=. \
-Dsonar.host.url=http://sonar \
-Dsonar.login=${SONAR_TOKEN}
# ESLint security plugins
npm install --save-dev \
eslint-plugin-security \
eslint-plugin-no-secretsDetects: Injection flaws, hardcoded secrets, crypto issues.
DAST - Dynamic Analysis
Tests running application like a real attacker.
# OWASP ZAP baseline scan docker run -t owasp/zap2docker-stable \ zap-baseline.py \ -t https://staging.example.com \ -r zap-report.html # Burp Suite API scan burp --project-file=project.burp \ --unpause-spider-and-scanner \ --config=config.json # Nuclei vulnerability scanner nuclei -u https://example.com \ -t cves/ -t vulnerabilities/
Detects: Runtime issues, auth bypasses, config errors.
SCA - Dependency Scanning
Identifies vulnerabilities in third-party dependencies.
# npm audit npm audit --audit-level=high # Snyk snyk test --severity-threshold=high snyk monitor # Continuous monitoring # OWASP Dependency-Check dependency-check.sh \ --project "MyApp" \ --scan ./lib \ --format HTML \ --failOnCVSS 7
Detects: Known CVEs, license issues, outdated deps.
IAST - Interactive Testing
Combines SAST+DAST, instruments code in runtime.
# Contrast Security IAST agent
java -javaagent:contrast.jar \
-Dcontrast.api.key=${API_KEY} \
-jar myapp.jar
# Seeker IAST
dotnet add package Seeker.Agent
export SEEKER_SERVER_URL="https://..."
dotnet runAdvantage: Low false positives, code-level context.
Complete security testing pipeline
Integration of all testing types:
# .gitlab-ci.yml
stages:
- build
- sast
- test
- dast
- deploy
sast_scan:
stage: sast
image: returntocorp/semgrep
script:
- semgrep --config=auto --json --output=semgrep.json src/
artifacts:
reports:
sast: semgrep.json
dependency_scan:
stage: sast
image: node:18
script:
- npm audit --json > npm-audit.json
- snyk test --json > snyk.json
artifacts:
reports:
dependency_scanning: snyk.json
container_scan:
stage: test
image: aquasec/trivy
script:
- trivy image --severity HIGH,CRITICAL myapp:latest
dast_scan:
stage: dast
image: owasp/zap2docker-stable
script:
- zap-baseline.py -t ${STAGING_URL} -r zap-report.html
artifacts:
paths:
- zap-report.html
only:
- staging
- production
security_gate:
stage: deploy
script:
- |
if [ $(jq '.vulnerabilities | map(select(.severity=="CRITICAL")) | length' snyk.json) -gt 0 ]; then
echo "CRITICAL vulnerabilities found, blocking deployment"
exit 1
fiTool Selection Strategy
Open source start: Semgrep (SAST), OWASP ZAP (DAST), npm audit (SCA). Enterprise scale: Snyk/Checkmarx/Veracode for comprehensive coverage, lower false positives, compliance reporting. Average organization uses 3-5 security tools in pipeline.
API security best practices
Your API is the door to all your data. In 2025, APIs are the primary attack vector, with 83% of network traffic being API calls (Cloudflare 2024). Every unsecured endpoint is a potential data leak. The OWASP API Security Top 10 lists specific threats for REST and GraphQL APIs that need to be addressed.
Authentication & Authorization
OAuth 2.1 + OpenID Connect as industry standard:
// Node.js - JWT verification with proper validation
const jwt = require('jsonwebtoken');
const jwksClient = require('jwks-rsa');
const client = jwksClient({
jwksUri: 'https://auth.example.com/.well-known/jwks.json',
cache: true,
rateLimit: true
});
function getKey(header, callback) {
client.getSigningKey(header.kid, (err, key) => {
const signingKey = key.publicKey || key.rsaPublicKey;
callback(null, signingKey);
});
}
function authenticateToken(req, res, next) {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'No token provided' });
}
jwt.verify(token, getKey, {
algorithms: ['RS256'],
audience: 'api.example.com',
issuer: 'https://auth.example.com',
maxAge: '1h' // Token expiration
}, (err, decoded) => {
if (err) {
return res.status(403).json({ error: 'Invalid token' });
}
req.user = decoded;
next();
});
}
// Scope-based authorization
function requireScope(scope) {
return (req, res, next) => {
if (!req.user.scope.includes(scope)) {
return res.status(403).json({ error: 'Insufficient permissions' });
}
next();
};
}
app.get('/api/admin/users',
authenticateToken,
requireScope('admin:read'),
(req, res) => {
// Handler
}
);Rate Limiting & Throttling
Prevent brute force, DDoS, API abuse:
// Express rate limiting (sliding window)
const rateLimit = require('express-rate-limit');
const RedisStore = require('rate-limit-redis');
const Redis = require('ioredis');
const redis = new Redis({
host: process.env.REDIS_HOST,
password: process.env.REDIS_PASSWORD
});
// Global rate limit
const globalLimiter = rateLimit({
store: new RedisStore({
client: redis,
prefix: 'rl:global:'
}),
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // 100 requests per window
standardHeaders: true,
legacyHeaders: false,
handler: (req, res) => {
res.status(429).json({
error: 'Too many requests',
retryAfter: req.rateLimit.resetTime
});
}
});
// Endpoint-specific limits
const authLimiter = rateLimit({
store: new RedisStore({ client: redis, prefix: 'rl:auth:' }),
windowMs: 15 * 60 * 1000,
max: 5, // Stricter limit for auth endpoints
skipSuccessfulRequests: true // Only count failed attempts
});
// User-specific rate limiting
const userLimiter = rateLimit({
store: new RedisStore({ client: redis, prefix: 'rl:user:' }),
windowMs: 60 * 1000,
max: 60,
keyGenerator: (req) => req.user.id // Per-user limit
});
app.use('/api/', globalLimiter);
app.post('/api/auth/login', authLimiter);
app.use('/api/user/*', authenticateToken, userLimiter);Input Validation & Sanitization
Validate all inputs against strict schemas:
// Zod schema validation
const { z } = require('zod');
const UserSchema = z.object({
email: z.string().email().max(255),
password: z.string()
.min(12, 'Password must be at least 12 characters')
.regex(/[A-Z]/, 'Must contain uppercase')
.regex(/[a-z]/, 'Must contain lowercase')
.regex(/[0-9]/, 'Must contain number')
.regex(/[^A-Za-z0-9]/, 'Must contain special char'),
age: z.number().int().min(18).max(120),
role: z.enum(['user', 'admin', 'moderator'])
});
app.post('/api/users', async (req, res) => {
try {
const validatedData = UserSchema.parse(req.body);
// Additional business logic validation
const existingUser = await db.users.findOne({
email: validatedData.email
});
if (existingUser) {
return res.status(409).json({ error: 'Email already exists' });
}
// Sanitize HTML inputs
const sanitizeHtml = require('sanitize-html');
if (validatedData.bio) {
validatedData.bio = sanitizeHtml(validatedData.bio, {
allowedTags: ['b', 'i', 'em', 'strong'],
allowedAttributes: {}
});
}
const user = await createUser(validatedData);
res.json({ id: user.id });
} catch (error) {
if (error instanceof z.ZodError) {
return res.status(400).json({
errors: error.errors
});
}
throw error;
}
});API Gateway Security
Centralized security policies in API Gateway:
# Kong API Gateway configuration
services:
- name: user-service
url: http://users:8080
routes:
- name: users-route
paths:
- /api/users
plugins:
# Rate limiting
- name: rate-limiting
config:
minute: 60
policy: redis
# JWT authentication
- name: jwt
config:
key_claim_name: kid
secret_is_base64: false
# Request validation
- name: request-validator
config:
body_schema: |
{
"type": "object",
"properties": {
"email": {"type": "string", "format": "email"}
},
"required": ["email"]
}
# CORS
- name: cors
config:
origins:
- https://app.example.com
credentials: true
max_age: 3600
# IP restriction
- name: ip-restriction
config:
allow:
- 10.0.0.0/8
- 172.16.0.0/12
# Bot detection
- name: bot-detection
config:
allow:
- Googlebot
deny:
- BadBotGraphQL Security
Specific protections for GraphQL APIs:
const { ApolloServer } = require('apollo-server');
const depthLimit = require('graphql-depth-limit');
const { createComplexityLimitRule } = require('graphql-validation-complexity');
const server = new ApolloServer({
typeDefs,
resolvers,
// Query depth limiting (prevent deeply nested queries)
validationRules: [
depthLimit(7), // Max 7 levels deep
createComplexityLimitRule(1000, {
scalarCost: 1,
objectCost: 10,
listFactor: 20
})
],
// Query cost analysis
plugins: [{
requestDidStart() {
return {
didResolveOperation({ request, operation }) {
const complexity = calculateComplexity({
query: request.query,
variables: request.variables
});
if (complexity > 1000) {
throw new Error('Query too complex');
}
}
};
}
}],
// Disable introspection in production
introspection: process.env.NODE_ENV !== 'production',
// Context authentication
context: ({ req }) => {
const token = req.headers.authorization || '';
const user = verifyToken(token);
return { user };
}
});
// Field-level authorization
const resolvers = {
User: {
email: (parent, args, context) => {
if (context.user.id !== parent.id && !context.user.isAdmin) {
throw new Error('Unauthorized');
}
return parent.email;
}
}
};OWASP API Security Top 10 2025
Top threats: API1:2025 Broken Object Level Authorization, API2:2025 Broken Authentication, API3:2025 Broken Object Property Level Authorization, API4:2025 Unrestricted Resource Consumption, API5:2025 Broken Function Level Authorization. Each requires specific mitigation strategies.

Compliance - GDPR, SOC2, ISO 27001
"GDPR is just a checkbox, right?" No. In 2024, GDPR fines exceeded 2.5 billion euros. Amazon paid 746 million euros, Meta 1.2 billion euros. You can't ignore numbers like that.
Regulatory compliance is a legal requirement with real financial consequences. Automated compliance monitoring and documentation are what keep you ready for audits and protected against penalties. Here is how to implement the technical requirements.
GDPR Technical Requirements
Data Protection by Design and Default - technical implementation:
// GDPR-compliant data handling
class GDPRCompliantUserService {
async createUser(data) {
// Data minimization - only required fields
const user = {
email: data.email,
hashedPassword: await bcrypt.hash(data.password, 12),
consentGiven: data.consent, // Explicit consent required
consentDate: new Date(),
dataRetentionDate: addYears(new Date(), 2) // Auto-deletion after 2 years
};
// Audit log
await auditLog.create({
action: 'USER_CREATED',
userId: user.id,
timestamp: new Date(),
ipAddress: req.ip
});
return user;
}
async exportUserData(userId) {
// Right to data portability (Art. 20)
const user = await db.users.findById(userId);
const orders = await db.orders.find({ userId });
const logs = await auditLog.find({ userId });
return {
personalData: {
email: user.email,
createdAt: user.createdAt,
consent: user.consentGiven
},
transactionHistory: orders,
accessLog: logs
};
}
async deleteUser(userId) {
// Right to erasure (Art. 17)
await db.users.update(userId, {
email: 'deleted@deleted.com',
hashedPassword: null,
deletedAt: new Date(),
deletionReason: 'USER_REQUEST'
});
// Pseudonymize instead of hard delete for compliance
await auditLog.create({
action: 'USER_DELETED',
userId,
timestamp: new Date()
});
}
}SOC2 Type II Controls
Security, Availability, Confidentiality controls for SaaS:
# Infrastructure as Code - SOC2 compliant AWS setup
resource "aws_s3_bucket" "data" {
bucket = "company-data"
# CC6.6 - Encryption at rest
server_side_encryption_configuration {
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "aws:kms"
kms_master_key_id = aws_kms_key.data.arn
}
}
}
# CC6.1 - Access logging
logging {
target_bucket = aws_s3_bucket.logs.id
target_prefix = "s3-access-logs/"
}
# Versioning for data integrity
versioning {
enabled = true
}
}
# CC7.2 - Automated compliance monitoring
resource "aws_config_rule" "s3_encryption_check" {
name = "s3-bucket-server-side-encryption-enabled"
source {
owner = "AWS"
source_identifier = "S3_BUCKET_SERVER_SIDE_ENCRYPTION_ENABLED"
}
depends_on = [aws_config_configuration_recorder.main]
}
# CC8.1 - Security monitoring
resource "aws_cloudwatch_metric_alarm" "unauthorized_api_calls" {
alarm_name = "unauthorized-api-calls"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = "1"
metric_name = "UnauthorizedAPICalls"
namespace = "CloudTrailMetrics"
period = "300"
statistic = "Sum"
threshold = "5"
alarm_actions = [aws_sns_topic.security_alerts.arn]
}ISO27001 Information Security Controls
Annex A controls implementation - technical measures:
Automated Compliance Monitoring
Cloud-native compliance automation tools:
# Azure Policy - automated compliance enforcement
resource "azurerm_policy_assignment" "gdpr_compliance" {
name = "gdpr-compliance"
scope = azurerm_resource_group.main.id
policy_definition_id = "/providers/Microsoft.Authorization/policySetDefinitions/GDPR"
parameters = jsonencode({
effect = { value = "Audit" }
})
}
# AWS Security Hub - continuous compliance
resource "aws_securityhub_account" "main" {}
resource "aws_securityhub_standards_subscription" "cis" {
standards_arn = "arn:aws:securityhub:region::standards/cis-aws-foundations-benchmark/v/1.4.0"
}
resource "aws_securityhub_standards_subscription" "pci" {
standards_arn = "arn:aws:securityhub:region::standards/pci-dss/v/3.2.1"
}
# Compliance-as-Code - Terraform Sentinel policies
policy "encryption_at_rest" {
enforcement_level = "hard-mandatory"
}
rule "s3_encryption_required" {
condition = all s3_buckets as bucket {
bucket.server_side_encryption_configuration is not null
}
}Compliance Automation ROI
Manual compliance audits: 200-400 hours effort per audit. Automated compliance monitoring reduces audit preparation time by 70%, continuous compliance validation instead of point-in-time checks. Tools: AWS Security Hub, Azure Policy, GCP Security Command Center, Vanta, Drata.
Security audit checklist 2025
If you need to audit your application's security, this checklist is a good starting point. A real security audit requires a systematic approach. You can't just glance at the code and call it secure.
This checklist is based on NIST Cybersecurity Framework, CIS Benchmarks, and OWASP ASVS, the same standards used by Fortune 500 companies. Go through each point and verify your application against it.
1. Authentication & Access Control
2. Data Protection
3. Application Security
4. Infrastructure Security
5. Monitoring & Logging
6. Incident Response
Penetration Testing Schedule
Regular external validation by security professionals:
Frequently Asked Questions
What are the most important changes in OWASP Top 10 2025?
OWASP Top 10 2025 adds three new threat categories. Insecure Design (A04) refers to fundamental architecture flaws - those you can't fix with a patch. Software and Data Integrity Failures (A08) is the response to supply chain attacks like SolarWinds. Server-Side Request Forgery (A10) is a growing problem in cloud architectures. Injection drops from #1 to #3 thanks to the popularization of ORMs and prepared statements, but Broken Access Control still reigns as the #1 threat - 94% of applications have this vulnerability.
What is the difference between SAST and DAST?
SAST (Static Application Security Testing) is code analysis without running the application - like reading a recipe without cooking. You find errors in source code during the development phase. DAST (Dynamic Application Security Testing) tests a running application - like a hacker trying to break into your system. You need both: SAST will find hardcoded secrets and SQL injection in code, DAST will detect configuration errors and authorization problems at runtime. Best results: SAST in CI/CD with every pull request + DAST weekly in staging.
How to implement DevSecOps in an existing CI/CD pipeline?
Start with small steps - don't try to change everything at once. Step 1: Add SAST (e.g., Semgrep) to pull request checks - this will take you 30 minutes. Step 2: Enable SCA (dependency scanning) - npm audit or Snyk, another 15 minutes. Step 3: Container scanning for Docker images - Trivy in your pipeline. Step 4: DAST in staging environment - OWASP ZAP once a week. Key: automate everything, zero manual checks. Every pipeline stage should have a security gate - if it finds a critical vulnerability, deployment fails.
What are GDPR requirements for application security?
GDPR requires specific technical safeguards, not generalities. You must have: encryption of personal data in transit (TLS 1.3 minimum) and at rest (AES-256), access controls with least privilege principle (RBAC), audit logging of every access to personal data with 12-month retention, breach detection within 72 hours, privacy by design from the design phase, ability to export user data (right to portability) and delete on request (right to erasure). All of this should be automated - manual processes are a recipe for disaster during an audit.
What should a security audit checklist contain?
Your security audit checklist should cover 6 main areas: 1) Authentication & Access Control - MFA, RBAC, session management, 2) Data Protection - encryption at rest/transit, key management, backups, 3) Application Security - OWASP Top 10 compliance, SAST/DAST/SCA results, dependency vulnerabilities, 4) Infrastructure Security - network segmentation, firewall rules, patch management, container/Kubernetes security, 5) Monitoring & Logging - SIEM, security events, audit trails, incident detection, 6) Compliance - GDPR/SOC2/ISO27001 requirements, documentation, policies. Plus annual penetration test by external vendor and quarterly vulnerability assessments. Use standards: NIST Cybersecurity Framework, CIS Benchmarks, OWASP ASVS.
Summary: security-first in 2025
Application security in 2025 is a business necessity. This guide covered five areas: OWASP Top 10 and the most common threats, DevSecOps integrated into your development workflow, automated testing (SAST/DAST/SCA) for continuous validation, API security for modern architectures, and compliance frameworks (GDPR/SOC2/ISO27001) with their specific technical requirements.
The data is clear: organizations with mature security practices have 60% fewer incidents, 50% faster remediation time, and 70% reduction in compliance effort. Security doesn't cost you money. The lack of it does, in the form of breaches, fines, and customers who leave.
Need help securing your application?
I do security assessments, DevSecOps implementation, OWASP Top 10 vulnerability remediation, automated security testing, and compliance audit preparation (GDPR/SOC2/ISO27001). I've run dozens of security audits and helped companies lock down their applications. Schedule a free 30-minute consultation and we can look at your specific situation.