Introduction: The Security Paradox
You've written clean code. Your functions are elegant. Your architecture is solid. Yet, your application still has vulnerabilities. Why?
The truth is: Writing good code isn't enough. Modern applications have three attack surfaces that many developers overlook:
- Your own code (logic flaws, injection vulnerabilities)
- Your running application (configuration issues, authentication bypasses)
- Your dependencies (vulnerable third-party libraries)
This is where SAST, DAST, and SCA come in. Think of them as your security trinity—each guarding a different frontier.
The Security Testing Trinity
🔍 SAST (Static Application Security Testing)
What it does: Analyzes your source code without executing it
Tools: SonarQube, Semgrep
Analogy: A code review expert who reads every line looking for security issues
🎯 DAST (Dynamic Application Security Testing)
What it does: Tests your running application like a hacker would
Tools: OWASP ZAP, Nuclei
Analogy: An ethical hacker who attacks your live application to find weaknesses
📦 SCA (Software Composition Analysis)
What it does: Scans your dependencies for known vulnerabilities
Tools: OWASP Dependency Check, Snyk
Analogy: A supply chain inspector checking if any of your imported components are defective
Part 1: SAST with SonarQube
Why Your Code Needs Static Analysis
Even experienced developers write vulnerable code. Here's why:
// This looks innocent, but it's vulnerable to SQL injection
public User getUser(String username) {
String query = "SELECT * FROM users WHERE username = '" + username + "'";
return database.execute(query);
}
A static analyzer catches this immediately and suggests a fix.
Setting Up SonarQube: Step-by-Step
Step 1: Install SonarQube Using Docker
# Pull and run SonarQube
docker run -d --name sonarqube \
-p 9000:9000 \
-e SONAR_ES_BOOTSTRAP_CHECKS_DISABLE=true \
sonarqube:latest
# Wait for SonarQube to start (check logs)
docker logs -f sonarqube
Access: Navigate to http://localhost:9000
Default credentials: admin/admin (change immediately!)
![SonarQube Docker container setup in progress]

