Introduction to Nessus
Nessus is one of the most widely deployed vulnerability assessment tools in the world. Developed by Tenable, it serves as an automated vulnerability scanner designed to identify security weaknesses, misconfigurations, and compliance gaps across a wide range of assets—servers, network devices, cloud instances, endpoints, and even containerized environments. For developers and security engineers, Nessus acts as a continuous feedback loop between code deployment and infrastructure hardening, ensuring that every layer of the stack is assessed against a constantly updated database of over 80,000 vulnerability plugins.
Why Nessus Matters for Developers
Vulnerability management is no longer the exclusive domain of dedicated security teams. With the rise of DevSecOps, developers are increasingly responsible for the security posture of the systems they build and deploy. Nessus provides a practical bridge: it translates abstract CVEs into actionable findings tied directly to your environment. It matters because:
- Shift-left security – Nessus can be integrated into CI/CD pipelines to scan staging environments before production rollout.
- Compliance automation – It includes policies for PCI DSS, HIPAA, CIS benchmarks, and more, turning manual compliance checks into scheduled scans.
- Prioritized remediation – Findings are ranked by severity (Critical, High, Medium, Low, Informational), with exploitability context such as public exploit availability or malware associations.
- API-driven extensibility – The REST API allows developers to trigger scans, retrieve results, and build custom dashboards programmatically.
Installation and Setup
Nessus is available in two primary editions: Nessus Professional (for commercial use) and Nessus Essentials (free for limited, non-commercial use). Both share the same engine but differ in asset limits and feature access. This tutorial covers setup on a Linux server, which is the most common deployment target for development and production scanning infrastructure.
System Requirements
Before installation, ensure your host meets these minimums:
- 4 GB RAM (8 GB recommended for large scan jobs)
- 4 CPU cores (more cores reduce scan duration linearly)
- 30 GB disk space for the application and plugin database
- Supported OS: RHEL/CentOS 7+, Ubuntu 20.04+, Debian 11+, or macOS for local testing
Downloading and Installing Nessus
First, register for an activation code at https://www.tenable.com/products/nessus/nessus-essentials (for Essentials) or purchase a Professional license. Then download the appropriate package for your distribution. Here's a complete walkthrough for Ubuntu 22.04 LTS:
# Update the system and install dependencies
sudo apt update && sudo apt upgrade -y
sudo apt install -y curl net-tools
# Download Nessus (check Tenable's site for the latest version number)
curl -O https://www.tenable.com/downloads/api/v2/pages/nessus/files/Nessus-latest-debian_amd64.deb
# Install the package
sudo dpkg -i Nessus-latest-debian_amd64.deb
# Fix any missing dependencies
sudo apt install -f -y
# Start the Nessus service
sudo systemctl start nessusd.service
sudo systemctl enable nessusd.service
# Verify the service is running
sudo systemctl status nessusd.service
For RHEL-based distributions, the process is nearly identical, using the .rpm package variant:
# Download the RPM package
curl -O https://www.tenable.com/downloads/api/v2/pages/nessus/files/Nessus-latest-rhel-8.x86_64.rpm
# Install
sudo rpm -ivh Nessus-latest-rhel-8.x86_64.rpm
# Start and enable
sudo systemctl start nessusd.service
sudo systemctl enable nessusd.service
Initial Web-Based Activation
Once the service is running, Nessus binds its web interface to port 8834 by default. Navigate to https://<your-server-ip>:8834 in a browser. You'll encounter a self-signed certificate warning—this is expected and can be resolved later by configuring a trusted certificate. Follow the on-screen wizard:
- Select your edition (Essentials, Professional, or Manager).
- Enter your activation code.
- Create an administrator account (username/password).
- Wait for plugin compilation—this can take 10–20 minutes as Nessus downloads and compiles the latest vulnerability plugins.
You can monitor plugin compilation progress via the CLI:
# Check compilation status
sudo /opt/nessus/sbin/nessuscli status
# View detailed logs
tail -f /opt/nessus/var/nessus/logs/nessusd.messages
Core Configuration
With Nessus installed and activated, the next step is configuring it to match your environment's requirements. Configuration spans network settings, scan policies, credentials, and scheduling.
Configuring Network Settings via nessuscli
While most settings are available through the web UI, the nessuscli utility provides programmatic control ideal for automated provisioning scripts:
# Set a custom proxy (if scans must traverse a corporate proxy)
sudo /opt/nessus/sbin/nessuscli fix --set proxy=proxy.corp.local:8080
sudo /opt/nessus/sbin/nessuscli fix --set proxy_username=svc_nessus
sudo /opt/nessus/sbin/nessuscli fix --set proxy_password='SecurePass!23'
# Change the web interface port from 8834 to a custom port
sudo /opt/nessus/sbin/nessuscli fix --set www_port=8443
# Restrict Nessus to listen only on a specific interface
sudo /opt/nessus/sbin/nessuscli fix --set listen_address=10.0.1.25
# Apply all changes with a service restart
sudo systemctl restart nessusd.service
Managing Custom SSL Certificates
The default self-signed certificate causes browser trust warnings and can complicate API integrations. Replace it with a valid certificate signed by your internal CA or a public provider:
# Replace the self-signed certificate with your own
sudo /opt/nessus/sbin/nessuscli mkcert-client
# Or manually overwrite the certificate files
sudo cp /path/to/your/server.crt /opt/nessus/var/nessus/cert/server-cert.pem
sudo cp /path/to/your/server.key /opt/nessus/var/nessus/cert/server-key.pem
sudo systemctl restart nessusd.service
Creating Scan Policies
Scan policies define the depth, scope, and safety of a scan. Rather than relying solely on the built-in templates, developers should create custom policies tailored to specific environments. A policy is essentially a configuration object specifying which plugin families to use, which ports to target, and how aggressive the scan should be.
In the web UI, navigate to Policies → New Policy. For advanced customization, you can also work with the policy XML directly through the API. Below is an example of a policy payload for a safe, non-disruptive web application scan:
// Example policy configuration JSON for Nessus API
{
"name": "Safe Web App Scan - Staging",
"description": "Non-intrusive scan for staging web servers",
"settings": {
"scan_type": "custom",
"policy_id": -1,
"basic_scan_info": {
"scan_name": "staging-web-scan",
"scan_targets": "192.168.50.0/24"
},
"advanced": {
"scan_plugins_ordering": "sequential",
"max_checks_per_host": 5,
"max_hosts": 20,
"safe_checks": "yes",
"stop_unreachable": "yes"
}
},
"plugins": {
"families": [
"Web Servers",
"CGI Abuses",
"Databases",
"Service Detection"
],
"individual": [87254, 84502]
}
}
Running Vulnerability Scans
Nessus supports multiple scan initiation methods: interactive via the web UI, scheduled recurring scans, and programmatic triggers through the REST API. For developer workflows, the API approach is the most powerful, enabling integration into CI/CD systems, chat ops, and custom orchestration.
Basic Scan via the Web UI
For ad-hoc scans, the web interface is straightforward. Navigate to Scans → New Scan, select a template (e.g., "Basic Network Scan"), define targets as IP ranges, CIDR blocks, or hostnames, choose your custom policy, and launch. Results appear in real-time as hosts are enumerated and tested.
Automated Scanning with the Nessus REST API
The Nessus REST API is the cornerstone of developer integration. It uses token-based authentication and exposes endpoints for scan management, result retrieval, and system administration. All API calls require the X-API-Token header or session-based authentication.
Step 1: Obtain an API token
You can generate API keys via the web UI under Settings → API Keys, or you can authenticate with username/password to obtain a session token:
#!/bin/bash
# Authenticate and extract a session token
NESSUS_URL="https://nessus.corp.local:8834"
USERNAME="admin"
PASSWORD='YourSecurePassword!'
TOKEN=$(curl -k -X POST "${NESSUS_URL}/session" \
-H "Content-Type: application/json" \
-d "{\"username\":\"${USERNAME}\",\"password\":\"${PASSWORD}\"}" \
-s | jq -r '.token')
echo "Session token: ${TOKEN}"
Step 2: Launch a scan
With a valid token, you can list available policies, define scan targets, and trigger a scan job:
#!/bin/bash
# Launch a scan using a pre-defined policy
TOKEN="your-session-token-here"
NESSUS_URL="https://nessus.corp.local:8834"
POLICY_ID="42" # Retrieve policy IDs via GET /policies
# Define the scan payload
SCAN_PAYLOAD=$(cat <
Step 3: Monitor scan progress
Poll the scan status endpoint until the scan completes:
#!/bin/bash
# Poll scan status until completion
SCAN_ID="123"
TOKEN="your-token"
NESSUS_URL="https://nessus.corp.local:8834"
while true; do
STATUS=$(curl -k -X GET "${NESSUS_URL}/scans/${SCAN_ID}" \
-H "X-API-Token: ${TOKEN}" -s | jq -r '.info.status')
echo "Scan ${SCAN_ID} status: ${STATUS}"
if [ "${STATUS}" == "completed" ] || [ "${STATUS}" == "stopped" ] || [ "${STATUS}" == "aborted" ]; then
break
fi
sleep 30
done
echo "Scan finished with status: ${STATUS}"
Step 4: Export and process results
Once completed, export findings in a structured format for downstream processing:
#!/bin/bash
# Export scan results as a Nessus XML report, then convert to JSON
SCAN_ID="123"
TOKEN="your-token"
NESSUS_URL="https://nessus.corp.local:8834"
# Request an export (chapters control which sections are included)
EXPORT_PAYLOAD='{
"format": "nessus",
"chapters": "vuln_hosts_summary,vuln_by_host,vuln_detail,compliance_exec_summary",
"filter": "severity_level=critical,high,medium"
}'
FILE_ID=$(curl -k -X POST "${NESSUS_URL}/scans/${SCAN_ID}/export" \
-H "Content-Type: application/json" \
-H "X-API-Token: ${TOKEN}" \
-d "${EXPORT_PAYLOAD}" -s | jq -r '.file')
echo "Export file ID: ${FILE_ID}"
# Poll until the export is ready for download
while true; do
READY=$(curl -k -X GET "${NESSUS_URL}/scans/${SCAN_ID}/export/${FILE_ID}/status" \
-H "X-API-Token: ${TOKEN}" -s | jq -r '.status')
if [ "${READY}" == "ready" ]; then
break
fi
sleep 5
done
# Download the report
curl -k -X GET "${NESSUS_URL}/scans/${SCAN_ID}/export/${FILE_ID}/download" \
-H "X-API-Token: ${TOKEN}" -o "scan_report_${SCAN_ID}.nessus"
echo "Report downloaded to scan_report_${SCAN_ID}.nessus"
Integrating with CI/CD Pipelines
A common pattern is to trigger a Nessus scan after a deployment to a staging environment, then fail the pipeline if critical vulnerabilities are detected. Here's a simplified GitLab CI example:
# .gitlab-ci.yml snippet
staging-vulnerability-scan:
stage: security
script:
- echo "Triggering Nessus scan against staging..."
- |
TOKEN=$(curl -k -X POST "${NESSUS_URL}/session" \
-H "Content-Type: application/json" \
-d "{\"username\":\"${NESSUS_USER}\",\"password\":\"${NESSUS_PASS}\"}" -s | jq -r '.token')
SCAN_ID=$(curl -k -X POST "${NESSUS_URL}/scans" \
-H "Content-Type: application/json" \
-H "X-API-Token: ${TOKEN}" \
-d "{\"settings\":{\"name\":\"CI Staging Scan\",\"policy_id\":42,\"text_targets\":\"${STAGING_HOST}\"}}" -s | jq -r '.scan.id')
curl -k -X POST "${NESSUS_URL}/scans/${SCAN_ID}/launch" -H "X-API-Token: ${TOKEN}" -s
- sleep 600 # Wait 10 minutes for scan completion (or implement polling)
- |
# Retrieve critical/high count
VULN_COUNT=$(curl -k -X GET "${NESSUS_URL}/scans/${SCAN_ID}" \
-H "X-API-Token: ${TOKEN}" -s | jq '.info.severity_counts.critical + .info.severity_counts.high')
- echo "Critical/High vulnerabilities found: ${VULN_COUNT}"
- if [ "${VULN_COUNT}" -gt 0 ]; then exit 1; fi
allow_failure: false
Understanding Scan Results
Nessus findings are structured hierarchically: each scan contains one or more host records, each host contains vulnerability instances, and each vulnerability references a plugin ID with detailed remediation guidance. Understanding this structure is essential for building effective automation around Nessus.
Vulnerability Severity and Exploitability
Each finding carries a severity level and, crucially, a "Vulnerability Priority Rating" (VPR) in Professional editions. The VPR combines CVSS score with exploit code maturity, malware associations, and report prevalence to give a more actionable ranking than CVSS alone. Developers should prioritize findings with high VPR values over raw CVSS scores, as VPR better reflects real-world risk.
Parsing Nessus XML Programmatically
When you export results in .nessus format, you get an XML document that can be parsed with standard libraries. Here's a Python example that extracts all critical findings across hosts:
import xml.etree.ElementTree as ET
import json
def parse_nessus_report(filepath):
"""Extract critical and high vulnerabilities from a .nessus export."""
tree = ET.parse(filepath)
root = tree.getroot()
findings = []
for report_host in root.findall('.//ReportHost'):
host_ip = report_host.get('name')
hostname = report_host.find('.//tag[@name="hostname"]')
hostname_value = hostname.text if hostname is not None else host_ip
for report_item in report_host.findall('.//ReportItem'):
severity = report_item.get('severity', '0')
plugin_id = report_item.get('pluginID')
plugin_name = report_item.get('pluginName')
# Only collect critical (4) and high (3) severity
if int(severity) >= 3:
description = report_item.find('description')
solution = report_item.find('solution')
cvss_vector = report_item.find('cvss_vector')
finding = {
'host': hostname_value,
'ip': host_ip,
'plugin_id': plugin_id,
'plugin_name': plugin_name,
'severity': severity,
'description': description.text if description is not None else '',
'solution': solution.text if solution is not None else '',
'cvss_vector': cvss_vector.text if cvss_vector is not None else ''
}
findings.append(finding)
return findings
# Usage
results = parse_nessus_report('scan_report_123.nessus')
print(json.dumps(results, indent=2))
print(f"Total critical/high findings: {len(results)}")
Working with the JSON Export Format
For newer integrations, request JSON export directly via the API by setting "format": "json" in the export payload. This avoids XML parsing overhead and integrates cleanly with modern data pipelines:
// Python snippet to fetch and process JSON export
import requests
NESSUS_URL = "https://nessus.corp.local:8834"
TOKEN = "your-api-token"
SCAN_ID = "123"
headers = {"X-API-Token": TOKEN, "Content-Type": "application/json"}
# Request JSON export
export_payload = {"format": "json", "chapters": "vuln_by_host"}
resp = requests.post(f"{NESSUS_URL}/scans/{SCAN_ID}/export",
headers=headers, json=export_payload, verify=False)
file_id = resp.json()['file']
# Download when ready (polling omitted for brevity)
download_resp = requests.get(
f"{NESSUS_URL}/scans/{SCAN_ID}/export/{file_id}/download",
headers=headers, verify=False
)
vulnerability_data = download_resp.json()
# Group vulnerabilities by severity
from collections import defaultdict
by_severity = defaultdict(list)
for host in vulnerability_data.get('hosts', []):
for vuln in host.get('vulnerabilities', []):
by_severity[vuln['severity']].append({
'host': host['hostname'],
'plugin': vuln['plugin_name'],
'solution': vuln.get('solution', 'N/A')
})
print(f"Critical: {len(by_severity['critical'])}")
print(f"High: {len(by_severity['high'])}")
print(f"Medium: {len(by_severity['medium'])}")
Automation and API Integration Patterns
Beyond simple scan triggers, the Nessus API enables sophisticated workflows that treat vulnerability data as a first-class citizen in your development lifecycle.
Webhook-Driven Remediation Tracking
You can build a service that listens for scan completions and automatically creates tickets in Jira, ServiceNow, or GitHub Issues for each critical finding:
# Python Flask webhook receiver example
from flask import Flask, request
import requests
import os
app = Flask(__name__)
NESSUS_URL = os.environ.get("NESSUS_URL")
API_TOKEN = os.environ.get("NESSUS_API_TOKEN")
JIRA_URL = os.environ.get("JIRA_URL")
@app.route('/webhook/scan-complete', methods=['POST'])
def scan_complete():
data = request.json
scan_id = data.get('scan_id')
# Fetch results from Nessus
headers = {"X-API-Token": API_TOKEN}
scan_info = requests.get(
f"{NESSUS_URL}/scans/{scan_id}",
headers=headers, verify=False
).json()
critical_count = scan_info['info']['severity_counts'].get('critical', 0)
if critical_count > 0:
# Create a Jira ticket
jira_payload = {
"fields": {
"project": {"key": "SEC"},
"summary": f"Nessus Scan {scan_id}: {critical_count} Critical Findings",
"description": f"Automated scan detected {critical_count} critical vulnerabilities.\nScan ID: {scan_id}\nReview at: {NESSUS_URL}/#/scans/{scan_id}",
"issuetype": {"name": "Bug"}
}
}
requests.post(
f"{JIRA_URL}/rest/api/2/issue",
json=jira_payload,
auth=('jira_user', os.environ.get('JIRA_PASS'))
)
return {"status": "processed"}, 200
if __name__ == '__main__':
app.run(port=5000)
Scheduled Scan Orchestration with Cron and API
While Nessus has built-in scheduling, external orchestration via cron allows you to coordinate scans with maintenance windows, backup completion, or deployment events:
#!/bin/bash
# /etc/cron.d/nessus-orchestration
# Runs every night at 2 AM after backup window closes
0 2 * * * root /usr/local/bin/nessus-scheduled-scan.sh
# --- nessus-scheduled-scan.sh ---
#!/bin/bash
TOKEN=$(curl -k -X POST "${NESSUS_URL}/session" \
-H "Content-Type: application/json" \
-d "{\"username\":\"${NESSUS_USER}\",\"password\":\"${NESSUS_PASS}\"}" -s | jq -r '.token')
# Scan production subnet
curl -k -X POST "${NESSUS_URL}/scans" \
-H "Content-Type: application/json" \
-H "X-API-Token: ${TOKEN}" \
-d '{
"settings": {
"name": "Prod-Nightly-Scan",
"policy_id": 5,
"text_targets": "10.0.100.0/24",
"launch": "ONETIME"
}
}' -s | jq '.scan.id' > /tmp/current_scan_id
# Log to syslog
logger -t nessus-orchestration "Launched nightly scan with ID: $(cat /tmp/current_scan_id)"
Best Practices
Adopting Nessus effectively requires more than just running scans. The following best practices will help you maximize detection accuracy, minimize operational disruption, and integrate vulnerability management seamlessly into your development lifecycle.
1. Tune Scan Policies to the Environment
Never run the default "All Plugins" policy against production without careful consideration. Aggressive plugins can overwhelm services, trigger IDS alerts, or cause performance degradation. Segment your scanning policies by asset type:
- Web Servers – Enable web-focused plugins (CGI, OWASP checks), disable database brute-force plugins.
- Database Servers – Include database-specific families, but avoid web application tests.
- Network Infrastructure – Focus on service detection, missing patches, and configuration audits.
- Staging/Development – You can safely run more aggressive plugins here, including credentialed checks with full root access.
2. Use Credentialed Scanning Wherever Possible
Uncredentialed scans see only what an attacker sees from the outside. Credentialed scans log into each target (via SSH, SMB, or agent) and enumerate installed packages, file permissions, registry entries, and local configurations. This increases finding accuracy by 40–60% and dramatically reduces false negatives. Store credentials securely within Nessus's encrypted credential vault or retrieve them at runtime via a secrets manager integration.
3. Implement Rate Limiting and Scan Windows
Production scans should be throttled to avoid saturating network links or CPU on critical systems. Configure these parameters in your scan policy:
// Advanced scan settings for production safety
{
"advanced": {
"max_checks_per_host": 3,
"max_hosts": 10,
"min_time_between_scans": 15,
"scan_delay": 30,
"parallel_scans": 1,
"safe_checks": "yes",
"stop_unreachable": "yes",
"scan_timeout": 3600
}
}
4. Treat Findings as Code
Version your Nessus scan policies and export scripts alongside your application code. Use infrastructure-as-code principles to define what a "clean" scan looks like, and gate deployments on scan results. Store policy JSON definitions in your repository and push them to Nessus via the API as part of your provisioning pipeline.
5. Deduplicate and Suppress Intelligently
Not every finding requires immediate action. Use Nessus's built-in "Accepted Risk" and "False Positive" marking capabilities to suppress findings that have been reviewed and deemed acceptable. This keeps your vulnerability dashboard actionable and prevents alert fatigue. For API-driven workflows, maintain a suppression list in your codebase that filters known false positives before generating tickets:
# Example suppression list used in post-processing
SUPPRESSED_PLUGINS = {
'11213': 'Internal IP disclosure - expected in private subnet',
'42263': 'Self-signed cert on internal dev endpoint',
'57608': 'HTTP TRACE method enabled - required for legacy app'
}
def filter_suppressed(findings):
return [f for f in findings if f['plugin_id'] not in SUPPRESSED_PLUGINS]
6. Monitor Plugin Freshness
Nessus plugins are updated daily. A scanner with outdated plugins misses newly disclosed vulnerabilities. Automate a health check that verifies plugin age:
#!/bin/bash
# Check plugin freshness and alert if older than 48 hours
TOKEN=$(curl -k -X POST "${NESSUS_URL}/session" \
-H "Content-Type: application/json" \
-d "{\"username\":\"${NESSUS_USER}\",\"password\":\"${NESSUS_PASS}\"}" -s | jq -r '.token')
PLUGIN_DATE=$(curl -k -X GET "${NESSUS_URL}/server/properties" \
-H "X-API-Token: ${TOKEN}" -s | jq -r '.plugin_set_timestamp')
# Convert timestamp to epoch and compare
PLUGIN_EPOCH=$(date -d "${PLUGIN_DATE}" +%s)
NOW_EPOCH=$(date +%s)
AGE_HOURS=$(( (NOW_EPOCH - PLUGIN_EPOCH) / 3600 ))
if [ ${AGE_HOURS} -gt 48 ]; then
echo "WARNING: Nessus plugins are ${AGE_HOURS} hours old. Triggering update..."
curl -k -X POST "${NESSUS_URL}/server/plugins/update" \
-H "X-API-Token: ${TOKEN}" -s
fi
7. Segment Scanning Infrastructure
Place your Nessus scanner on a dedicated management VLAN with direct network paths to all target subnets. Ensure firewall rules allow the scanner's source IP to reach target ports but restrict the scanner's own management interface (port 8834) to a secure jump host or VPN. Never expose the Nessus web interface directly to the internet.
8. Integrate with SIEM and Logging Platforms
Forward scan results to your SIEM (e.g., Splunk, Elastic) for correlation with runtime events. A critical vulnerability combined with an active intrusion detection alert becomes an actionable incident. Use the export API to push structured JSON to your SIEM via HTTP Event Collector or log file ingestion.
Conclusion
Nessus is far more than a checkbox compliance scanner—it is a foundational tool for developers and engineers committed to building resilient systems. By mastering its installation, configuring precise scan policies, leveraging the REST API for pipeline automation, and adopting the best practices outlined here, you transform vulnerability assessment from a periodic audit into a continuous, code-driven feedback mechanism. The combination of Nessus's deep plugin coverage with developer-friendly API extensibility makes it an ideal fit for modern DevSecOps workflows. Start with a single staged integration—perhaps a nightly scan of your staging environment with results posted to your team's Slack channel—and iterate toward fully automated, policy-gated deployments that refuse to promote code when critical vulnerabilities are present. In a landscape where new vulnerabilities emerge daily, this proactive posture is not optional; it's essential.