header-analyzer 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- header_analyzer-1.0.0/LICENSE +21 -0
- header_analyzer-1.0.0/PKG-INFO +153 -0
- header_analyzer-1.0.0/README.md +121 -0
- header_analyzer-1.0.0/header_analyzer.egg-info/PKG-INFO +153 -0
- header_analyzer-1.0.0/header_analyzer.egg-info/SOURCES.txt +10 -0
- header_analyzer-1.0.0/header_analyzer.egg-info/dependency_links.txt +1 -0
- header_analyzer-1.0.0/header_analyzer.egg-info/entry_points.txt +2 -0
- header_analyzer-1.0.0/header_analyzer.egg-info/requires.txt +1 -0
- header_analyzer-1.0.0/header_analyzer.egg-info/top_level.txt +1 -0
- header_analyzer-1.0.0/header_scanner.py +525 -0
- header_analyzer-1.0.0/pyproject.toml +47 -0
- header_analyzer-1.0.0/setup.cfg +4 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Negrescu Alexandru Mihai
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: header-analyzer
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A CLI tool for analyzing HTTP security headers
|
|
5
|
+
Author: negoro26
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/negoro26/Header-analyzer
|
|
8
|
+
Project-URL: Repository, https://github.com/negoro26/Header-analyzer.git
|
|
9
|
+
Project-URL: Issues, https://github.com/negoro26/Header-analyzer/issues
|
|
10
|
+
Keywords: security,http,headers,infosec,cli,hsts
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Information Technology
|
|
15
|
+
Classifier: Intended Audience :: System Administrators
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Topic :: Security
|
|
25
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
26
|
+
Classifier: Topic :: Utilities
|
|
27
|
+
Requires-Python: >=3.8
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
License-File: LICENSE
|
|
30
|
+
Requires-Dist: requests>=2.28.0
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
|
|
33
|
+
# Header-analyzer
|
|
34
|
+
|
|
35
|
+
A Python CLI tool for analyzing HTTP security headers. Scans target URLs, evaluates security posture, and provides a letter grade with detailed breakdown.
|
|
36
|
+
|
|
37
|
+
## Features
|
|
38
|
+
|
|
39
|
+
- **Security Header Analysis** – Checks for HSTS, CSP, X-Frame-Options, X-Content-Type-Options, and more
|
|
40
|
+
- **Security Grading** – Letter grade (A+ to F) based on header configuration
|
|
41
|
+
- **HSTS Preload Detection** – Sites on browser preload lists get automatic A+ rating
|
|
42
|
+
- **Sensitive Header Detection** – Flags headers that may leak server/stack details
|
|
43
|
+
- **Flexible Requests** – Custom headers, User-Agent override, timeout, and SSL bypass options
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
### Via pip (recommended)
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install git+https://github.com/negoro26/Header-analyzer.git
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
This installs the `header-analyzer` command globally (or in your active virtual environment).
|
|
54
|
+
|
|
55
|
+
### Development install (editable)
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
git clone https://github.com/negoro26/Header-analyzer.git
|
|
59
|
+
cd Header-analyzer
|
|
60
|
+
|
|
61
|
+
# Windows
|
|
62
|
+
py -3 -m venv .venv && .\.venv\Scripts\Activate.ps1
|
|
63
|
+
|
|
64
|
+
# macOS/Linux
|
|
65
|
+
python3 -m venv .venv && source .venv/bin/activate
|
|
66
|
+
|
|
67
|
+
pip install -e .
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Usage
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
header-analyzer <url> [options]
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Options
|
|
77
|
+
|
|
78
|
+
| Flag | Description |
|
|
79
|
+
|------|-------------|
|
|
80
|
+
| `-k, --insecure` | Skip SSL certificate verification |
|
|
81
|
+
| `--timeout <int>` | Request timeout in seconds (default: 10) |
|
|
82
|
+
| `--user-agent <string>` | Override User-Agent header |
|
|
83
|
+
| `-H, --header "Key: Value"` | Add custom request header (repeatable) |
|
|
84
|
+
| `--no-default-headers` | Start with empty header set |
|
|
85
|
+
| `--json` | Output headers as prettified JSON |
|
|
86
|
+
|
|
87
|
+
### Examples
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# Basic scan
|
|
91
|
+
header-analyzer example.com
|
|
92
|
+
|
|
93
|
+
# Skip SSL verification with shorter timeout
|
|
94
|
+
header-analyzer https://example.com -k --timeout 5
|
|
95
|
+
|
|
96
|
+
# Custom headers
|
|
97
|
+
header-analyzer https://example.com --user-agent "MyScanner/1.0" \
|
|
98
|
+
-H "Accept-Language: en-US"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Grading System
|
|
102
|
+
|
|
103
|
+
Sites are graded based on security header presence and configuration:
|
|
104
|
+
|
|
105
|
+
| Grade | Score | Description |
|
|
106
|
+
|-------|-------|-------------|
|
|
107
|
+
| A+ | — | Browser Trusted (HSTS preloaded) |
|
|
108
|
+
| A+ | 90%+ | Excellent |
|
|
109
|
+
| A | 75%+ | Very Good |
|
|
110
|
+
| B | 60%+ | Good |
|
|
111
|
+
| C | 45%+ | Acceptable |
|
|
112
|
+
| D | 30%+ | Poor |
|
|
113
|
+
| F | <30% | Critical |
|
|
114
|
+
|
|
115
|
+
### Scoring Breakdown
|
|
116
|
+
|
|
117
|
+
- **HTTPS Baseline**: +25 pts
|
|
118
|
+
- **Strict-Transport-Security**: +25 pts
|
|
119
|
+
- **Content-Security-Policy**: +20 pts
|
|
120
|
+
- **X-Content-Type-Options**: +10 pts
|
|
121
|
+
- **X-Frame-Options**: +10 pts
|
|
122
|
+
- **Referrer-Policy**: +5 pts
|
|
123
|
+
- **Permissions-Policy**: +5 pts
|
|
124
|
+
- **Sensitive headers exposed**: -2 pts each (max -10)
|
|
125
|
+
|
|
126
|
+
Sites in the [HSTS Preload List](https://hstspreload.org/) receive automatic A+ since browsers enforce HTTPS at the protocol level.
|
|
127
|
+
|
|
128
|
+
## Sample Output
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
[+] Target: https://twitter.com
|
|
132
|
+
[+] Status Code: 200
|
|
133
|
+
|
|
134
|
+
============================================================
|
|
135
|
+
[+] SECURITY GRADE
|
|
136
|
+
============================================================
|
|
137
|
+
|
|
138
|
+
Grade: A+ (Browser Trusted)
|
|
139
|
+
|
|
140
|
+
★ This site is in the HSTS Preload List (twitter.com)
|
|
141
|
+
★ HSTS is built into all major browsers (Chrome, Firefox, Safari, Edge)
|
|
142
|
+
★ Browsers will ALWAYS use HTTPS for this site, even on first visit
|
|
143
|
+
★ Header analysis is not needed - browser-level trust is the gold standard
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Requirements
|
|
147
|
+
|
|
148
|
+
- Python 3.8+
|
|
149
|
+
- `requests>=2.28.0` (listed in `requirements.txt` and `pyproject.toml`)
|
|
150
|
+
|
|
151
|
+
## License
|
|
152
|
+
|
|
153
|
+
MIT
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# Header-analyzer
|
|
2
|
+
|
|
3
|
+
A Python CLI tool for analyzing HTTP security headers. Scans target URLs, evaluates security posture, and provides a letter grade with detailed breakdown.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Security Header Analysis** – Checks for HSTS, CSP, X-Frame-Options, X-Content-Type-Options, and more
|
|
8
|
+
- **Security Grading** – Letter grade (A+ to F) based on header configuration
|
|
9
|
+
- **HSTS Preload Detection** – Sites on browser preload lists get automatic A+ rating
|
|
10
|
+
- **Sensitive Header Detection** – Flags headers that may leak server/stack details
|
|
11
|
+
- **Flexible Requests** – Custom headers, User-Agent override, timeout, and SSL bypass options
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
### Via pip (recommended)
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install git+https://github.com/negoro26/Header-analyzer.git
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
This installs the `header-analyzer` command globally (or in your active virtual environment).
|
|
22
|
+
|
|
23
|
+
### Development install (editable)
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
git clone https://github.com/negoro26/Header-analyzer.git
|
|
27
|
+
cd Header-analyzer
|
|
28
|
+
|
|
29
|
+
# Windows
|
|
30
|
+
py -3 -m venv .venv && .\.venv\Scripts\Activate.ps1
|
|
31
|
+
|
|
32
|
+
# macOS/Linux
|
|
33
|
+
python3 -m venv .venv && source .venv/bin/activate
|
|
34
|
+
|
|
35
|
+
pip install -e .
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Usage
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
header-analyzer <url> [options]
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Options
|
|
45
|
+
|
|
46
|
+
| Flag | Description |
|
|
47
|
+
|------|-------------|
|
|
48
|
+
| `-k, --insecure` | Skip SSL certificate verification |
|
|
49
|
+
| `--timeout <int>` | Request timeout in seconds (default: 10) |
|
|
50
|
+
| `--user-agent <string>` | Override User-Agent header |
|
|
51
|
+
| `-H, --header "Key: Value"` | Add custom request header (repeatable) |
|
|
52
|
+
| `--no-default-headers` | Start with empty header set |
|
|
53
|
+
| `--json` | Output headers as prettified JSON |
|
|
54
|
+
|
|
55
|
+
### Examples
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# Basic scan
|
|
59
|
+
header-analyzer example.com
|
|
60
|
+
|
|
61
|
+
# Skip SSL verification with shorter timeout
|
|
62
|
+
header-analyzer https://example.com -k --timeout 5
|
|
63
|
+
|
|
64
|
+
# Custom headers
|
|
65
|
+
header-analyzer https://example.com --user-agent "MyScanner/1.0" \
|
|
66
|
+
-H "Accept-Language: en-US"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Grading System
|
|
70
|
+
|
|
71
|
+
Sites are graded based on security header presence and configuration:
|
|
72
|
+
|
|
73
|
+
| Grade | Score | Description |
|
|
74
|
+
|-------|-------|-------------|
|
|
75
|
+
| A+ | — | Browser Trusted (HSTS preloaded) |
|
|
76
|
+
| A+ | 90%+ | Excellent |
|
|
77
|
+
| A | 75%+ | Very Good |
|
|
78
|
+
| B | 60%+ | Good |
|
|
79
|
+
| C | 45%+ | Acceptable |
|
|
80
|
+
| D | 30%+ | Poor |
|
|
81
|
+
| F | <30% | Critical |
|
|
82
|
+
|
|
83
|
+
### Scoring Breakdown
|
|
84
|
+
|
|
85
|
+
- **HTTPS Baseline**: +25 pts
|
|
86
|
+
- **Strict-Transport-Security**: +25 pts
|
|
87
|
+
- **Content-Security-Policy**: +20 pts
|
|
88
|
+
- **X-Content-Type-Options**: +10 pts
|
|
89
|
+
- **X-Frame-Options**: +10 pts
|
|
90
|
+
- **Referrer-Policy**: +5 pts
|
|
91
|
+
- **Permissions-Policy**: +5 pts
|
|
92
|
+
- **Sensitive headers exposed**: -2 pts each (max -10)
|
|
93
|
+
|
|
94
|
+
Sites in the [HSTS Preload List](https://hstspreload.org/) receive automatic A+ since browsers enforce HTTPS at the protocol level.
|
|
95
|
+
|
|
96
|
+
## Sample Output
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
[+] Target: https://twitter.com
|
|
100
|
+
[+] Status Code: 200
|
|
101
|
+
|
|
102
|
+
============================================================
|
|
103
|
+
[+] SECURITY GRADE
|
|
104
|
+
============================================================
|
|
105
|
+
|
|
106
|
+
Grade: A+ (Browser Trusted)
|
|
107
|
+
|
|
108
|
+
★ This site is in the HSTS Preload List (twitter.com)
|
|
109
|
+
★ HSTS is built into all major browsers (Chrome, Firefox, Safari, Edge)
|
|
110
|
+
★ Browsers will ALWAYS use HTTPS for this site, even on first visit
|
|
111
|
+
★ Header analysis is not needed - browser-level trust is the gold standard
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Requirements
|
|
115
|
+
|
|
116
|
+
- Python 3.8+
|
|
117
|
+
- `requests>=2.28.0` (listed in `requirements.txt` and `pyproject.toml`)
|
|
118
|
+
|
|
119
|
+
## License
|
|
120
|
+
|
|
121
|
+
MIT
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: header-analyzer
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A CLI tool for analyzing HTTP security headers
|
|
5
|
+
Author: negoro26
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/negoro26/Header-analyzer
|
|
8
|
+
Project-URL: Repository, https://github.com/negoro26/Header-analyzer.git
|
|
9
|
+
Project-URL: Issues, https://github.com/negoro26/Header-analyzer/issues
|
|
10
|
+
Keywords: security,http,headers,infosec,cli,hsts
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Information Technology
|
|
15
|
+
Classifier: Intended Audience :: System Administrators
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Topic :: Security
|
|
25
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
26
|
+
Classifier: Topic :: Utilities
|
|
27
|
+
Requires-Python: >=3.8
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
License-File: LICENSE
|
|
30
|
+
Requires-Dist: requests>=2.28.0
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
|
|
33
|
+
# Header-analyzer
|
|
34
|
+
|
|
35
|
+
A Python CLI tool for analyzing HTTP security headers. Scans target URLs, evaluates security posture, and provides a letter grade with detailed breakdown.
|
|
36
|
+
|
|
37
|
+
## Features
|
|
38
|
+
|
|
39
|
+
- **Security Header Analysis** – Checks for HSTS, CSP, X-Frame-Options, X-Content-Type-Options, and more
|
|
40
|
+
- **Security Grading** – Letter grade (A+ to F) based on header configuration
|
|
41
|
+
- **HSTS Preload Detection** – Sites on browser preload lists get automatic A+ rating
|
|
42
|
+
- **Sensitive Header Detection** – Flags headers that may leak server/stack details
|
|
43
|
+
- **Flexible Requests** – Custom headers, User-Agent override, timeout, and SSL bypass options
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
### Via pip (recommended)
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install git+https://github.com/negoro26/Header-analyzer.git
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
This installs the `header-analyzer` command globally (or in your active virtual environment).
|
|
54
|
+
|
|
55
|
+
### Development install (editable)
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
git clone https://github.com/negoro26/Header-analyzer.git
|
|
59
|
+
cd Header-analyzer
|
|
60
|
+
|
|
61
|
+
# Windows
|
|
62
|
+
py -3 -m venv .venv && .\.venv\Scripts\Activate.ps1
|
|
63
|
+
|
|
64
|
+
# macOS/Linux
|
|
65
|
+
python3 -m venv .venv && source .venv/bin/activate
|
|
66
|
+
|
|
67
|
+
pip install -e .
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Usage
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
header-analyzer <url> [options]
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Options
|
|
77
|
+
|
|
78
|
+
| Flag | Description |
|
|
79
|
+
|------|-------------|
|
|
80
|
+
| `-k, --insecure` | Skip SSL certificate verification |
|
|
81
|
+
| `--timeout <int>` | Request timeout in seconds (default: 10) |
|
|
82
|
+
| `--user-agent <string>` | Override User-Agent header |
|
|
83
|
+
| `-H, --header "Key: Value"` | Add custom request header (repeatable) |
|
|
84
|
+
| `--no-default-headers` | Start with empty header set |
|
|
85
|
+
| `--json` | Output headers as prettified JSON |
|
|
86
|
+
|
|
87
|
+
### Examples
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# Basic scan
|
|
91
|
+
header-analyzer example.com
|
|
92
|
+
|
|
93
|
+
# Skip SSL verification with shorter timeout
|
|
94
|
+
header-analyzer https://example.com -k --timeout 5
|
|
95
|
+
|
|
96
|
+
# Custom headers
|
|
97
|
+
header-analyzer https://example.com --user-agent "MyScanner/1.0" \
|
|
98
|
+
-H "Accept-Language: en-US"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Grading System
|
|
102
|
+
|
|
103
|
+
Sites are graded based on security header presence and configuration:
|
|
104
|
+
|
|
105
|
+
| Grade | Score | Description |
|
|
106
|
+
|-------|-------|-------------|
|
|
107
|
+
| A+ | — | Browser Trusted (HSTS preloaded) |
|
|
108
|
+
| A+ | 90%+ | Excellent |
|
|
109
|
+
| A | 75%+ | Very Good |
|
|
110
|
+
| B | 60%+ | Good |
|
|
111
|
+
| C | 45%+ | Acceptable |
|
|
112
|
+
| D | 30%+ | Poor |
|
|
113
|
+
| F | <30% | Critical |
|
|
114
|
+
|
|
115
|
+
### Scoring Breakdown
|
|
116
|
+
|
|
117
|
+
- **HTTPS Baseline**: +25 pts
|
|
118
|
+
- **Strict-Transport-Security**: +25 pts
|
|
119
|
+
- **Content-Security-Policy**: +20 pts
|
|
120
|
+
- **X-Content-Type-Options**: +10 pts
|
|
121
|
+
- **X-Frame-Options**: +10 pts
|
|
122
|
+
- **Referrer-Policy**: +5 pts
|
|
123
|
+
- **Permissions-Policy**: +5 pts
|
|
124
|
+
- **Sensitive headers exposed**: -2 pts each (max -10)
|
|
125
|
+
|
|
126
|
+
Sites in the [HSTS Preload List](https://hstspreload.org/) receive automatic A+ since browsers enforce HTTPS at the protocol level.
|
|
127
|
+
|
|
128
|
+
## Sample Output
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
[+] Target: https://twitter.com
|
|
132
|
+
[+] Status Code: 200
|
|
133
|
+
|
|
134
|
+
============================================================
|
|
135
|
+
[+] SECURITY GRADE
|
|
136
|
+
============================================================
|
|
137
|
+
|
|
138
|
+
Grade: A+ (Browser Trusted)
|
|
139
|
+
|
|
140
|
+
★ This site is in the HSTS Preload List (twitter.com)
|
|
141
|
+
★ HSTS is built into all major browsers (Chrome, Firefox, Safari, Edge)
|
|
142
|
+
★ Browsers will ALWAYS use HTTPS for this site, even on first visit
|
|
143
|
+
★ Header analysis is not needed - browser-level trust is the gold standard
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Requirements
|
|
147
|
+
|
|
148
|
+
- Python 3.8+
|
|
149
|
+
- `requests>=2.28.0` (listed in `requirements.txt` and `pyproject.toml`)
|
|
150
|
+
|
|
151
|
+
## License
|
|
152
|
+
|
|
153
|
+
MIT
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
header_scanner.py
|
|
4
|
+
pyproject.toml
|
|
5
|
+
header_analyzer.egg-info/PKG-INFO
|
|
6
|
+
header_analyzer.egg-info/SOURCES.txt
|
|
7
|
+
header_analyzer.egg-info/dependency_links.txt
|
|
8
|
+
header_analyzer.egg-info/entry_points.txt
|
|
9
|
+
header_analyzer.egg-info/requires.txt
|
|
10
|
+
header_analyzer.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
requests>=2.28.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
header_scanner
|
|
@@ -0,0 +1,525 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import requests
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
from urllib.parse import urlparse
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class HeaderScanner:
|
|
9
|
+
DEFAULT_CLIENT_HEADERS = {
|
|
10
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0",
|
|
11
|
+
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
|
12
|
+
"Accept-Language": "en-US,en;q=0.5",
|
|
13
|
+
"Upgrade-Insecure-Requests": "1",
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
SECURITY_HEADERS = {
|
|
17
|
+
"Strict-Transport-Security": "Enforces HTTPS (HSTS)",
|
|
18
|
+
"Content-Security-Policy": "Mitigates XSS and data injection",
|
|
19
|
+
"X-Content-Type-Options": 'Prevents MIME sniffing (should be "nosniff")',
|
|
20
|
+
"X-Frame-Options": 'Prevents clickjacking (should be "DENY" or "SAMEORIGIN")',
|
|
21
|
+
"X-XSS-Protection": "Legacy XSS protection (modern browsers ignore)",
|
|
22
|
+
"Referrer-Policy": "Controls referrer information",
|
|
23
|
+
"Permissions-Policy": "Controls browser feature access",
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
SENSITIVE_HEADERS = [
|
|
27
|
+
"Server",
|
|
28
|
+
"X-Powered-By",
|
|
29
|
+
"X-AspNet-Version",
|
|
30
|
+
"X-AspNetMvc-Version",
|
|
31
|
+
"X-Runtime",
|
|
32
|
+
"X-Version",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
# Grading system configuration
|
|
36
|
+
# Baseline points for using HTTPS (awarded automatically if URL is https)
|
|
37
|
+
HTTPS_BASELINE = 25
|
|
38
|
+
# HSTS preload API
|
|
39
|
+
HSTS_PRELOAD_API = "https://hstspreload.org/api/v2/status"
|
|
40
|
+
|
|
41
|
+
HEADER_WEIGHTS = {
|
|
42
|
+
"Strict-Transport-Security": 25, # Critical - enforces HTTPS
|
|
43
|
+
"Content-Security-Policy": 20, # Critical - XSS/injection protection
|
|
44
|
+
"X-Content-Type-Options": 10, # Important - MIME sniffing
|
|
45
|
+
"X-Frame-Options": 10, # Important - clickjacking
|
|
46
|
+
"Referrer-Policy": 5, # Moderate - privacy
|
|
47
|
+
"Permissions-Policy": 5, # Moderate - feature access
|
|
48
|
+
"X-XSS-Protection": 0, # Deprecated - browsers ignore this
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
EXPECTED_VALUES = {
|
|
52
|
+
"X-Content-Type-Options": lambda v: v.lower() == "nosniff",
|
|
53
|
+
"X-Frame-Options": lambda v: v.upper() in ("DENY", "SAMEORIGIN"),
|
|
54
|
+
"Referrer-Policy": lambda v: v.lower()
|
|
55
|
+
in (
|
|
56
|
+
"no-referrer",
|
|
57
|
+
"no-referrer-when-downgrade",
|
|
58
|
+
"origin",
|
|
59
|
+
"origin-when-cross-origin",
|
|
60
|
+
"same-origin",
|
|
61
|
+
"strict-origin",
|
|
62
|
+
"strict-origin-when-cross-origin",
|
|
63
|
+
),
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
# Reduced penalty - Server header is common and low-risk
|
|
67
|
+
SENSITIVE_PENALTY = 2 # Points deducted per sensitive header exposed
|
|
68
|
+
|
|
69
|
+
GRADE_THRESHOLDS = [
|
|
70
|
+
(90, "A+", "Excellent"),
|
|
71
|
+
(75, "A", "Very Good"),
|
|
72
|
+
(60, "B", "Good"),
|
|
73
|
+
(45, "C", "Acceptable"),
|
|
74
|
+
(30, "D", "Poor"),
|
|
75
|
+
(0, "F", "Critical"),
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
def __init__(self, url, args):
|
|
79
|
+
self.args = args
|
|
80
|
+
self.url = self._validate_url(url)
|
|
81
|
+
self.headers = self._build_client_headers()
|
|
82
|
+
self.response = None
|
|
83
|
+
self._hsts_preload_status = None # Cached result
|
|
84
|
+
|
|
85
|
+
def _validate_url(self, url):
|
|
86
|
+
if not url.startswith(("http://", "https://")):
|
|
87
|
+
url = f"https://{url}"
|
|
88
|
+
return url
|
|
89
|
+
|
|
90
|
+
def _build_client_headers(self):
|
|
91
|
+
if self.args.no_default_headers:
|
|
92
|
+
headers = {}
|
|
93
|
+
else:
|
|
94
|
+
headers = dict(self.DEFAULT_CLIENT_HEADERS)
|
|
95
|
+
|
|
96
|
+
if getattr(self.args, "user_agent", None):
|
|
97
|
+
headers["User-Agent"] = self.args.user_agent
|
|
98
|
+
|
|
99
|
+
for item in getattr(self.args, "header", []) or []:
|
|
100
|
+
if ":" not in item:
|
|
101
|
+
print(
|
|
102
|
+
f"[!] Ignoring invalid header (expected 'Key: Value'): {item}",
|
|
103
|
+
file=sys.stderr,
|
|
104
|
+
)
|
|
105
|
+
continue
|
|
106
|
+
key, value = item.split(":", 1)
|
|
107
|
+
key = key.strip()
|
|
108
|
+
value = value.strip()
|
|
109
|
+
if not key:
|
|
110
|
+
print(f"[!] Ignoring header with empty key: {item}", file=sys.stderr)
|
|
111
|
+
continue
|
|
112
|
+
headers[key] = value
|
|
113
|
+
return headers
|
|
114
|
+
|
|
115
|
+
def _extract_domain(self, url):
|
|
116
|
+
"""Extract the registrable domain from a URL."""
|
|
117
|
+
parsed = urlparse(url)
|
|
118
|
+
hostname = parsed.netloc or parsed.path
|
|
119
|
+
# Remove port if present
|
|
120
|
+
hostname = hostname.split(":")[0]
|
|
121
|
+
# Get the main domain (e.g., www.google.com -> google.com)
|
|
122
|
+
parts = hostname.split(".")
|
|
123
|
+
if len(parts) >= 2:
|
|
124
|
+
return ".".join(parts[-2:])
|
|
125
|
+
return hostname
|
|
126
|
+
|
|
127
|
+
def check_hsts_preload(self):
|
|
128
|
+
"""Check if domain is in browser HSTS preload list.
|
|
129
|
+
|
|
130
|
+
Checks both the original requested domain and the final redirected domain,
|
|
131
|
+
since sites like twitter.com may redirect to x.com but still be preloaded.
|
|
132
|
+
"""
|
|
133
|
+
if self._hsts_preload_status is not None:
|
|
134
|
+
return self._hsts_preload_status
|
|
135
|
+
|
|
136
|
+
# Check both original domain and final domain (after redirects)
|
|
137
|
+
original_domain = self._extract_domain(self.url)
|
|
138
|
+
final_domain = (
|
|
139
|
+
self._extract_domain(self.response.url)
|
|
140
|
+
if self.response
|
|
141
|
+
else original_domain
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
domains_to_check = [original_domain]
|
|
145
|
+
if final_domain != original_domain:
|
|
146
|
+
domains_to_check.append(final_domain)
|
|
147
|
+
|
|
148
|
+
for domain in domains_to_check:
|
|
149
|
+
try:
|
|
150
|
+
resp = requests.get(
|
|
151
|
+
self.HSTS_PRELOAD_API, params={"domain": domain}, timeout=5
|
|
152
|
+
)
|
|
153
|
+
if resp.ok:
|
|
154
|
+
data = resp.json()
|
|
155
|
+
status = data.get("status", "")
|
|
156
|
+
if status == "preloaded":
|
|
157
|
+
self._hsts_preload_status = {
|
|
158
|
+
"domain": domain,
|
|
159
|
+
"preloaded": True,
|
|
160
|
+
"status": status,
|
|
161
|
+
}
|
|
162
|
+
return self._hsts_preload_status
|
|
163
|
+
except requests.exceptions.RequestException:
|
|
164
|
+
continue
|
|
165
|
+
|
|
166
|
+
# Neither domain is preloaded
|
|
167
|
+
self._hsts_preload_status = {
|
|
168
|
+
"domain": original_domain,
|
|
169
|
+
"preloaded": False,
|
|
170
|
+
"status": "unknown",
|
|
171
|
+
}
|
|
172
|
+
return self._hsts_preload_status
|
|
173
|
+
|
|
174
|
+
def make_request(self):
|
|
175
|
+
timeout = self.args.timeout
|
|
176
|
+
insecure = self.args.insecure
|
|
177
|
+
|
|
178
|
+
try:
|
|
179
|
+
response = requests.head(
|
|
180
|
+
self.url,
|
|
181
|
+
headers=self.headers,
|
|
182
|
+
timeout=timeout,
|
|
183
|
+
verify=not insecure,
|
|
184
|
+
allow_redirects=True,
|
|
185
|
+
)
|
|
186
|
+
if not response.ok:
|
|
187
|
+
raise requests.exceptions.RequestException(
|
|
188
|
+
f"HEAD not OK: {response.status_code}"
|
|
189
|
+
)
|
|
190
|
+
self.response = response
|
|
191
|
+
return response
|
|
192
|
+
except (requests.exceptions.RequestException, requests.exceptions.HTTPError):
|
|
193
|
+
try:
|
|
194
|
+
response = requests.get(
|
|
195
|
+
self.url,
|
|
196
|
+
headers=self.headers,
|
|
197
|
+
timeout=timeout,
|
|
198
|
+
verify=not insecure,
|
|
199
|
+
allow_redirects=True,
|
|
200
|
+
)
|
|
201
|
+
self.response = response
|
|
202
|
+
return response
|
|
203
|
+
except requests.exceptions.RequestException:
|
|
204
|
+
raise
|
|
205
|
+
|
|
206
|
+
def analyze_security_headers(self):
|
|
207
|
+
print("\n" + "=" * 60)
|
|
208
|
+
print("[+] Analyzing security headers...")
|
|
209
|
+
print("=" * 60)
|
|
210
|
+
|
|
211
|
+
present = []
|
|
212
|
+
missing = []
|
|
213
|
+
headers = self.response.headers
|
|
214
|
+
|
|
215
|
+
for header, description in self.SECURITY_HEADERS.items():
|
|
216
|
+
if header in headers:
|
|
217
|
+
present.append((header, headers[header]))
|
|
218
|
+
else:
|
|
219
|
+
missing.append((header, description))
|
|
220
|
+
|
|
221
|
+
if present:
|
|
222
|
+
print("Security headers present:")
|
|
223
|
+
for header, value in present:
|
|
224
|
+
if header == "X-Content-Type-Options" and value.lower() != "nosniff":
|
|
225
|
+
print(f" • {header} → {value} (should be 'nosniff')")
|
|
226
|
+
elif header == "X-Frame-Options" and value.upper() not in (
|
|
227
|
+
"DENY",
|
|
228
|
+
"SAMEORIGIN",
|
|
229
|
+
):
|
|
230
|
+
print(f" • {header} → {value} (should be 'DENY' or 'SAMEORIGIN')")
|
|
231
|
+
else:
|
|
232
|
+
print(f" • {header} → {value}")
|
|
233
|
+
else:
|
|
234
|
+
print("All security headers missing")
|
|
235
|
+
|
|
236
|
+
if missing:
|
|
237
|
+
print("Security headers missing:")
|
|
238
|
+
for header, description in missing:
|
|
239
|
+
print(f" • {header} → {description}")
|
|
240
|
+
else:
|
|
241
|
+
print("All security headers present")
|
|
242
|
+
|
|
243
|
+
def analyze_sensitive_headers(self):
|
|
244
|
+
print("\n" + "=" * 60)
|
|
245
|
+
print("[+] Analyzing sensitive headers...")
|
|
246
|
+
print("=" * 60)
|
|
247
|
+
|
|
248
|
+
found = []
|
|
249
|
+
headers = self.response.headers
|
|
250
|
+
for header in self.SENSITIVE_HEADERS:
|
|
251
|
+
if header in headers:
|
|
252
|
+
found.append((header, headers[header]))
|
|
253
|
+
|
|
254
|
+
if found:
|
|
255
|
+
print("Potentially sensitive headers found:")
|
|
256
|
+
for header, value in found:
|
|
257
|
+
print(f" • {header}: {value}")
|
|
258
|
+
print("These headers might reveal technology, version or framework details")
|
|
259
|
+
else:
|
|
260
|
+
print("[+] No sensitive headers present")
|
|
261
|
+
|
|
262
|
+
def calculate_grade(self):
|
|
263
|
+
"""Calculate security grade based on headers analysis."""
|
|
264
|
+
headers = self.response.headers
|
|
265
|
+
|
|
266
|
+
# Calculate total possible (HTTPS baseline + header weights, excluding 0-weight headers)
|
|
267
|
+
header_points_possible = sum(w for w in self.HEADER_WEIGHTS.values() if w > 0)
|
|
268
|
+
total_possible = self.HTTPS_BASELINE + header_points_possible
|
|
269
|
+
|
|
270
|
+
score_details = {
|
|
271
|
+
"header_scores": [],
|
|
272
|
+
"value_bonuses": [],
|
|
273
|
+
"penalties": [],
|
|
274
|
+
"https_baseline": 0,
|
|
275
|
+
"total_possible": total_possible,
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
earned_points = 0
|
|
279
|
+
|
|
280
|
+
# Baseline points for HTTPS
|
|
281
|
+
if self.response.url.startswith("https://"):
|
|
282
|
+
earned_points += self.HTTPS_BASELINE
|
|
283
|
+
score_details["https_baseline"] = self.HTTPS_BASELINE
|
|
284
|
+
|
|
285
|
+
# Check if site is HSTS preloaded - if so, automatic A+ (browser already trusts it)
|
|
286
|
+
hsts_preload = self.check_hsts_preload()
|
|
287
|
+
if hsts_preload.get("preloaded"):
|
|
288
|
+
return {
|
|
289
|
+
"score": 100,
|
|
290
|
+
"max_score": 100,
|
|
291
|
+
"percentage": 100,
|
|
292
|
+
"grade": "A+",
|
|
293
|
+
"description": "Browser Trusted",
|
|
294
|
+
"hsts_preloaded": True,
|
|
295
|
+
"hsts_domain": hsts_preload["domain"],
|
|
296
|
+
"details": None,
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
# Score for security headers present and their values
|
|
300
|
+
for header, weight in self.HEADER_WEIGHTS.items():
|
|
301
|
+
if weight == 0: # Skip deprecated headers with 0 weight
|
|
302
|
+
continue
|
|
303
|
+
if header in headers:
|
|
304
|
+
value = headers[header]
|
|
305
|
+
earned_points += weight
|
|
306
|
+
score_details["header_scores"].append((header, weight, True))
|
|
307
|
+
|
|
308
|
+
# Check for correct value (bonus: no deduction)
|
|
309
|
+
if header in self.EXPECTED_VALUES:
|
|
310
|
+
validator = self.EXPECTED_VALUES[header]
|
|
311
|
+
if validator(value):
|
|
312
|
+
score_details["value_bonuses"].append((header, "correct"))
|
|
313
|
+
else:
|
|
314
|
+
# Partial credit - header present but misconfigured
|
|
315
|
+
penalty = weight // 4 # Reduced penalty for misconfiguration
|
|
316
|
+
earned_points -= penalty
|
|
317
|
+
score_details["value_bonuses"].append(
|
|
318
|
+
(header, f"misconfigured (-{penalty})")
|
|
319
|
+
)
|
|
320
|
+
else:
|
|
321
|
+
score_details["header_scores"].append((header, weight, False))
|
|
322
|
+
|
|
323
|
+
# Penalty for sensitive headers exposed (capped to avoid crushing the score)
|
|
324
|
+
sensitive_count = 0
|
|
325
|
+
for header in self.SENSITIVE_HEADERS:
|
|
326
|
+
if header in headers:
|
|
327
|
+
sensitive_count += 1
|
|
328
|
+
score_details["penalties"].append((header, self.SENSITIVE_PENALTY))
|
|
329
|
+
|
|
330
|
+
total_penalty = min(
|
|
331
|
+
sensitive_count * self.SENSITIVE_PENALTY, 10
|
|
332
|
+
) # Cap at 10 pts
|
|
333
|
+
earned_points = max(0, earned_points - total_penalty)
|
|
334
|
+
|
|
335
|
+
# Calculate percentage
|
|
336
|
+
percentage = (earned_points / total_possible) * 100
|
|
337
|
+
percentage = min(100, max(0, percentage)) # Clamp to 0-100
|
|
338
|
+
|
|
339
|
+
# Determine letter grade
|
|
340
|
+
grade = "F"
|
|
341
|
+
description = "Critical"
|
|
342
|
+
for threshold, letter, desc in self.GRADE_THRESHOLDS:
|
|
343
|
+
if percentage >= threshold:
|
|
344
|
+
grade = letter
|
|
345
|
+
description = desc
|
|
346
|
+
break
|
|
347
|
+
|
|
348
|
+
return {
|
|
349
|
+
"score": round(earned_points, 1),
|
|
350
|
+
"max_score": score_details["total_possible"],
|
|
351
|
+
"percentage": round(percentage, 1),
|
|
352
|
+
"grade": grade,
|
|
353
|
+
"description": description,
|
|
354
|
+
"details": score_details,
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
def display_grade(self):
|
|
358
|
+
"""Display the security grade with breakdown."""
|
|
359
|
+
result = self.calculate_grade()
|
|
360
|
+
|
|
361
|
+
print("\n" + "=" * 60)
|
|
362
|
+
print("[+] SECURITY GRADE")
|
|
363
|
+
print("=" * 60)
|
|
364
|
+
|
|
365
|
+
# Special case: HSTS preloaded sites
|
|
366
|
+
if result.get("hsts_preloaded"):
|
|
367
|
+
print(f"\n Grade: {result['grade']} ({result['description']})")
|
|
368
|
+
print(
|
|
369
|
+
f"\n ★ This site is in the HSTS Preload List ({result['hsts_domain']})"
|
|
370
|
+
)
|
|
371
|
+
print(
|
|
372
|
+
" ★ HSTS is built into all major browsers (Chrome, Firefox, Safari, Edge)"
|
|
373
|
+
)
|
|
374
|
+
print(
|
|
375
|
+
" ★ Browsers will ALWAYS use HTTPS for this site, even on first visit"
|
|
376
|
+
)
|
|
377
|
+
print(
|
|
378
|
+
" ★ Header analysis is not needed - browser-level trust is the gold standard"
|
|
379
|
+
)
|
|
380
|
+
return result
|
|
381
|
+
|
|
382
|
+
# Normal grading display
|
|
383
|
+
grade_bar = self._create_grade_bar(result["percentage"])
|
|
384
|
+
print(f"\n Grade: {result['grade']} ({result['description']})")
|
|
385
|
+
print(
|
|
386
|
+
f" Score: {result['score']}/{result['max_score']} ({result['percentage']}%)"
|
|
387
|
+
)
|
|
388
|
+
print(f" {grade_bar}")
|
|
389
|
+
|
|
390
|
+
# Breakdown
|
|
391
|
+
details = result["details"]
|
|
392
|
+
|
|
393
|
+
# Show HTTPS baseline if earned
|
|
394
|
+
if details.get("https_baseline", 0) > 0:
|
|
395
|
+
print(f"\n HTTPS Baseline: +{details['https_baseline']} pts")
|
|
396
|
+
|
|
397
|
+
print("\n Header Scores:")
|
|
398
|
+
for header, weight, present in details["header_scores"]:
|
|
399
|
+
status = "✓" if present else "✗"
|
|
400
|
+
points = f"+{weight}" if present else f" 0"
|
|
401
|
+
print(f" {status} {header}: {points} pts")
|
|
402
|
+
|
|
403
|
+
if details["value_bonuses"]:
|
|
404
|
+
print("\n Value Validation:")
|
|
405
|
+
for header, status in details["value_bonuses"]:
|
|
406
|
+
icon = "✓" if status == "correct" else "⚠"
|
|
407
|
+
print(f" {icon} {header}: {status}")
|
|
408
|
+
|
|
409
|
+
if details["penalties"]:
|
|
410
|
+
print("\n Penalties (Sensitive Headers Exposed):")
|
|
411
|
+
for header, penalty in details["penalties"]:
|
|
412
|
+
print(f" ✗ {header}: -{penalty} pts")
|
|
413
|
+
|
|
414
|
+
return result
|
|
415
|
+
|
|
416
|
+
def _create_grade_bar(self, percentage):
|
|
417
|
+
"""Create a visual progress bar for the grade."""
|
|
418
|
+
bar_length = 30
|
|
419
|
+
filled = int(bar_length * percentage / 100)
|
|
420
|
+
empty = bar_length - filled
|
|
421
|
+
|
|
422
|
+
# Color coding based on percentage
|
|
423
|
+
if percentage >= 80:
|
|
424
|
+
fill_char = "█"
|
|
425
|
+
elif percentage >= 60:
|
|
426
|
+
fill_char = "▓"
|
|
427
|
+
elif percentage >= 40:
|
|
428
|
+
fill_char = "▒"
|
|
429
|
+
else:
|
|
430
|
+
fill_char = "░"
|
|
431
|
+
|
|
432
|
+
bar = fill_char * filled + "░" * empty
|
|
433
|
+
return f" [{bar}]"
|
|
434
|
+
|
|
435
|
+
def print_headers(self, as_json=False):
|
|
436
|
+
if as_json:
|
|
437
|
+
print("\n" + "=" * 60)
|
|
438
|
+
print("[+] RAW RESPONSE HEADERS (AS JSON)")
|
|
439
|
+
print("=" * 60)
|
|
440
|
+
|
|
441
|
+
headers_dict = dict(self.response.headers)
|
|
442
|
+
print(json.dumps(headers_dict, indent=4, sort_keys=True))
|
|
443
|
+
else:
|
|
444
|
+
print("\n" + "=" * 60)
|
|
445
|
+
print("[+] RAW RESPONSE HEADERS")
|
|
446
|
+
print("=" * 60)
|
|
447
|
+
for key, value in self.response.headers.items():
|
|
448
|
+
print(f"{key}: {value}")
|
|
449
|
+
|
|
450
|
+
def scan(self):
|
|
451
|
+
self.make_request()
|
|
452
|
+
|
|
453
|
+
print(f"[+] Target: {self.url}")
|
|
454
|
+
print(f"[+] Status Code: {self.response.status_code}")
|
|
455
|
+
if self.response.history:
|
|
456
|
+
print(f"[+] Redirected {len(self.response.history)} times")
|
|
457
|
+
print(f" Final URL: {self.response.url}")
|
|
458
|
+
|
|
459
|
+
self.print_headers(as_json=self.args.json)
|
|
460
|
+
self.analyze_security_headers()
|
|
461
|
+
self.analyze_sensitive_headers()
|
|
462
|
+
self.display_grade()
|
|
463
|
+
|
|
464
|
+
print("\n" + "=" * 60)
|
|
465
|
+
print("[+] Scan completed successfully!")
|
|
466
|
+
print("=" * 60)
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
def main():
|
|
470
|
+
parser = argparse.ArgumentParser(
|
|
471
|
+
formatter_class=argparse.RawTextHelpFormatter,
|
|
472
|
+
description="HTTP Header Security Analyzer",
|
|
473
|
+
)
|
|
474
|
+
parser.add_argument("url", help="Target URL to scan")
|
|
475
|
+
parser.add_argument(
|
|
476
|
+
"--insecure",
|
|
477
|
+
"-k",
|
|
478
|
+
action="store_true",
|
|
479
|
+
help="Skip SSL certificate verification",
|
|
480
|
+
)
|
|
481
|
+
parser.add_argument(
|
|
482
|
+
"--timeout",
|
|
483
|
+
type=int,
|
|
484
|
+
default=10,
|
|
485
|
+
help="Request timeout in seconds (default: 10)",
|
|
486
|
+
)
|
|
487
|
+
parser.add_argument(
|
|
488
|
+
"--user-agent", dest="user_agent", help="Override User-Agent header"
|
|
489
|
+
)
|
|
490
|
+
parser.add_argument(
|
|
491
|
+
"--header",
|
|
492
|
+
"-H",
|
|
493
|
+
action="append",
|
|
494
|
+
default=[],
|
|
495
|
+
help='Custom request header, e.g., -H "Key: Value". Repeat to add multiple.',
|
|
496
|
+
)
|
|
497
|
+
parser.add_argument(
|
|
498
|
+
"--no-default-headers",
|
|
499
|
+
action="store_true",
|
|
500
|
+
help="Do not include built-in client headers (start from empty set)",
|
|
501
|
+
)
|
|
502
|
+
parser.add_argument("--json", action="store_true", help="Output results as JSON")
|
|
503
|
+
|
|
504
|
+
args = parser.parse_args()
|
|
505
|
+
|
|
506
|
+
try:
|
|
507
|
+
scanner = HeaderScanner(args.url.strip(), args)
|
|
508
|
+
scanner.scan()
|
|
509
|
+
except KeyboardInterrupt:
|
|
510
|
+
sys.exit("\n[!] Scan interrupted by user")
|
|
511
|
+
except requests.exceptions.RequestException as e:
|
|
512
|
+
if isinstance(e, requests.exceptions.SSLError):
|
|
513
|
+
sys.exit(
|
|
514
|
+
f"[!] SSL Error: {e}\n Use --insecure to bypass certificate validation"
|
|
515
|
+
)
|
|
516
|
+
elif isinstance(e, requests.exceptions.Timeout):
|
|
517
|
+
sys.exit(f"[!] Request timed out after {args.timeout} seconds")
|
|
518
|
+
elif isinstance(e, requests.exceptions.ConnectionError):
|
|
519
|
+
sys.exit("[!] Failed to connect to the server")
|
|
520
|
+
else:
|
|
521
|
+
sys.exit(f"[!] Request failed: {e}")
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
if __name__ == "__main__":
|
|
525
|
+
main()
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "header-analyzer"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "A CLI tool for analyzing HTTP security headers"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
requires-python = ">=3.8"
|
|
12
|
+
dependencies = [
|
|
13
|
+
"requests>=2.28.0",
|
|
14
|
+
]
|
|
15
|
+
keywords = ["security", "http", "headers", "infosec", "cli", "hsts"]
|
|
16
|
+
authors = [
|
|
17
|
+
{ name = "negoro26" },
|
|
18
|
+
]
|
|
19
|
+
classifiers = [
|
|
20
|
+
"Development Status :: 4 - Beta",
|
|
21
|
+
"Environment :: Console",
|
|
22
|
+
"Intended Audience :: Developers",
|
|
23
|
+
"Intended Audience :: Information Technology",
|
|
24
|
+
"Intended Audience :: System Administrators",
|
|
25
|
+
"License :: OSI Approved :: MIT License",
|
|
26
|
+
"Operating System :: OS Independent",
|
|
27
|
+
"Programming Language :: Python :: 3",
|
|
28
|
+
"Programming Language :: Python :: 3.8",
|
|
29
|
+
"Programming Language :: Python :: 3.9",
|
|
30
|
+
"Programming Language :: Python :: 3.10",
|
|
31
|
+
"Programming Language :: Python :: 3.11",
|
|
32
|
+
"Programming Language :: Python :: 3.12",
|
|
33
|
+
"Topic :: Security",
|
|
34
|
+
"Topic :: Internet :: WWW/HTTP",
|
|
35
|
+
"Topic :: Utilities",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[project.urls]
|
|
39
|
+
Homepage = "https://github.com/negoro26/Header-analyzer"
|
|
40
|
+
Repository = "https://github.com/negoro26/Header-analyzer.git"
|
|
41
|
+
Issues = "https://github.com/negoro26/Header-analyzer/issues"
|
|
42
|
+
|
|
43
|
+
[project.scripts]
|
|
44
|
+
header-analyzer = "header_scanner:main"
|
|
45
|
+
|
|
46
|
+
[tool.setuptools]
|
|
47
|
+
py-modules = ["header_scanner"]
|