Step 2: Create Your First Project
Once SonarQube is running, you'll see the project creation interface:
- Click "Create Project" → "Manually"
- Set a project key (e.g.,
my-secure-app) - Generate a token for authentication
- Choose your build system (Maven, Gradle, npm, etc.)
Step 3: Scan a Java Project
# For Maven projects
mvn clean verify sonar:sonar \
-Dsonar.projectKey=my-secure-app \
-Dsonar.host.url=http://localhost:9000 \
-Dsonar.login=your-token-here
# For Gradle projects
./gradlew sonar \
-Dsonar.projectKey=my-secure-app \
-Dsonar.host.url=http://localhost:9000 \
-Dsonar.login=your-token-here
Step 4: Analyze the Results
SonarQube categorizes issues by severity:
- Blocker: SQL Injection, Command Injection (Fix immediately!)
- Critical: XSS vulnerabilities, Hard-coded credentials
- Major: Weak cryptography, Insecure randomness
- Minor: Code smells that could lead to vulnerabilities
Real-World Example: Fixing a Vulnerability
SonarQube finding: "SQL Injection vulnerability detected"
// ❌ Vulnerable code
public void deleteUser(String userId) {
String sql = "DELETE FROM users WHERE id = " + userId;
jdbcTemplate.execute(sql);
}
// ✅ Fixed code
public void deleteUser(String userId) {
String sql = "DELETE FROM users WHERE id = ?";
jdbcTemplate.update(sql, userId);
}
Pro Tips for SonarQube
- Integrate with CI/CD: Fail builds if security issues exceed a threshold
- Use Quality Gates: Define minimum security standards
- Enable Security Hotspots: Review areas that need manual validation
- Set up branch analysis: Scan feature branches before merging
# Example: GitLab CI integration
sonarqube-check:
image: maven:3.8-openjdk-11
script:
- mvn verify sonar:sonar -Dsonar.projectKey=$CI_PROJECT_NAME
only:
- merge_requests
- main
Part 2: DAST with OWASP ZAP
Why Running Apps Need Dynamic Testing
Static analysis can't catch everything. Consider these scenarios:
- Configuration issues: Exposed admin panels, default passwords
- Authentication bypasses: Session management flaws
- Server misconfigurations: Missing security headers
- Business logic flaws: Password reset vulnerabilities
DAST tests your application as an attacker would—from the outside.
Setting Up OWASP ZAP: Step-by-Step
Step 1: Set Up a Test Target
For demonstration purposes, we'll use OWASP Juice Shop (a deliberately vulnerable application):
# Run Juice Shop as our test target
docker run -d --name juiceshop -p 3000:3000 bkimminich/juice-shop
Step 2: Install and Run OWASP ZAP
# Using Docker (recommended)
docker pull zaproxy/zap-stable
# Run ZAP in daemon mode (headless)
docker run -u zap -p 8080:8080 -i zaproxy/zap-stable \
zap.sh -daemon -host 0.0.0.0 -port 8080 \
-config api.addrs.addr.name=.* \
-config api.addrs.addr.regex=true \
-config api.key=your-api-key-here
# Or download the desktop version from https://www.zaproxy.org/download/
Step 3: Run a Baseline Scan (Quickest Way to Start)
Create a scan script to automate the process:
#!/usr/bin/env bash
# Get local IP so Docker container can reach the target
TARGET_IP=$(hostname -I | awk '{print $1}')
TARGET_URL="http://$TARGET_IP:3000"
echo "Starting Security Scan on: $TARGET_URL"
# Create a folder for reports
mkdir -p zap-reports
# Run ZAP Baseline Scan
docker run --rm -v $(pwd)/zap-reports:/zap/wrk/:rw \
ghcr.io/zaproxy/zaproxy:stable zap-baseline.py \
-t $TARGET_URL \
-r juice_shop_report.html
echo "Scan Complete. Report generated in zap-reports/juice_shop_report.html"
Step 4: Understanding ZAP Scan Results
When you run the scan, you'll see output categorized as:
- PASS: Checks that didn't find vulnerabilities (good!)
- WARN-NEW: New warnings detected
- FAIL-NEW: Critical failures that need immediate attention
Common findings include:
- Deprecated Feature Policy Header Set [10063]
- Timestamp Disclosure - Unix [10096]
- Cross-Domain Misconfiguration [10098]
- Modern Web Application [10109]
- Dangerous JS Functions [10110]
- Insufficient Site Isolation Against Spectre Vulnerability [90004]
Step 5: Advanced Scan with API (Automation)
For more control, use the ZAP API:
# Set your API key
ZAP_API_KEY="your-api-key-here"
TARGET="http://localhost:3000"
# Start a spider scan
curl "http://localhost:8080/JSON/spider/action/scan/?url=$TARGET&apikey=$ZAP_API_KEY"
# Wait for spider to complete (poll status)
while : ; do
PROGRESS=$(curl -s "http://localhost:8080/JSON/spider/view/status/?apikey=$ZAP_API_KEY" | jq -r '.status')
echo "Spider progress: $PROGRESS%"
[ "$PROGRESS" = "100" ] && break
sleep 5
done
# Start active scan
SCAN_ID=$(curl -s "http://localhost:8080/JSON/ascan/action/scan/?url=$TARGET&apikey=$ZAP_API_KEY" | jq -r '.scan')
# Wait for active scan to complete
while : ; do
STATUS=$(curl -s "http://localhost:8080/JSON/ascan/view/status/?scanId=$SCAN_ID&apikey=$ZAP_API_KEY" | jq -r '.status')
echo "Active scan progress: $STATUS%"
[ "$STATUS" = "100" ] && break
sleep 10
done
# Generate HTML report
curl "http://localhost:8080/OTHER/core/other/htmlreport/?apikey=$ZAP_API_KEY" > zap_report.html
Pro Tips for OWASP ZAP
- Use ZAP in CI/CD: Automate scans on every deployment before production
- Configure scan policies: Disable checks that cause false positives
- Set up proxy mode: Manually browse your app through ZAP to build a better sitemap
- Use ZAP scripts: Extend functionality with custom scripts
- Start with baseline scans: Quick feedback, then move to full active scans
Part 3: SCA with OWASP Dependency Check
Why Your Dependencies Are Your Weakest Link
Consider this: The average application has 200+ dependencies. Each one is a potential vulnerability.
Famous incidents:
- Equifax breach (2017): Unpatched Apache Struts vulnerability
- Log4Shell (2021): Log4j vulnerability affected millions of applications
You didn't write the vulnerable code, but you're still responsible for it.
Setting Up OWASP Dependency Check: Step-by-Step
Step 1: Install OWASP Dependency Check
# Download the latest release
wget https://github.com/jeremylong/DependencyCheck/releases/download/v9.0.9/dependency-check-9.0.9-release.zip
# Unzip
unzip dependency-check-9.0.9-release.zip
# Add to PATH (optional)
export PATH=$PATH:$(pwd)/dependency-check/bin
Step 2: Get NVD API Key
The National Vulnerability Database (NVD) requires an API key for faster, reliable scanning.
- Go to https://nvd.nist.gov/developers/request-an-api-key
- Fill out the form with your email
- Check your email for the API key
- Store it securely
Why you need it: Without an API key, scans are rate-limited and take 10x longer.
Step 3: Run Your First Scan
# Basic scan
dependency-check.sh \
--project "My Demo project" \
--scan ./target \
--out ./report \
--format HTML \
--format JSON \
--format XML \
--nvdApiKey 5b2b7320-exxf-4b1e-95xx-bdxxxxxxxxxx \
--failOnCVSS 7 \
--enableExperimental
Key parameters explained:
-
--project: Name of your project (appears in reports) -
--scan: Directory to scan (e.g.,./target,./node_modules) -
--out: Where to save reports -
--format: Output format (HTML for viewing, JSON for CI/CD) -
--nvdApiKey: Your NVD API key (critical for performance) -
--failOnCVSS 7: Fail the build if vulnerabilities with CVSS score ≥ 7 are found -
--enableExperimental: Enable experimental analyzers for better coverage
Step 4: Scan Different Project Types
For Java/Maven Projects:
dependency-check.sh \
--project "My Java App" \
--scan ./target \
--format HTML \
--nvdApiKey YOUR_NVD_API_KEY_HERE
For Node.js Projects:
# Scan node_modules directory
dependency-check.sh \
--project "My Node App" \
--scan ./node_modules \
--format HTML \
--nvdApiKey YOUR_NVD_API_KEY_HERE
# Or use npm audit (built-in)
npm audit
# Generate detailed report
npm audit --json > npm-audit-report.json
Step 5: Analyze the Report
The report shows vulnerabilities with:
- CVE ID: Unique identifier (e.g., CVE-2023-12345)
- CVSS Score: Severity (0-10, where 7+ is critical)
- Description: What the vulnerability does
- Recommendation: How to fix it
Example vulnerability:
CVE-2023-12345 | CVSS: 9.8 (Critical)
Component: jackson-databind 2.9.8
Description: Deserialization vulnerability allows remote code execution
Recommendation: Update to version 2.15.0 or higher
Real-World Example: Fixing a Dependency Vulnerability
Finding: Log4j vulnerability (CVE-2021-44228)
<!-- ❌ Vulnerable dependency in pom.xml -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.14.1</version>
</dependency>
<!-- ✅ Fixed: Updated to patched version -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.17.1</version>
</dependency>
For Node.js:
# Find vulnerable packages
npm audit
# Auto-fix (when possible)
npm audit fix
# Force update breaking changes if necessary
npm audit fix --force
# Or update specific package
npm update log4js@latest
Pro Tips for OWASP Dependency Check
- Always use the NVD API key: Scans are 10x faster
- Set a CVSS threshold: Fail builds on critical vulnerabilities (7+)
- Suppress false positives: Create a suppression file for known safe cases
- Schedule regular scans: Run daily or with every build
- Update regularly: Keep the vulnerability database fresh
- Use multiple formats: HTML for viewing, JSON for automation
Common Pitfalls and How to Avoid Them
Pitfall 1: "Too many false positives!"
Solution:
- Configure your tools properly
- Use suppression files for known safe cases
- Don't ignore everything - triage issues properly
- Start with high-severity issues first
Pitfall 2: "Scans take too long!"
Solution:
- Use incremental analysis (scan only changed code)
- Run full scans nightly, quick scans on every commit
- Use the NVD API key for dependency checks
- Cache dependency databases
Pitfall 3: "Developers ignore security reports"
Solution:
- Make security visible (dashboards, Slack notifications)
- Fail builds on critical issues
- Provide clear remediation guidance
- Train developers on common vulnerabilities
- Gamify security fixes (track improvements)
Pitfall 4: "We fixed the code but still got hacked"
Solution:
- Use all three tools - they complement each other
- Test in production-like environments
- Consider manual penetration testing for critical apps
- Regularly update your tools and databases
Conclusion: Security is a Journey, Not a Destination
You can't prevent every breach, but you can make your application a much harder target. By implementing SAST, DAST, and SCA:
- You catch 94% of vulnerabilities before production
- You reduce incident response time from months to days
- You build a security-first culture in your team
- You gain peace of mind knowing your code is continuously monitored
Remember: The best time to start was yesterday. The second best time is now.
Additional Resources
Official Documentation:
Practice Targets:
juice-shop
/
juice-shop
OWASP Juice Shop: Probably the most modern and sophisticated insecure web application
The most trustworthy online shop out there. (@dschadow) — The best juice shop on the whole internet! (@shehackspurple) — Actually the most bug-free vulnerable application in existence! (@vanderaj) — First you 😂😂then you 😢 (@kramse) — But this doesn't have anything to do with juice. (@coderPatros' wife)
OWASP Juice Shop is probably the most modern and sophisticated insecure web application! It can be used in security trainings, awareness demos, CTFs and as a guinea pig for security tools! Juice Shop encompasses vulnerabilities from the entire OWASP Top Ten along with many other security flaws found in real-world applications!
For a detailed introduction, full list of features and architecture overview please visit the official project page https://owasp-juice.shop
Table of contents
Let's Build Secure Software Together
💬 What's your biggest security challenge? Share in the comments below, and let's build more secure applications together.
Got questions about implementing these tools? Drop them in the comments—I'll respond to every single one.
Found this helpful? Share it with your team and help spread security awareness!
Connect with me:
📧 Email me


Top comments (0)