June 13, 2026
Building a Beginner-Friendly Vulnerability Scanner in Python
Introduction
Tony Kahare
4 min read
Introduction
As an aspiring cybersecurity professional, one of the best ways to develop practical skills is by building simple security tools. While enterprise vulnerability scanners such as Nmap, Nessus, and OpenVAS provide extensive functionality, understanding the underlying concepts is essential before progressing to advanced tools.
To strengthen my Python programming skills and gain hands-on experience with cybersecurity automation, I developed a beginner-friendly vulnerability scanner. The project focuses on several fundamental security assessment tasks:
- Verifying website availability
- Scanning common network ports
- Identifying missing security headers
- Generating a simple risk score
- Producing a vulnerability report
This article walks through the development process, explaining both the Python concepts and cybersecurity principles involved.
Project Objectives
The primary goal of the project was to create a lightweight vulnerability scanner capable of:
- Accepting a target website from the user.
- Verifying that the website is reachable.
- Scanning common ports for exposed services.
- Inspecting important HTTP security headers.
- Assigning a simple risk score based on findings.
- Producing a text-based report for future reference.
By the end of the project, I had a working cybersecurity automation tool that demonstrates networking, web security, and Python programming fundamentals.
Prerequisites
Before starting, ensure you have:
- Python 3 installed
- A code editor (Visual Studio Code recommended)
- Basic understanding of Python variables, loops, and functions
- An internet connection
Install the required Python package:
pip install requestspip install requestsThe Requests library simplifies sending HTTP requests and retrieving website information.
Understanding the Scanner Architecture
The scanner consists of five main stages:
User Input (Web URL)
↓
Website Connectivity Check
↓
Port Scanning
↓
Security Header Analysis
↓
Risk Scoring & Report Generation & SavingUser Input (Web URL)
↓
Website Connectivity Check
↓
Port Scanning
↓
Security Header Analysis
↓
Risk Scoring & Report Generation & SavingEach stage performs a specific security assessment and contributes to the overall vulnerability report.
Step 1: Accepting User Input
The first step is obtaining the target website.
target = input("Enter website (example: https://www.google.com): ")target = input("Enter website (example: https://www.google.com): ")This allows the user to specify the website that will be assessed.
Example:
https://www.google.comhttps://www.google.comStep 2: Extracting the Domain Name
Websites contain multiple components such as protocols, paths, and query strings. For port scanning, we only need the domain name.
from urllib.parse import urlparse
parsed_url = urlparse(target)
domain = parsed_url.netlocfrom urllib.parse import urlparse
parsed_url = urlparse(target)
domain = parsed_url.netlocFor example:
Input:
https://www.google.com/login
Output:
www.google.comInput:
https://www.google.com/login
Output:
www.google.comThis domain will later be used for network scanning.
Step 3: Verifying Website Availability
Before performing any security checks, the scanner confirms that the website is reachable.
import requests
response = requests.get(target)import requests
response = requests.get(target)If the request succeeds, the scanner displays the HTTP status code.
Example:
Status Code: 200Status Code: 200Common status codes include:
200 — Success
301 — Redirect
403 — Forbidden
404 — Not Found
500 — Internal Server Error
This step ensures the target is online before proceeding.
Step 4: Performing Port Scanning
Port scanning helps identify exposed services running on the target system.
Common ports scanned include:
21 — FTP
22 — SSH
80 — HTTP
443 — HTTPS
Using Python's Socket module:
import socket
common_ports = [21, 22, 80, 443]
for port in common_ports:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(1)
result = sock.connect_ex((domain, port))
if result == 0:
print(f"[OPEN] Port {port}")
sock.close()import socket
common_ports = [21, 22, 80, 443]
for port in common_ports:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(1)
result = sock.connect_ex((domain, port))
if result == 0:
print(f"[OPEN] Port {port}")
sock.close()An open port indicates that a service is actively listening for connections.
Example output:
[OPEN] Port 22
[OPEN] Port 80
[OPEN] Port 443[OPEN] Port 22
[OPEN] Port 80
[OPEN] Port 443Why Open Ports Matter
Every open port represents a potential attack surface.
For example:
a) FTP (Port 21)
FTP often transmits credentials in plain text and is considered insecure.
b) SSH (Port 22)
SSH is generally secure but can be targeted through brute-force attacks if not properly configured.
c) HTTP (Port 80)
Unencrypted web traffic can expose sensitive information.
d) HTTPS (Port 443)
Encrypted traffic is preferred for secure communication.
Step 5: Checking Security Headers
HTTP security headers help protect websites from common web attacks.
The scanner checks for:
security_headers = [
"Content-Security-Policy",
"X-Frame-Options",
"Strict-Transport-Security"
]security_headers = [
"Content-Security-Policy",
"X-Frame-Options",
"Strict-Transport-Security"
]The code:
headers = response.headers
for header in security_headers:
if header in headers:
print(f"[FOUND] {header}")
else:
print(f"[MISSING] {header}")headers = response.headers
for header in security_headers:
if header in headers:
print(f"[FOUND] {header}")
else:
print(f"[MISSING] {header}")Understanding Security Headers
Content-Security-Policy (CSP)
Helps prevent Cross-Site Scripting (XSS) attacks by restricting content sources.
X-Frame-Options
Protects against clickjacking attacks.
Strict-Transport-Security (HSTS)
Forces browsers to use HTTPS connections.
Missing headers may indicate weaker security configurations.
Step 6: Creating a Risk Score
To make the findings easier to interpret, the scanner assigns a risk score.
risk_score = 0risk_score = 0Points are added when:
- Security headers are missing.
- Risky ports are exposed.
Example:
risk_score += 1risk_score += 1The final score provides a simplified representation of overall risk.
Step 7: Recording Vulnerabilities
Instead of only displaying a numerical score, the scanner stores identified vulnerabilities.
vulnerabilities = []vulnerabilities = []Examples:
vulnerabilities.append("FTP Port 21 is open")
vulnerabilities.append(
"Missing Security Header: Content-Security-Policy"
)vulnerabilities.append("FTP Port 21 is open")
vulnerabilities.append(
"Missing Security Header: Content-Security-Policy"
)This creates a more informative report.
Step 8: Displaying Results
Example output:
Final Risk Score: 3
Detected Vulnerabilities:
- SSH Port 22 is open
- Missing Security Header: Content-Security-Policy
- Missing Security Header: Strict-Transport-SecurityFinal Risk Score: 3
Detected Vulnerabilities:
- SSH Port 22 is open
- Missing Security Header: Content-Security-Policy
- Missing Security Header: Strict-Transport-SecurityThis provides both a summary score and detailed findings.
Step 9: Saving the Report
To make the tool more flexible, the user specifies the report filename.
filename = input(
"Enter report file name (example: report.txt): "
)filename = input(
"Enter report file name (example: report.txt): "
)The report is then written to disk.
report = open(filename, "w")report = open(filename, "w")Example report:
Website Vulnerability Scan Report
Target: example.com
Risk Score: 3
Detected Vulnerabilities:
- SSH Port 22 is open
- Missing Security Header: Content-Security-Policy
- Missing Security Header: Strict-Transport-SecurityWebsite Vulnerability Scan Report
Target: example.com
Risk Score: 3
Detected Vulnerabilities:
- SSH Port 22 is open
- Missing Security Header: Content-Security-Policy
- Missing Security Header: Strict-Transport-SecurityThis enables future review and documentation of findings.
Lessons Learned
Building this project reinforced several important cybersecurity concepts:
Networking Fundamentals
Understanding ports, protocols, and connectivity.
Web Security
Learning how security headers contribute to defensive security.
Risk Assessment
Identifying vulnerabilities and assigning severity.
Python Automation
Using Python to automate repetitive security tasks.
Reporting
Documenting findings in a structured and professional manner.
Limitations
While educational, this scanner has several limitations:
- Scans only a small number of ports.
- Does not identify software versions.
- Does not perform vulnerability exploitation.
- Does not map findings to CVEs.
- Uses a simplistic risk scoring model.
Professional scanners provide significantly deeper analysis.
Future Improvements
Potential enhancements include:
Banner Grabbing
Identify service versions running on open ports.
CVE Integration
Map software versions to known vulnerabilities.
SSL/TLS Analysis
Evaluate certificate validity and encryption strength.
Threat Intelligence Integration
Check indicators against threat intelligence feeds.
GUI Development
Build a graphical interface using Tkinter or Flask.
PDF Report Generation
Create professional assessment reports automatically.
Conclusion
Developing this vulnerability scanner provided valuable hands-on experience in cybersecurity automation, networking, and secure coding practices. Although simple, the project demonstrates how Python can be used to automate fundamental security assessments and generate actionable insights.
For beginners entering cybersecurity, projects like this provide an excellent foundation before progressing to more advanced tools such as Nmap, OpenVAS, Burp Suite, and enterprise vulnerability management platforms.
The best way to learn cybersecurity is by building, experimenting, and continuously improving. Every project completed adds another layer of practical experience that can be showcased through GitHub repositories, portfolios, and future job applications.
Happy coding and stay curious.