Integrating security into every phase of the Software Development Lifecycle (SDLC) reduces vulnerabilities and remediation costs. This guide covers practical security integration from requirements through deployment.
Security-First SDLC Framework
Phase Integration Model
# secure_sdlc_framework.py
from dataclasses import dataclass, field
from typing import List, Dict, Optional
from enum import Enum
from datetime import datetime
class SDLCPhase(Enum):
REQUIREMENTS = "requirements"
DESIGN = "design"
DEVELOPMENT = "development"
TESTING = "testing"
DEPLOYMENT = "deployment"
OPERATIONS = "operations"
class SecurityActivity(Enum):
THREAT_MODELING = "threat_modeling"
SECURITY_REQUIREMENTS = "security_requirements"
SECURE_DESIGN_REVIEW = "secure_design_review"
CODE_REVIEW = "code_review"
SAST = "static_analysis"
DAST = "dynamic_analysis"
PENETRATION_TEST = "penetration_test"
SECURITY_ASSESSMENT = "security_assessment"
VULNERABILITY_SCAN = "vulnerability_scan"
SECURITY_MONITORING = "security_monitoring"
@dataclass
class SecurityGate:
gate_id: str
phase: SDLCPhase
required_activities: List[SecurityActivity]
pass_criteria: Dict
blocking: bool = True
@dataclass
class SecurityFinding:
finding_id: str
phase: SDLCPhase
activity: SecurityActivity
severity: str
title: str
description: str
remediation: str
status: str
assigned_to: Optional[str] = None
due_date: Optional[datetime] = None
class SecureSDLC:
"""Manage security activities across SDLC"""
PHASE_ACTIVITIES = {
SDLCPhase.REQUIREMENTS: [
SecurityActivity.SECURITY_REQUIREMENTS,
SecurityActivity.THREAT_MODELING
],
SDLCPhase.DESIGN: [
SecurityActivity.THREAT_MODELING,
SecurityActivity.SECURE_DESIGN_REVIEW
],
SDLCPhase.DEVELOPMENT: [
SecurityActivity.SAST,
SecurityActivity.CODE_REVIEW
],
SDLCPhase.TESTING: [
SecurityActivity.DAST,
SecurityActivity.PENETRATION_TEST,
SecurityActivity.VULNERABILITY_SCAN
],
SDLCPhase.DEPLOYMENT: [
SecurityActivity.SECURITY_ASSESSMENT,
SecurityActivity.VULNERABILITY_SCAN
],
SDLCPhase.OPERATIONS: [
SecurityActivity.SECURITY_MONITORING,
SecurityActivity.VULNERABILITY_SCAN
]
}
def __init__(self):
self.gates = self._create_default_gates()
self.findings: List[SecurityFinding] = []
self.activities_completed = {}
def _create_default_gates(self) -> List[SecurityGate]:
"""Create default security gates"""
return [
SecurityGate(
gate_id="G1",
phase=SDLCPhase.DESIGN,
required_activities=[
SecurityActivity.THREAT_MODELING,
SecurityActivity.SECURITY_REQUIREMENTS
],
pass_criteria={
"threat_model_completed": True,
"security_requirements_defined": True,
"high_risks_addressed": True
},
blocking=True
),
SecurityGate(
gate_id="G2",
phase=SDLCPhase.DEVELOPMENT,
required_activities=[
SecurityActivity.SAST,
SecurityActivity.CODE_REVIEW
],
pass_criteria={
"no_critical_findings": True,
"no_high_findings": True,
"code_review_completed": True
},
blocking=True
),
SecurityGate(
gate_id="G3",
phase=SDLCPhase.TESTING,
required_activities=[
SecurityActivity.DAST,
SecurityActivity.PENETRATION_TEST
],
pass_criteria={
"no_critical_findings": True,
"high_findings_remediated": True,
"pentest_passed": True
},
blocking=True
),
SecurityGate(
gate_id="G4",
phase=SDLCPhase.DEPLOYMENT,
required_activities=[
SecurityActivity.SECURITY_ASSESSMENT
],
pass_criteria={
"all_findings_addressed": True,
"security_signoff": True
},
blocking=True
)
]
def check_gate(self, phase: SDLCPhase) -> Dict:
"""Check if security gate for phase is passed"""
gate = next((g for g in self.gates if g.phase == phase), None)
if not gate:
return {"passed": True, "message": "No gate defined for this phase"}
# Check required activities completed
completed = self.activities_completed.get(phase, [])
missing_activities = [
a for a in gate.required_activities if a not in completed
]
if missing_activities:
return {
"passed": False,
"gate_id": gate.gate_id,
"message": f"Missing activities: {[a.value for a in missing_activities]}",
"blocking": gate.blocking
}
# Check findings criteria
phase_findings = [f for f in self.findings if f.phase == phase]
critical_findings = [f for f in phase_findings if f.severity == "critical" and f.status != "resolved"]
high_findings = [f for f in phase_findings if f.severity == "high" and f.status != "resolved"]
if gate.pass_criteria.get("no_critical_findings") and critical_findings:
return {
"passed": False,
"gate_id": gate.gate_id,
"message": f"{len(critical_findings)} unresolved critical findings",
"blocking": gate.blocking
}
if gate.pass_criteria.get("no_high_findings") and high_findings:
return {
"passed": False,
"gate_id": gate.gate_id,
"message": f"{len(high_findings)} unresolved high findings",
"blocking": gate.blocking
}
return {
"passed": True,
"gate_id": gate.gate_id,
"message": "All criteria met"
}Threat Modeling
STRIDE Threat Model
# threat_modeling.py
from dataclasses import dataclass
from typing import List, Dict
from enum import Enum
class STRIDECategory(Enum):
SPOOFING = "spoofing"
TAMPERING = "tampering"
REPUDIATION = "repudiation"
INFORMATION_DISCLOSURE = "information_disclosure"
DENIAL_OF_SERVICE = "denial_of_service"
ELEVATION_OF_PRIVILEGE = "elevation_of_privilege"
@dataclass
class DataFlow:
flow_id: str
source: str
destination: str
data_type: str
protocol: str
crosses_trust_boundary: bool
@dataclass
class ThreatScenario:
threat_id: str
category: STRIDECategory
affected_component: str
description: str
likelihood: str # low, medium, high
impact: str # low, medium, high
risk_rating: str
mitigations: List[str]
status: str
class ThreatModeler:
"""STRIDE-based threat modeling"""
STRIDE_QUESTIONS = {
STRIDECategory.SPOOFING: [
"Can an attacker pretend to be another user or system?",
"Is authentication properly implemented?",
"Can credentials be intercepted or replayed?"
],
STRIDECategory.TAMPERING: [
"Can data be modified in transit?",
"Can stored data be altered?",
"Is input validation sufficient?"
],
STRIDECategory.REPUDIATION: [
"Can actions be traced to their origin?",
"Is logging comprehensive and tamper-proof?",
"Can users deny performing actions?"
],
STRIDECategory.INFORMATION_DISCLOSURE: [
"Can sensitive data be accessed without authorization?",
"Is data encrypted at rest and in transit?",
"Are error messages revealing too much?"
],
STRIDECategory.DENIAL_OF_SERVICE: [
"Can the system be overwhelmed with requests?",
"Are there rate limits in place?",
"Can resources be exhausted?"
],
STRIDECategory.ELEVATION_OF_PRIVILEGE: [
"Can users gain unauthorized permissions?",
"Is authorization properly enforced?",
"Are there privilege escalation paths?"
]
}
def __init__(self, system_name: str):
self.system_name = system_name
self.components = []
self.data_flows = []
self.threats = []
def add_component(self, component: Dict):
"""Add system component"""
self.components.append(component)
def add_data_flow(self, flow: DataFlow):
"""Add data flow"""
self.data_flows.append(flow)
def analyze_threats(self) -> List[ThreatScenario]:
"""Analyze system for STRIDE threats"""
threats = []
for component in self.components:
for category in STRIDECategory:
threat = self._analyze_component_threat(component, category)
if threat:
threats.append(threat)
for flow in self.data_flows:
if flow.crosses_trust_boundary:
flow_threats = self._analyze_flow_threats(flow)
threats.extend(flow_threats)
self.threats = threats
return threats
def _analyze_component_threat(
self,
component: Dict,
category: STRIDECategory
) -> ThreatScenario:
"""Analyze component for specific threat category"""
component_type = component.get("type", "")
component_name = component.get("name", "")
# Example threat generation logic
threat_templates = {
"web_server": {
STRIDECategory.SPOOFING: "Attacker spoofs legitimate users via session hijacking",
STRIDECategory.TAMPERING: "Request parameters manipulated to alter behavior",
STRIDECategory.DENIAL_OF_SERVICE: "Server overwhelmed by flood of requests"
},
"database": {
STRIDECategory.INFORMATION_DISCLOSURE: "SQL injection exposes sensitive data",
STRIDECategory.TAMPERING: "Unauthorized modification of stored data"
},
"api": {
STRIDECategory.SPOOFING: "API keys compromised or impersonated",
STRIDECategory.ELEVATION_OF_PRIVILEGE: "Broken access control allows unauthorized actions"
}
}
templates = threat_templates.get(component_type, {})
description = templates.get(category)
if description:
return ThreatScenario(
threat_id=f"T-{component_name}-{category.value[:3].upper()}",
category=category,
affected_component=component_name,
description=description,
likelihood="medium",
impact="high",
risk_rating=self._calculate_risk("medium", "high"),
mitigations=[],
status="identified"
)
return None
def _analyze_flow_threats(self, flow: DataFlow) -> List[ThreatScenario]:
"""Analyze data flow crossing trust boundary"""
threats = []
# Data in transit threats
if flow.protocol not in ["https", "tls", "ssh"]:
threats.append(ThreatScenario(
threat_id=f"T-{flow.flow_id}-TAMPER",
category=STRIDECategory.TAMPERING,
affected_component=f"Flow: {flow.source} -> {flow.destination}",
description=f"Data transmitted without encryption via {flow.protocol}",
likelihood="high",
impact="high",
risk_rating="critical",
mitigations=["Implement TLS/HTTPS for all data in transit"],
status="identified"
))
return threats
def _calculate_risk(self, likelihood: str, impact: str) -> str:
"""Calculate risk rating from likelihood and impact"""
matrix = {
("low", "low"): "low",
("low", "medium"): "low",
("low", "high"): "medium",
("medium", "low"): "low",
("medium", "medium"): "medium",
("medium", "high"): "high",
("high", "low"): "medium",
("high", "medium"): "high",
("high", "high"): "critical"
}
return matrix.get((likelihood, impact), "medium")
def generate_report(self) -> Dict:
"""Generate threat model report"""
return {
"system": self.system_name,
"generated_at": datetime.utcnow().isoformat(),
"components": len(self.components),
"data_flows": len(self.data_flows),
"total_threats": len(self.threats),
"threats_by_category": {
cat.value: len([t for t in self.threats if t.category == cat])
for cat in STRIDECategory
},
"threats_by_risk": {
risk: len([t for t in self.threats if t.risk_rating == risk])
for risk in ["critical", "high", "medium", "low"]
},
"threats": [
{
"id": t.threat_id,
"category": t.category.value,
"component": t.affected_component,
"description": t.description,
"risk": t.risk_rating,
"mitigations": t.mitigations
}
for t in self.threats
]
}Secure Coding Standards
Code Security Checker
# secure_coding_checker.py
import re
from dataclasses import dataclass
from typing import List, Dict
@dataclass
class CodeViolation:
rule_id: str
severity: str
line_number: int
description: str
remediation: str
code_snippet: str
class SecureCodingChecker:
"""Check code against secure coding standards"""
def __init__(self, language: str):
self.language = language
self.rules = self._load_rules()
def _load_rules(self) -> List[Dict]:
"""Load language-specific security rules"""
if self.language == "python":
return [
{
"id": "PY-SEC-001",
"severity": "critical",
"pattern": r"eval\s*\(",
"description": "Use of eval() can execute arbitrary code",
"remediation": "Use ast.literal_eval() for safe evaluation or avoid dynamic code execution"
},
{
"id": "PY-SEC-002",
"severity": "critical",
"pattern": r"exec\s*\(",
"description": "Use of exec() can execute arbitrary code",
"remediation": "Avoid dynamic code execution; use safer alternatives"
},
{
"id": "PY-SEC-003",
"severity": "high",
"pattern": r"pickle\.loads?\s*\(",
"description": "Unsafe deserialization with pickle",
"remediation": "Use json for serialization or verify pickle source"
},
{
"id": "PY-SEC-004",
"severity": "high",
"pattern": r"subprocess\.(?:call|run|Popen)\s*\([^)]*shell\s*=\s*True",
"description": "Shell injection vulnerability",
"remediation": "Use shell=False and pass arguments as list"
},
{
"id": "PY-SEC-005",
"severity": "high",
"pattern": r"os\.system\s*\(",
"description": "Command injection via os.system",
"remediation": "Use subprocess with shell=False"
},
{
"id": "PY-SEC-006",
"severity": "medium",
"pattern": r"hashlib\.md5\s*\(",
"description": "MD5 is cryptographically weak",
"remediation": "Use SHA-256 or stronger hash function"
},
{
"id": "PY-SEC-007",
"severity": "critical",
"pattern": r"execute\s*\(\s*[\"'].*%s.*[\"']\s*%",
"description": "SQL injection via string formatting",
"remediation": "Use parameterized queries"
},
{
"id": "PY-SEC-008",
"severity": "high",
"pattern": r"verify\s*=\s*False",
"description": "SSL verification disabled",
"remediation": "Enable SSL certificate verification"
},
{
"id": "PY-SEC-009",
"severity": "medium",
"pattern": r"random\.(random|randint|choice)",
"description": "Insecure random number generator for security purposes",
"remediation": "Use secrets module for security-sensitive randomness"
},
{
"id": "PY-SEC-010",
"severity": "high",
"pattern": r"password\s*=\s*[\"'][^\"']+[\"']",
"description": "Hardcoded password detected",
"remediation": "Use environment variables or secrets management"
}
]
elif self.language == "javascript":
return [
{
"id": "JS-SEC-001",
"severity": "critical",
"pattern": r"eval\s*\(",
"description": "Use of eval() can execute arbitrary code",
"remediation": "Avoid eval(); use JSON.parse() for JSON data"
},
{
"id": "JS-SEC-002",
"severity": "high",
"pattern": r"innerHTML\s*=",
"description": "Potential XSS via innerHTML",
"remediation": "Use textContent or sanitize HTML input"
},
{
"id": "JS-SEC-003",
"severity": "high",
"pattern": r"document\.write\s*\(",
"description": "document.write can introduce XSS",
"remediation": "Use DOM manipulation methods instead"
},
{
"id": "JS-SEC-004",
"severity": "critical",
"pattern": r"new\s+Function\s*\(",
"description": "Dynamic function creation can execute arbitrary code",
"remediation": "Avoid dynamic function creation"
},
{
"id": "JS-SEC-005",
"severity": "medium",
"pattern": r"localStorage\.(setItem|getItem)\s*\([^)]*password",
"description": "Sensitive data stored in localStorage",
"remediation": "Don't store sensitive data in localStorage"
}
]
return []
def check_file(self, content: str, filename: str) -> List[CodeViolation]:
"""Check file content for security violations"""
violations = []
lines = content.split('\n')
for rule in self.rules:
pattern = re.compile(rule["pattern"], re.IGNORECASE)
for line_num, line in enumerate(lines, 1):
if pattern.search(line):
violations.append(CodeViolation(
rule_id=rule["id"],
severity=rule["severity"],
line_number=line_num,
description=rule["description"],
remediation=rule["remediation"],
code_snippet=line.strip()[:100]
))
return violations
def generate_report(self, violations: List[CodeViolation]) -> Dict:
"""Generate security code review report"""
return {
"total_violations": len(violations),
"by_severity": {
severity: len([v for v in violations if v.severity == severity])
for severity in ["critical", "high", "medium", "low"]
},
"violations": [
{
"rule": v.rule_id,
"severity": v.severity,
"line": v.line_number,
"description": v.description,
"fix": v.remediation,
"code": v.code_snippet
}
for v in violations
]
}CI/CD Security Integration
Security Pipeline
# .github/workflows/security-sdlc.yml
name: Security SDLC Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
# Gate 1: Static Analysis
sast:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Semgrep SAST
uses: returntocorp/semgrep-action@v1
with:
config: >-
p/security-audit
p/secrets
p/owasp-top-ten
- name: Run Bandit (Python)
if: hashFiles('**/*.py')
run: |
pip install bandit
bandit -r . -f json -o bandit-results.json || true
- name: Run ESLint Security (JavaScript)
if: hashFiles('**/*.js')
run: |
npm install eslint eslint-plugin-security
npx eslint --ext .js . --format json -o eslint-results.json || true
- name: Check for hardcoded secrets
uses: gitleaks/gitleaks-action@v2
- name: Upload SAST results
uses: actions/upload-artifact@v4
with:
name: sast-results
path: |
*-results.json
# Gate 2: Dependency Check
sca:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Snyk SCA
uses: snyk/actions/node@master
continue-on-error: true
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --severity-threshold=high
- name: OWASP Dependency Check
uses: dependency-check/Dependency-Check_Action@main
with:
project: '${{ github.repository }}'
path: '.'
format: 'JSON'
# Gate 3: Container Security
container-scan:
runs-on: ubuntu-latest
if: hashFiles('Dockerfile')
steps:
- uses: actions/checkout@v4
- name: Build image
run: docker build -t app:${{ github.sha }} .
- name: Run Trivy container scan
uses: aquasecurity/trivy-action@master
with:
image-ref: 'app:${{ github.sha }}'
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH'
- name: Run Hadolint
uses: hadolint/hadolint-action@v3.1.0
with:
dockerfile: Dockerfile
# Security Gate Check
security-gate:
needs: [sast, sca, container-scan]
runs-on: ubuntu-latest
steps:
- name: Download all results
uses: actions/download-artifact@v4
- name: Check security gate
run: |
python scripts/check_security_gate.pySecurity Gate Script
# scripts/check_security_gate.py
import json
import sys
from pathlib import Path
def check_security_gate():
"""Check if security gate passes"""
critical_count = 0
high_count = 0
# Check SAST results
sast_files = Path('sast-results').glob('*.json')
for file in sast_files:
with open(file) as f:
results = json.load(f)
# Parse based on tool format
if 'results' in results:
for finding in results['results']:
severity = finding.get('severity', '').lower()
if severity == 'critical':
critical_count += 1
elif severity == 'high':
high_count += 1
print(f"\nSecurity Gate Results:")
print(f" Critical findings: {critical_count}")
print(f" High findings: {high_count}")
# Gate criteria
if critical_count > 0:
print("\n❌ GATE FAILED: Critical findings must be resolved")
return 1
if high_count > 5:
print(f"\n❌ GATE FAILED: Too many high findings ({high_count} > 5)")
return 1
print("\n✅ GATE PASSED")
return 0
if __name__ == "__main__":
sys.exit(check_security_gate())Summary
Secure SDLC requires security integration at every phase:
- Requirements: Define security requirements and acceptance criteria
- Design: Conduct threat modeling and secure design review
- Development: Apply secure coding standards and SAST
- Testing: Perform DAST, SCA, and penetration testing
- Deployment: Validate security controls before release
- Operations: Continuous monitoring and vulnerability management
Implement security gates to enforce standards and prevent insecure releases. Shift security left to find issues early when they're cheapest to fix.
Is your AI system compliant with the EU AI Act? Free risk assessment - find out in 2 minutes →