Lewati ke konten
Kembali ke Blog

Hugo Security Best Practices: Mengamankan Website Hugo

· · 8 menit baca

Meskipun Hugo sites secara inheren lebih aman karena sifatnya yang static, tetap ada aspek security yang perlu diperhatikan untuk memastikan website Anda terlindungi dari berbagai threats. Dalam panduan ini, kita akan membahas best practices untuk mengamankan website Hugo, mulai dari server configuration hingga content security.

Static sites memiliki attack surface yang lebih kecil dibandingkan dynamic sites karena tidak ada database atau server-side processing yang bisa dieksploitasi. Namun, ini tidak berarti website static sepenuhnya immune dari security issues. Ada beberapa area yang perlu perhatian khusus untuk memastikan security yang comprehensive.

Server Security

Firewall Configuration

# Install and configure UFW
sudo apt install ufw

Set default policies

sudo ufw default deny incoming sudo ufw default allow outgoing

Allow SSH

sudo ufw allow ssh

Allow HTTP and HTTPS

sudo ufw allow 80/tcp sudo ufw allow 443/tcp

Enable UFW

sudo ufw enable

Check status

sudo ufw status verbose

SSH Hardening

# /etc/ssh/sshd_config

Disable root login

PermitRootLogin no

Disable password authentication

PasswordAuthentication no

Use only protocol 2

Protocol 2

Limit login attempts

MaxAuthTries 3 MaxSessions 5

Idle timeout

ClientAliveInterval 300 ClientAliveCountMax 2

Banner

Banner /etc/ssh/banner

# Generate SSH key
ssh-keygen -t ed25519 -C "[email protected]"

Copy public key to server

ssh-copy-id user@server-ip

Restart SSH

sudo systemctl restart sshd

Fail2ban Configuration

# /etc/fail2ban/jail.local

[DEFAULT] bantime = 3600 findtime = 600 maxretry = 3

[sshd] enabled = true port = ssh filter = sshd logpath = /var/log/auth.log maxretry = 3 bantime = 3600

[nginx-http-auth] enabled = true filter = nginx-http-auth port = http,https logpath = /var/log/nginx/error.log maxretry = 3 bantime = 3600

[nginx-noscript] enabled = true port = http,https logpath = /var/log/nginx/access.log maxretry = 6 bantime = 86400

Web Server Security

Nginx Security Headers

# /etc/nginx/conf.d/security-headers.conf

Security headers

add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header X-XSS-Protection "1; mode=block" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' https:;" always; add_header Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=()" always; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

Rate Limiting

# /etc/nginx/conf.d/rate-limit.conf

Rate limit zones

limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s; limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s; limit_conn_zone $binary_remote_addr zone=conn:10m;

Apply rate limiting

server { location /api/ { limit_req zone=api burst=20 nodelay; }

location /login {
    limit_req zone=login burst=5 nodelay;
}

location / {
    limit_conn conn 10;
}

}

Disable Unnecessary Features

# Disable server version in headers
server_tokens off;

Disable directory listing

autoindex off;

Disable .htaccess (for Apache compatibility)

location ~ /.ht { deny all; }

SSL/TLS Configuration

Let’s Encrypt Setup

# Install Certbot
sudo apt install certbot python3-certbot-nginx

Obtain certificate

sudo certbot --nginx -d example.com -d www.example.com

Verify auto-renewal

sudo certbot renew --dry-run

Enable auto-renewal

sudo systemctl enable certbot.timer

SSL Configuration

# /etc/nginx/snippets/ssl-params.conf

SSL configuration

ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers off; ssl_session_cache shared:SSL:50m; ssl_session_timeout 1d; ssl_session_tickets off;

OCSP Stapling

ssl_stapling on; ssl_stapling_verify on; resolver 8.8.8.8 8.8.4.4 valid=300s; resolver_timeout 5s;

Diffie-Hellman

ssl_dhparam /etc/nginx/dhparam.pem;

Generate DH parameters

sudo openssl dhparam -out /etc/nginx/dhparam.pem 2048

File System Security

Proper Permissions

#!/bin/bash
# scripts/set-permissions.sh

WEB_ROOT="/var/www/example.com" USER="www-data" GROUP="www-data"

Set owner

sudo chown -R ${USER}:${GROUP} ${WEB_ROOT}

Set directory permissions

sudo find ${WEB_ROOT} -type d -exec chmod 755 {} \;

Set file permissions

sudo find ${WEB_ROOT} -type f -exec chmod 644 {} \;

Secure sensitive files

sudo chmod 600 /etc/nginx/conf.d/ sudo chmod 600 /etc/letsencrypt/

Log files should be writable but not readable by all

sudo chmod 640 /var/log/nginx/

Protect Sensitive Files

# Protect .git, .env, and other sensitive files
location ~ /\. {
    deny all;
    access_log off;
    log_not_found off;
}

location ~ /.env { deny all; }

location ~ /.git { deny all; }

Content Security Policy

CSP Implementation

# Strict CSP
add_header Content-Security-Policy "
    default-src 'self';
    script-src 'self' 'unsafe-inline' 'unsafe-eval' https://www.google-analytics.com https://www.googletagmanager.com;
    style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
    font-src 'self' https://fonts.gstatic.com;
    img-src 'self' data: https:;
    connect-src 'self' https://www.google-analytics.com;
    frame-src 'self';
    object-src 'none';
    base-uri 'self';
    form-action 'self';
    frame-ancestors 'self';
" always;

CSP Report-Only

# For testing CSP
add_header Content-Security-Policy-Report-Only "
    default-src 'self';
    report-uri /csp-report;
    report-to csp-endpoint;
" always;

API Security

API Gateway Configuration

