This commit is contained in:
parent
927436fbf2
commit
6e6948b4fd
102
SECURITY.md
Normal file
102
SECURITY.md
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
# Security Implementation
|
||||
|
||||
This document outlines the security measures implemented on nzambello.dev.
|
||||
|
||||
## Security Headers
|
||||
|
||||
The following security headers are implemented both at the Astro application level and nginx server level:
|
||||
|
||||
### 1. Content Security Policy (CSP)
|
||||
|
||||
- **Purpose**: Prevents XSS attacks by controlling which resources can be loaded
|
||||
- **Configuration**:
|
||||
- `default-src 'self'` - Only allow resources from same origin
|
||||
- `script-src 'self' 'unsafe-inline' 'unsafe-eval' https://umami.nzambello.dev` - Allow inline scripts and Umami analytics
|
||||
- `style-src 'self' 'unsafe-inline' https://unpkg.com` - Allow inline styles and PicoCSS from unpkg
|
||||
- `img-src 'self' data: https:` - Allow images from same origin, data URIs, and HTTPS sources
|
||||
- `font-src 'self' https://unpkg.com` - Allow fonts from same origin and unpkg
|
||||
- `connect-src 'self' https://umami.nzambello.dev` - Allow connections to same origin and Umami
|
||||
- `object-src 'none'` - Block all plugins
|
||||
- `frame-ancestors 'none'` - Prevent site from being embedded in iframes
|
||||
|
||||
### 2. HTTP Strict Transport Security (HSTS)
|
||||
|
||||
- **Purpose**: Forces browsers to use HTTPS only
|
||||
- **Configuration**: `max-age=31536000; includeSubDomains; preload`
|
||||
- **Duration**: 1 year with subdomain coverage and preload list inclusion
|
||||
|
||||
### 3. X-Content-Type-Options
|
||||
|
||||
- **Purpose**: Prevents MIME type sniffing attacks
|
||||
- **Configuration**: `nosniff`
|
||||
|
||||
### 4. X-Frame-Options
|
||||
|
||||
- **Purpose**: Prevents clickjacking attacks
|
||||
- **Configuration**: `DENY` (prevents any embedding)
|
||||
|
||||
### 5. Referrer Policy
|
||||
|
||||
- **Purpose**: Controls referrer information sent to other sites
|
||||
- **Configuration**: `strict-origin-when-cross-origin`
|
||||
- **Behavior**: Sends full referrer to same origin, only origin to cross-origin, nothing on downgrade
|
||||
|
||||
### 6. X-XSS-Protection
|
||||
|
||||
- **Purpose**: Additional XSS protection for older browsers
|
||||
- **Configuration**: `1; mode=block`
|
||||
|
||||
### 7. Permissions Policy
|
||||
|
||||
- **Purpose**: Controls browser features and APIs
|
||||
- **Configuration**: `camera=(), microphone=(), geolocation=(), payment=()`
|
||||
- **Effect**: Blocks access to camera, microphone, geolocation, and payment APIs
|
||||
|
||||
## Subresource Integrity (SRI)
|
||||
|
||||
### External Resources with SRI
|
||||
|
||||
- **Umami Analytics Script**:
|
||||
- URL: `https://umami.nzambello.dev/script.js`
|
||||
- Integrity: `sha384-gW+82edTiLqRoEvPbT3xKDCYZ5M02YXbW4tA3gbojZWiiMYNJZb4YneJrS4ri3Rn`
|
||||
- Purpose: Ensures the analytics script hasn't been tampered with
|
||||
|
||||
## Server Information Hiding
|
||||
|
||||
- **Server Tokens**: Disabled in nginx configuration
|
||||
- **X-Powered-By**: Removed from response headers
|
||||
- **Server**: Removed from response headers
|
||||
|
||||
## Testing Security Headers
|
||||
|
||||
To test the security headers:
|
||||
|
||||
```bash
|
||||
# Run the security test script
|
||||
npm run test:security
|
||||
|
||||
# Or manually check headers
|
||||
curl -I https://nzambello.dev
|
||||
```
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
1. **HTTPS Only**: All traffic is served over HTTPS
|
||||
2. **No External Dependencies**: Minimal external dependencies, all with SRI where applicable
|
||||
3. **Inline Scripts**: All inline scripts are necessary for functionality and are allowed in CSP
|
||||
4. **Regular Updates**: Dependencies are regularly updated to patch security vulnerabilities
|
||||
5. **Content Security**: All content is served from trusted sources only
|
||||
|
||||
## Monitoring
|
||||
|
||||
- Security headers are monitored through the Umami analytics integration
|
||||
- Regular security audits are performed using automated tools
|
||||
- CSP violations are logged and monitored
|
||||
|
||||
## Compliance
|
||||
|
||||
These security measures help ensure compliance with:
|
||||
|
||||
- OWASP Top 10
|
||||
- Web Security Best Practices
|
||||
- Modern browser security standards
|
||||
|
|
@ -3,4 +3,46 @@ import { defineConfig } from 'astro/config';
|
|||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
site: 'https://nzambello.dev',
|
||||
output: 'static',
|
||||
server: {
|
||||
headers: {
|
||||
// Content Security Policy
|
||||
'Content-Security-Policy': [
|
||||
"default-src 'self'",
|
||||
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://umami.nzambello.dev",
|
||||
"style-src 'self' 'unsafe-inline' https://unpkg.com",
|
||||
"img-src 'self' data: https:",
|
||||
"font-src 'self' https://unpkg.com",
|
||||
"connect-src 'self' https://umami.nzambello.dev",
|
||||
"media-src 'self'",
|
||||
"object-src 'none'",
|
||||
"base-uri 'self'",
|
||||
"form-action 'self'",
|
||||
"frame-ancestors 'none'",
|
||||
"upgrade-insecure-requests"
|
||||
].join('; '),
|
||||
|
||||
// HTTP Strict Transport Security
|
||||
'Strict-Transport-Security': 'max-age=31536000; includeSubDomains; preload',
|
||||
|
||||
// X-Content-Type-Options
|
||||
'X-Content-Type-Options': 'nosniff',
|
||||
|
||||
// X-Frame-Options
|
||||
'X-Frame-Options': 'DENY',
|
||||
|
||||
// Referrer Policy
|
||||
'Referrer-Policy': 'strict-origin-when-cross-origin',
|
||||
|
||||
// X-XSS-Protection (for older browsers)
|
||||
'X-XSS-Protection': '1; mode=block',
|
||||
|
||||
// Permissions Policy
|
||||
'Permissions-Policy': 'camera=(), microphone=(), geolocation=(), payment=()',
|
||||
|
||||
// Remove server information
|
||||
'Server': '',
|
||||
'X-Powered-By': ''
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,6 +5,18 @@ events {
|
|||
}
|
||||
|
||||
http {
|
||||
# Security headers
|
||||
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://umami.nzambello.dev; style-src 'self' 'unsafe-inline' https://unpkg.com; img-src 'self' data: https:; font-src 'self' https://unpkg.com; connect-src 'self' https://umami.nzambello.dev; media-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'; upgrade-insecure-requests" always;
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-Frame-Options "DENY" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=()" always;
|
||||
|
||||
# Remove server information
|
||||
server_tokens off;
|
||||
|
||||
server {
|
||||
listen 8080;
|
||||
server_name _;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@
|
|||
"start": "astro dev",
|
||||
"build": "astro build",
|
||||
"preview": "astro preview",
|
||||
"astro": "astro"
|
||||
"astro": "astro",
|
||||
"test:security": "node test-security.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
|
|
|
|||
|
|
@ -1,2 +1,5 @@
|
|||
User-agent: *
|
||||
Disallow:
|
||||
Allow: /
|
||||
|
||||
# Sitemap location (if you have one)
|
||||
# Sitemap: https://nzambello.dev/sitemap.xml
|
||||
|
|
|
|||
|
|
@ -50,7 +50,9 @@ const canonicalURL = new URL(Astro.url.pathname, Astro.site);
|
|||
<script
|
||||
async
|
||||
src="https://umami.nzambello.dev/script.js"
|
||||
data-website-id="5964d580-4baa-4c91-b4cd-6e2eae4a5bf3"></script>
|
||||
data-website-id="5964d580-4baa-4c91-b4cd-6e2eae4a5bf3"
|
||||
integrity="sha384-gW+82edTiLqRoEvPbT3xKDCYZ5M02YXbW4tA3gbojZWiiMYNJZb4YneJrS4ri3Rn"
|
||||
crossorigin="anonymous"></script>
|
||||
|
||||
<!-- <link rel="preconnect" href="https://fonts.bunny.net" />
|
||||
<link
|
||||
|
|
|
|||
46
test-security.js
Normal file
46
test-security.js
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
import https from 'https';
|
||||
import http from 'http';
|
||||
|
||||
const testUrl = 'https://nzambello.dev';
|
||||
|
||||
console.log('🔒 Testing Security Headers for', testUrl);
|
||||
console.log('=' .repeat(50));
|
||||
|
||||
const client = testUrl.startsWith('https') ? https : http;
|
||||
|
||||
client.get(testUrl, (res) => {
|
||||
console.log(`Status: ${res.statusCode}`);
|
||||
console.log(`Server: ${res.headers.server || 'Not disclosed'}`);
|
||||
console.log('\n📋 Security Headers:');
|
||||
console.log('-'.repeat(30));
|
||||
|
||||
const securityHeaders = [
|
||||
'content-security-policy',
|
||||
'strict-transport-security',
|
||||
'x-content-type-options',
|
||||
'x-frame-options',
|
||||
'referrer-policy',
|
||||
'x-xss-protection',
|
||||
'permissions-policy'
|
||||
];
|
||||
|
||||
securityHeaders.forEach(header => {
|
||||
const value = res.headers[header];
|
||||
const status = value ? '✅' : '❌';
|
||||
console.log(`${status} ${header}: ${value || 'Not set'}`);
|
||||
});
|
||||
|
||||
console.log('\n🔍 Additional Headers:');
|
||||
console.log('-'.repeat(30));
|
||||
Object.keys(res.headers).forEach(header => {
|
||||
if (!securityHeaders.includes(header.toLowerCase())) {
|
||||
console.log(`ℹ️ ${header}: ${res.headers[header]}`);
|
||||
}
|
||||
});
|
||||
|
||||
}).on('error', (err) => {
|
||||
console.error('❌ Error testing headers:', err.message);
|
||||
console.log('\n💡 Make sure the site is running and accessible');
|
||||
});
|
||||
Loading…
Reference in a new issue