7c3f01c9eb
12 funciones Bash del dominio cybersecurity: auditoria de red y servicios (analyze_dns, audit_http_headers, inspect_ssl_cert, list_active_connections, enumerate_subdomains, geolocate_ip), auditoria de sistema (audit_ssh_config, check_firewall, detect_suspicious_users), y utilidades crypto (encrypt_file, generate_password, verify_file_hash). Dominio nuevo en bash/functions/.
170 lines
6.0 KiB
Bash
170 lines
6.0 KiB
Bash
#!/usr/bin/env bash
|
|
# inspect_ssl_cert
|
|
# ----------------
|
|
# Inspecciona el certificado SSL/TLS de un host: sujeto, emisor, fechas de validez,
|
|
# SANs, cadena de confianza y versiones de TLS aceptadas (detecta TLS 1.0/1.1 inseguros).
|
|
#
|
|
# USO (directo):
|
|
# inspect_ssl_cert <host[:puerto]>
|
|
# inspect_ssl_cert example.com
|
|
# inspect_ssl_cert example.com:8443
|
|
#
|
|
# Depende de: openssl, timeout
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
source "$SCRIPT_DIR/../shell/bash_colors.sh"
|
|
source "$SCRIPT_DIR/../shell/bash_log.sh"
|
|
bash_colors
|
|
bash_log_init
|
|
|
|
# ─── Constantes ───────────────────────────────────────────────────────────────
|
|
|
|
_SSL_WARN_DAYS=30
|
|
|
|
# ─── Funciones puras ──────────────────────────────────────────────────────────
|
|
|
|
_ssl_parse_host_port() {
|
|
local input="$1"
|
|
if [[ "$input" =~ ^(.+):([0-9]+)$ ]]; then
|
|
echo "${BASH_REMATCH[1]} ${BASH_REMATCH[2]}"
|
|
else
|
|
echo "${input} 443"
|
|
fi
|
|
}
|
|
|
|
_ssl_days_until_expiry() {
|
|
local expiry_str="$1"
|
|
local expiry_epoch
|
|
expiry_epoch="$(date -d "$expiry_str" +%s 2>/dev/null || echo 0)"
|
|
local now_epoch
|
|
now_epoch="$(date +%s)"
|
|
echo $(( (expiry_epoch - now_epoch) / 86400 ))
|
|
}
|
|
|
|
# ─── Funciones de efecto ──────────────────────────────────────────────────────
|
|
|
|
_ssl_fetch_subject() {
|
|
local host="$1"
|
|
local port="$2"
|
|
echo | timeout 10 openssl s_client -connect "${host}:${port}" -servername "$host" 2>/dev/null \
|
|
| openssl x509 -noout -subject -issuer 2>/dev/null
|
|
}
|
|
|
|
_ssl_fetch_dates() {
|
|
local host="$1"
|
|
local port="$2"
|
|
echo | timeout 10 openssl s_client -connect "${host}:${port}" -servername "$host" 2>/dev/null \
|
|
| openssl x509 -noout -dates 2>/dev/null
|
|
}
|
|
|
|
_ssl_fetch_san() {
|
|
local host="$1"
|
|
local port="$2"
|
|
echo | timeout 10 openssl s_client -connect "${host}:${port}" -servername "$host" 2>/dev/null \
|
|
| openssl x509 -noout -ext subjectAltName 2>/dev/null \
|
|
| grep -oE 'DNS:[^,]+' | sed 's/DNS://g' | tr '\n' ' '
|
|
}
|
|
|
|
_ssl_fetch_chain() {
|
|
local host="$1"
|
|
local port="$2"
|
|
echo | timeout 10 openssl s_client -connect "${host}:${port}" -servername "$host" \
|
|
-showcerts 2>/dev/null \
|
|
| grep -E "^(subject|issuer)=" | sed 's/^/ /'
|
|
}
|
|
|
|
_ssl_check_tls_version() {
|
|
local host="$1"
|
|
local port="$2"
|
|
local proto="$3"
|
|
local label="$4"
|
|
if echo | timeout 5 openssl s_client -connect "${host}:${port}" \
|
|
-servername "$host" "${proto}" 2>/dev/null | grep -q "Cipher"; then
|
|
echo -e " ${RED}[x]${NC} ${label} -- soportado (inseguro)"
|
|
else
|
|
echo -e " ${GREEN}[ok]${NC} ${label} no soportado"
|
|
fi
|
|
}
|
|
|
|
# ─── Punto de entrada ─────────────────────────────────────────────────────────
|
|
|
|
inspect_ssl_cert() {
|
|
local input="$1"
|
|
|
|
if [[ -z "$input" ]]; then
|
|
error "inspect_ssl_cert: se requiere un host como argumento (ej: example.com o example.com:8443)" >&2
|
|
return 1
|
|
fi
|
|
|
|
if ! command -v openssl &>/dev/null; then
|
|
error "inspect_ssl_cert: 'openssl' no está instalado (sudo apt install openssl)" >&2
|
|
return 1
|
|
fi
|
|
|
|
local host port
|
|
read -r host port <<< "$(_ssl_parse_host_port "$input")"
|
|
|
|
info "Conectando a ${host}:${port}..."
|
|
echo ""
|
|
|
|
local subj_issuer
|
|
subj_issuer="$(_ssl_fetch_subject "$host" "$port")"
|
|
if [[ -z "$subj_issuer" ]]; then
|
|
error "inspect_ssl_cert: no se pudo obtener el certificado. ¿El host está disponible?" >&2
|
|
return 1
|
|
fi
|
|
|
|
local subject issuer
|
|
subject="$(echo "$subj_issuer" | grep ^subject | cut -d= -f2- | xargs)"
|
|
issuer="$(echo "$subj_issuer" | grep ^issuer | cut -d= -f2- | xargs)"
|
|
|
|
local dates
|
|
dates="$(_ssl_fetch_dates "$host" "$port")"
|
|
local not_before not_after
|
|
not_before="$(echo "$dates" | grep notBefore | cut -d= -f2)"
|
|
not_after="$(echo "$dates" | grep notAfter | cut -d= -f2)"
|
|
|
|
local days_left
|
|
days_left="$(_ssl_days_until_expiry "$not_after")"
|
|
|
|
local sans
|
|
sans="$(_ssl_fetch_san "$host" "$port")"
|
|
|
|
echo -e "${PURPLE}════════ Certificado SSL/TLS ════════════════════${NC}"
|
|
echo -e " ${CYAN}Sujeto:${NC} ${subject}"
|
|
echo -e " ${CYAN}Emisor:${NC} ${issuer}"
|
|
echo -e " ${CYAN}Válido desde:${NC} ${not_before}"
|
|
echo -e " ${CYAN}Válido hasta:${NC} ${not_after}"
|
|
echo ""
|
|
|
|
if [[ $days_left -le 0 ]]; then
|
|
echo -e " ${RED}[x] CERTIFICADO EXPIRADO${NC}"
|
|
elif [[ $days_left -le $_SSL_WARN_DAYS ]]; then
|
|
echo -e " ${YELLOW}[!] Expira en ${days_left} días -- renovar pronto${NC}"
|
|
else
|
|
echo -e " ${GREEN}[ok] Válido -- expira en ${days_left} días${NC}"
|
|
fi
|
|
|
|
echo ""
|
|
echo -e " ${CYAN}SANs:${NC}"
|
|
echo "$sans" | tr ' ' '\n' | grep -v '^$' | while IFS= read -r san; do
|
|
echo -e " ${GREEN}*${NC} ${san}"
|
|
done
|
|
|
|
echo ""
|
|
echo -e "${PURPLE}════════ Cadena de confianza ════════════════════${NC}"
|
|
_ssl_fetch_chain "$host" "$port"
|
|
|
|
echo ""
|
|
echo -e "${PURPLE}════════ Versiones TLS aceptadas ════════════════${NC}"
|
|
_ssl_check_tls_version "$host" "$port" "-tls1" "TLS 1.0"
|
|
_ssl_check_tls_version "$host" "$port" "-tls1_1" "TLS 1.1"
|
|
echo -e " ${GREEN}[ok]${NC} TLS 1.2 / 1.3 (estándar)"
|
|
echo -e "${PURPLE}═════════════════════════════════════════════════${NC}"
|
|
}
|
|
|
|
# Ejecutar si se llama directamente
|
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|
inspect_ssl_cert "$@"
|
|
fi
|