# API rate limiting dan authentication
location /api/ {
    # Rate limiting
    limit_req zone=api burst=20 nodelay;
# JWT verification (with nginx-jwt module)
jwt_issuer "your-issuer";
jwt_signature_key file:/etc/nginx/jwt.key;

# Validate claims
jwt_claim_exp 3600;

# Forward to backend
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;

}

Input Validation

Untuk API endpoints, selalu validasi input:

// Example: Validate API input
function validateInput(body) {
    const schema = {
        name: { type: 'string', maxLength: 100 },
        email: { type: 'string', pattern: /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/ },
        age: { type: 'number', min: 0, max: 150 }
    };
for (const [key, rules] of Object.entries(schema)) {
    if (body[key] === undefined && !rules.optional) {
        throw new Error(`Missing required field: ${key}`);
    }

    if (body[key] !== undefined) {
        if (typeof body[key] !== rules.type) {
            throw new Error(`Invalid type for ${key}`);
        }

        if (rules.pattern && !rules.pattern.test(body[key])) {
            throw new Error(`Invalid format for ${key}`);
        }

        if (rules.maxLength && body[key].length > rules.maxLength) {
            throw new Error(`${key} too long`);
        }
    }
}

return true;

}

Monitoring dan Alerting

Log Configuration

# /etc/nginx/conf.d/logging.conf

Custom log format with more details

log_format detailed '$remote_addr - $remote_user [$time_local] ' '"$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent" ' '$request_time $upstream_response_time';

access_log /var/log/nginx/access.log detailed; error_log /var/log/nginx/error.log warn;

Security Monitoring Script

#!/bin/bash
# scripts/security-monitor.sh

LOG_FILE="/var/log/nginx/access.log" REPORT_FILE="/tmp/security-report-$(date +%Y%m%d).txt"

Failed login attempts

echo "=== Failed Login Attempts ===" > ${REPORT_FILE} grep "POST /login" ${LOG_FILE} | \ awk '{print $1}' | sort | uniq -c | sort -rn | \ awk '$1 > 5 {print}' >> ${REPORT_FILE}

404 scans

echo -e "\n=== Potential 404 Scans ===" >> ${REPORT_FILE} awk '$9 == 404 {print $7}' ${LOG_FILE} | \ sort | uniq -c | sort -rn | \ awk '$1 > 10 {print}' >> ${REPORT_FILE}

SQL Injection attempts

echo -e "\n=== Potential SQL Injection ===" >> ${REPORT_FILE} grep -E "(union|select|insert|update|delete|drop|alter)" ${LOG_FILE} | \ awk '{print $1, $7}' | sort | uniq -c | sort -rn | \ head -20 >> ${REPORT_FILE}

XSS attempts

echo -e "\n=== Potential XSS Attempts ===" >> ${REPORT_FILE} grep -E "(<script|javascript:|onerror|onload|alert()" ${LOG_FILE} | \ awk '{print $1, $7}' | sort | uniq -c | sort -rn | \ head -20 >> ${REPORT_FILE}

Send report

mail -s "Daily Security Report" [email protected] < ${REPORT_FILE}

GitHub Actions Security

Secret Management

# .github/workflows/deploy.yml
name: Deploy

on: push: branches: [main]

jobs: deploy: runs-on: ubuntu-latest steps:

  • uses: actions/checkout@v4
  - name: Deploy
    run: |
      # Use secrets from GitHub
      echo &quot;${{ secrets.SSH_PRIVATE_KEY }}&quot; &gt; deploy_key
      chmod 600 deploy_key
      scp -i deploy_key files user@server:/path/
    env:
      # Secrets available as environment variables
      API_TOKEN: ${{ secrets.API_TOKEN }}

Dependency Scanning

# .github/workflows/security-scan.yml
name: Security Scan

on: push: branches: [main]

jobs: security-scan: runs-on: ubuntu-latest steps:

  • uses: actions/checkout@v4
  - name: Run Trivy
    uses: aquasecurity/trivy-action@master
    with:
      scan-type: 'fs'
      scan-ref: '.'
      severity: 'CRITICAL,HIGH'
      format: 'sarif'
      output: 'trivy-results.sarif'

  - name: Upload Trivy results
    uses: github/codeql-action/upload-sarif@v2
    with:
      sarif_file: 'trivy-results.sarif'

Regular Security Audits

Automated Audit Script

#!/bin/bash
# scripts/security-audit.sh

echo "=== Hugo Site Security Audit ===" echo "Date: $(date)" echo ""

Check SSL certificate

echo "=== SSL Certificate ===" echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null | openssl x509 -noout -dates

Check security headers

echo -e "\n=== Security Headers ===" curl -I https://example.com 2>/dev/null | grep -i "strict-transport|x-frame|x-content-type|x-xss|content-security"

Check for outdated dependencies

echo -e "\n=== Dependencies ===" if [ -f "package.json" ]; then npm audit --audit-level=high fi

Check file permissions

echo -e "\n=== File Permissions ===" find . -name "*.env" -exec ls -la {} \; 2>/dev/null

echo -e "\n=== Audit Complete ==="

Best Practices Summary

AreaPractice
ServerFirewall, SSH hardening, Fail2ban
Web ServerSecurity headers, Rate limiting, SSL/TLS
FilesystemProper permissions, Protect sensitive files
CSPStrict policy, Report-only mode for testing
MonitoringLog analysis, Alerting, Regular audits
CI/CDSecret management, Dependency scanning

Kesimpulan

Meskipun Hugo sites secara inheren lebih aman, security yang comprehensive tetap essential. Dengan mengimplementasikan best practices yang dibahas dalam panduan ini, website Hugo Anda akan memiliki security posture yang kuat terhadap berbagai threats.

Ditulis oleh

Hendra Wijaya

Tinggalkan Komentar

Email tidak akan ditampilkan.