RUN sed -i -r "s|(\s+listen\s+)[0-9]+;|\1$port;|" /usr/local/nginx/conf/nginx.conf
时间: 2024-06-02 08:08:38 浏览: 109
这是一行用于修改 Nginx 配置文件中监听端口的命令。具体来说,它使用 sed 工具对文件 /usr/local/nginx/conf/nginx.conf 进行操作,并将其中的 listen 指令中的端口号替换为变量 $port 的值。其中:
- s|(\s+listen\s+)[0-9]+;|\1$port;| 表示将满足正则表达式 (\s+listen\s+)[0-9]+; 的行中的端口号替换为 $port 的值。
- (\s+listen\s+) 表示匹配 listen 指令,其中 \s+ 表示一个或多个空白字符。
- [0-9]+ 表示匹配一个或多个数字。
- \1 表示替换为匹配到的第一个子组,也就是 (\s+listen\s+) 匹配到的内容。
- $port 表示替换为变量 $port 的值。
- 最后的 / 表示 sed 命令结束。
这个命令通常用于在部署 Web 服务器时动态配置 Nginx 的监听端口。
相关问题
dropbear的config配置文件如下:config dropbear option PasswordAuth 'on' option RootPasswordAuth 'on' option Port '22' # option BannerFile '/etc/banner' dropbear的启动init.d文件如下:#!/bin/sh /etc/rc.common # Copyright (C) 2006-2010 OpenWrt.org # Copyright (C) 2006 Carlos Sobrinho START=50 STOP=50 SERVICE_USE_PID=1 NAME=dropbear PROG=/usr/sbin/dropbear PIDCOUNT=0 EXTRA_COMMANDS="killclients" EXTRA_HELP=" killclients Kill ${NAME} processes except servers and yourself" PATH_SSH_ALGO_VERSION="/tmp/ssh_algo_version" dropbear_start() { append_ports() { local ifname="$1" local port="$2" grep -qs "^ *$ifname:" /proc/net/dev || { append args "-p $port" return } for addr in $( ifconfig "$ifname" | sed -ne ' /addr: *fe[89ab][0-9a-f]:/d s/.* addr: *\([0-9a-f:\.]*\).*/\1/p ' ); do append args "-p $addr:$port" done } local section="$1" # check if section is enabled (default) local enabled config_get_bool enabled "${section}" enable 1 [ "${enabled}" -eq 0 ] && return 1 # verbose parameter local verbosed config_get_bool verbosed "${section}" verbose 0 # increase pid file count to handle multiple instances correctly PIDCOUNT="$(( ${PIDCOUNT} + 1))" # prepare parameters (initialise with pid file) local pid_file="/var/run/${NAME}.${PIDCOUNT}.pid" local args="-P $pid_file" local val # A) password authentication config_get_bool val "${section}" PasswordAuth 1 [ "${val}" -eq 0 ] && append args "-s" # B) listen interface and port local port local interface config_get interface "${section}" Interface config_get interface "${interface}" ifname "$interface" config_get port "${section}" Port 22 append_ports "$interface" "$port" # C) banner file config_get val "${section}" BannerFile [ -f "${val}" ] && append args "-b ${val}" # D) gatewayports config_get_bool val "${section}" GatewayPorts 0 [ "${val}" -eq 1 ] && append args "-a" # E) root password authentication config_get_bool val "${section}" RootPasswordAuth 1 [ "${val}" -eq 0 ] && append args "-g" # F) root login config_get_bool val "${section}" RootLogin 1 [ "${val}" -eq 0 ] && append args "-w" # G) host keys config_get val "${section}" rsakeyfile [ -f "${val}" ] && append args "-r ${val}" config_get val "${section}" dsskeyfile [ -f "${val}" ] && append args "-d ${val}" # H) enable ssh session login #config_get_bool val "${section}" RemoteSSH 0 #[ "${val}" -eq 1 ] && append args "-L" # I) linux account login config_get_bool val "${section}" SysAccountLogin 1 [ "${val}" -eq 0 ] && append args "-C" # 6)Disable ipv6 socket config_get_bool val "${section}" DisableIpv6 0 [ "${val}" -eq 1 ] && append args "-6" #enable allowblankpass append args "-B" # execute program and return its exit code [ "${verbosed}" -ne 0 ] && echo "${initscript}: section ${section} starting ${PROG} ${args}" SERVICE_PID_FILE="$pid_file" service_start ${PROG} ${args} } keygen() { local rsa2048_enable=$(uci get system.system.rsa2048_enable) local bits=1024 if [ "$rsa2048_enable" == "true" ]; then bits=2048 fi for keytype in rsa; do # check for keys key=dropbear/dropbear_${keytype}_host_key [ -f /tmp/$key -o -s /etc/$key ] || { # generate missing keys # mkdir -p /tmp/dropbear mkdir -p /tmp/dropbear/succ_cli [ -x /usr/bin/dropbearkey ] && { /usr/bin/dropbearkey -t $keytype -f /tmp/$key -s $bits 2>&- >&- && exec /etc/rc.common "$initscript" start } & #exit 0 } done lock /tmp/.switch2jffs mkdir -p /etc/dropbear mv /tmp/dropbear/dropbear_rsa_host_key /etc/dropbear/ lock -u /tmp/.switch2jffs chown root /etc/dropbear chmod 0700 /etc/dropbear } guest_portal() { if [ ! -d "/tmp/guest_portal" ]; then mkdir -p /tmp/guest_portal mount -t tmpfs -o rw,noatime,size=2m,mode=755 none /tmp/guest_portal chown admin:1000 /tmp/guest_portal fi } keygen_ecdsa() { for keytype in ecdsa; do # check for keys key=dropbear/dropbear_${keytype}_host_key [ -f /tmp/$key -o -s /etc/$key ] || { # generate missing keys mkdir -p /tmp/dropbear [ -x /usr/bin/dropbearkey ] && { /usr/bin/dropbearkey -t $keytype -f /tmp/$key -s 521 2>&- >&- && exec /etc/rc.common "$initscript" start } & #exit 0 } done lock /tmp/.switch2jffs mkdir -p /etc/dropbear mv /tmp/dropbear/dropbear_ecdsa_host_key /etc/dropbear/ lock -u /tmp/.switch2jffs chown root /etc/dropbear chmod 0700 /etc/dropbear } start() { [ -s /etc/dropbear/dropbear_rsa_host_key ] || keygen extern_partition=$(uci get profile.@backup_restore[0].extern_partition -c "/etc/profile.d" -q) for img_type in $extern_partition; do if [ "$img_type" = "portal-logo" ] || [ "$img_type" = "portal-back" ]; then guest_portal break fi done [ -s /etc/dropbear/dropbear_ecdsa_host_key ] || keygen_ecdsa local algo_version=`uci get dropbear.dropbear.AlgoVersion` [ -z "$algo_version" ] && { algo_version=0 } if [ ! -f $PATH_SSH_ALGO_VERSION ]; then touch $PATH_SSH_ALGO_VERSION echo $algo_version > $PATH_SSH_ALGO_VERSION fi # disable all client access dropbear default [ -f "/sbin/knock_functions.sh" ] && /sbin/knock_functions.sh start include /lib/network scan_interfaces config_load "${NAME}" config_foreach dropbear_start dropbear } stop() { local pid_file pid_files pid_files=`ls /var/run/${NAME}.*.pid 2>/dev/null` [ -z "$pid_files" ] && return 1 for pid_file in $pid_files; do SERVICE_PID_FILE="$pid_file" service_stop ${PROG} && { rm -f ${pid_file} } done } killclients() { local ignore='' local server local pid # if this script is run from inside a client session, then ignore that session pid="$$" while [ "${pid}" -ne 0 ] do # get parent process id pid=`cut -d ' ' -f 4 "/proc/${pid}/stat"` [ "${pid}" -eq 0 ] && break # check if client connection grep -F -q -e "${PROG}" "/proc/${pid}/cmdline" && { append ignore "${pid}" break } done # get all server pids that should be ignored for server in `cat /var/run/${NAME}.*.pid` do append ignore "${server}" done # get all running pids and kill client connections local skip for pid in `pidof "${NAME}"` do # check if correct program, otherwise process next pid grep -F -q -e "${PROG}" "/proc/${pid}/cmdline" || { continue } # check if pid should be ignored (servers, ourself) skip=0 for server in ${ignore} do if [ "${pid}" == "${server}" ] then skip=1 break fi done [ "${skip}" -ne 0 ] && continue # kill process echo "${initscript}: Killing ${pid}..." kill -KILL ${pid} done } 如何修改上述文件,让外部dropbear远程连接该样机时要在键盘区输入密码验证
<think>嗯,用户的问题是关于如何配置Dropbear以启用密码验证的远程连接,重点是修改配置文件和init.d脚本。用户还特别强调了需要确保外部远程连接必须通过键盘交互式密码验证。从用户的历史记录来看,ta正在处理服务器安全相关的配置,可能是个系统管理员或有Linux运维需求的开发者。之前提供的引用材料主要涉及SSH端口转发、密码验证原理等,说明用户对SSH协议有基础理解,但现在需要具体落实到Dropbear这个轻量级实现上。Dropbear和OpenSSH虽然都是SSH实现,但配置方式差异很大。核心在于两点:一是找到正确的配置文件路径,二是理解Dropbear特有的参数命名习惯。用户提到的"键盘交互式密码验证"实际上就是Dropbear默认的password认证方式,不需要特别开启,反而可能需要禁用其他认证方式。用户可能会遇到的坑点包括:1)找不到配置文件(Dropbear默认不创建),2)和OpenSSH配置习惯混淆,3)初始化脚本的路径因系统而异。需要特别提醒的是,修改后必须完全重启服务而不是重载配置,这点和systemctl体系不同。安全方面要着重警告空密码的风险,建议用户至少设置密码复杂度策略。如果用户实际是树莓派玩家,可能需要补充说明BusyBox环境下init.d脚本的路径差异。考虑到用户明确要求引用配置模板,直接给出两段可复制代码最实用:1)基础密码验证配置,2)增强安全性的追加配置。同时提供测试命令很重要,因为Dropbear重启失败有时不会主动报错。</think>
要配置Dropbear以强制通过**键盘交互式密码验证**进行远程连接,需修改其配置文件和初始化脚本。以下是详细步骤:
---
### 一、修改Dropbear配置文件
1. **定位配置文件**
Dropbear默认配置文件通常是`/etc/default/dropbear`(Debian/Ubuntu)或`/etc/sysconfig/dropbear`(CentOS/RedHat)。若不存在,需手动创建。
2. **启用密码验证**
在配置文件中设置以下参数:
```bash
# 禁用公钥认证,强制密码验证
DROPBEAR_EXTRA_ARGS="-s -j -k"
```
**参数说明**:
- `-s`:禁用密码身份验证(**注意:此处名称虽为禁用,实际是启用键盘交互式密码认证**)
- `-j`:禁止本地端口转发
- `-k`:禁止远程端口转发
> ✅ **关键点**:Dropbear中`-s`参数实际对应SSH的`keyboard-interactive`认证方式,即用户预期的密码验证。
3. **禁用其他认证方式**(可选)
确保公钥认证被关闭(避免绕过密码):
```bash
DROPBEAR_RSAKEY="/dev/null" # 不加载RSA密钥
DROPBEAR_DSSKEY="/dev/null" # 不加载DSS密钥
```
---
### 二、修改init.d脚本(可选)
> ⚠️ 仅当配置文件路径不适用时需操作
在`/etc/init.d/dropbear`中找到启动命令,替换为:
```bash
/usr/sbin/dropbear -s -j -k -p 22 # 添加 -s -j -k 参数
```
---
### 三、重启服务生效
```bash
# Debian/Ubuntu
sudo systemctl restart dropbear # 或 sudo service dropbear restart
# CentOS/RedHat
sudo systemctl restart dropbear # 或 sudo service dropbear restart
```
---
### 四、验证配置是否生效
1. **本地测试连接**:
```bash
ssh user@localhost -o PubkeyAuthentication=no
```
系统应强制要求输入密码(若直接拒绝连接,说明公钥认证仍生效)。
2. **检查进程参数**:
```bash
ps aux | grep dropbear
```
输出中需包含`-s`参数,例如:
```
/usr/sbin/dropbear -s -j -k -F -p 22
```
---
### 安全加固建议
1. **禁止空密码**:
在配置文件中追加:
```bash
DROPBEAR_EXTRA_ARGS="$DROPBEAR_EXTRA_ARGS -g" # 禁止空密码登录
```
2. **限制IP访问**:
使用防火墙(如`iptables`或`ufw`)限制SSH访问源IP范围。
---
### 常见问题解决
- **连接被拒绝**:检查`-s`参数是否存在,确认服务已重启。
- **认证失败**:确保用户密码正确且未被PAM模块阻止。
- **配置未生效**:确认未在命令行或其它位置覆盖`DROPBEAR_EXTRA_ARGS`。
> 📌 **原理说明**:Dropbear默认启用密码认证,但通过`-s`可强制禁用其他认证方式(如公钥),使其仅保留键盘交互式认证[^1][^4]。这与OpenSSH的`PasswordAuthentication yes`等效但实现机制不同。
---
nginx: image: nginx:latest restart: always volumes: - ./nginx/nginx.conf.template:/etc/nginx/nginx.conf.template - ./nginx/proxy.conf.template:/etc/nginx/proxy.conf.template - ./nginx/https.conf.template:/etc/nginx/https.conf.template - ./nginx/conf.d:/etc/nginx/conf.d - ./nginx/docker-entrypoint.sh:/docker-entrypoint-mount.sh - ./nginx/ssl:/etc/ssl # cert dir (legacy) - ./volumes/certbot/conf/live:/etc/letsencrypt/live # cert dir (with certbot container) - ./volumes/certbot/conf:/etc/letsencrypt - ./volumes/certbot/www:/var/www/html entrypoint: [ 'sh', '-c', "cp /docker-entrypoint-mount.sh /docker-entrypoint.sh && sed -i 's/\r$$//' /docker-entrypoint.sh && chmod +x /docker-entrypoint.sh && /docker-entrypoint.sh" ] environment: NGINX_SERVER_NAME: ${NGINX_SERVER_NAME:-_} NGINX_HTTPS_ENABLED: ${NGINX_HTTPS_ENABLED:-false} NGINX_SSL_PORT: ${NGINX_SSL_PORT:-443} NGINX_PORT: ${NGINX_PORT:-80} # You're required to add your own SSL certificates/keys to the `./nginx/ssl` directory # and modify the env vars below in .env if HTTPS_ENABLED is true. NGINX_SSL_CERT_FILENAME: ${NGINX_SSL_CERT_FILENAME:-dify.crt} NGINX_SSL_CERT_KEY_FILENAME: ${NGINX_SSL_CERT_KEY_FILENAME:-dify.key} NGINX_SSL_PROTOCOLS: ${NGINX_SSL_PROTOCOLS:-TLSv1.1 TLSv1.2 TLSv1.3} NGINX_WORKER_PROCESSES: ${NGINX_WORKER_PROCESSES:-auto} NGINX_CLIENT_MAX_BODY_SIZE: ${NGINX_CLIENT_MAX_BODY_SIZE:-100M} NGINX_KEEPALIVE_TIMEOUT: ${NGINX_KEEPALIVE_TIMEOUT:-65} NGINX_PROXY_READ_TIMEOUT: ${NGINX_PROXY_READ_TIMEOUT:-3600s} NGINX_PROXY_SEND_TIMEOUT: ${NGINX_PROXY_SEND_TIMEOUT:-3600s} NGINX_ENABLE_CERTBOT_CHALLENGE: ${NGINX_ENABLE_CERTBOT_CHALLENGE:-false} CERTBOT_DOMAIN: ${CERTBOT_DOMAIN:-} depends_on: - api - web ports: - '${EXPOSE_NGINX_PORT:-80}:${NGINX_PORT:-80}' - '${EXPOSE_NGINX_SSL_PORT:-443}:${NGINX_SSL_PORT:-443}'
<think>我们正在配置Nginx以绑定域名并启用HTTPS,使用Let's Encrypt证书。根据引用[1]和[2],Let's Encrypt提供免费SSL证书,有效期为90天,支持自动续期。以下是具体步骤:
### 步骤1:安装Certbot工具
根据引用[1],在CentOS 9上安装Certbot:
```bash
sudo dnf install certbot python3-certbot-nginx
```
### 步骤2:获取Let's Encrypt证书
运行以下命令获取证书(替换`example.com`为你的域名):
```bash
sudo certbot certonly --nginx -d example.com -d www.example.com
```
- 此命令会验证域名所有权并签发证书,证书保存在`/etc/letsencrypt/live/example.com/`目录下[^1]。
### 步骤3:配置Nginx HTTPS代理
编辑Nginx配置文件(通常位于`/etc/nginx/conf.d/example.com.conf`),添加以下内容:
```nginx
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri; # 强制跳转HTTPS[^2]
}
server {
listen 443 ssl;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3; # 启用安全协议[^1]
# 配置代理到Dify(假设Dify运行在localhost:8080)
location / {
proxy_pass http://127.0.0.1:8080; # 绑定到本地Dify服务
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;
}
}
```
### 步骤4:重启Nginx并验证
```bash
sudo nginx -t # 测试配置
sudo systemctl restart nginx
```
访问`https://example.com`,确认HTTPS生效且能正确代理到Dify服务。
### 步骤5:设置证书自动续期
Let's Encrypt证书有效期为90天,设置自动续期:
```bash
# 编辑crontab
sudo crontab -e
```
添加以下行(每周日凌晨2点续期):
```bash
0 2 * * 0 /usr/bin/certbot renew --quiet && systemctl reload nginx
```
### 安全加固(可选)
1. **启用HSTS**(强制浏览器使用HTTPS):
```nginx
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
```
2. **使用强加密套件**:
```nginx
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';
```
### 数学表示证书续期周期
Let's Encrypt证书有效期$T=90$天,续期周期$T_r$应满足:
$$
T_r < T \quad \Rightarrow \quad T_r \in (0, 90) \text{天}
$$
推荐设置$T_r=7$天(每周检查一次),避免续期失败导致证书过期[^1]。
### 相关问题
1. 如何为多个域名配置HTTPS代理?
2. Nginx如何配置HTTP/2提升性能?
3. 如何验证SSL证书的安装是否正确?
4. 证书续期失败如何排查?
[^1]: Let‘s Encrypt免费安全证书的步骤及使用-基于centos9
[^2]: 全民https时代,Let's Encrypt免费SSL证书的申请及使用(Tomcat版)</think>### 配置Nginx绑定域名并启用HTTPS(使用Let's Encrypt证书)
#### 完整配置流程
##### 步骤1:安装Certbot工具
```bash
# 基于CentOS/RHEL
sudo yum install epel-release
sudo yum install certbot python3-certbot-nginx
# 基于Ubuntu/Debian
sudo apt update
sudo apt install certbot python3-certbot-nginx
```
##### 步骤2:获取Let's Encrypt证书
```bash
sudo certbot certonly --nginx -d yourdomain.com -d www.yourdomain.com
```
- 证书将保存在`/etc/letsencrypt/live/yourdomain.com/`目录
- 自动验证域名所有权(需确保域名已解析到服务器)[^1]
##### 步骤3:配置Nginx HTTPS代理
编辑`/etc/nginx/conf.d/dify.conf`:
```nginx
# HTTP重定向到HTTPS
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
return 301 https://$host$request_uri; # 强制HTTPS跳转[^2]
}
# HTTPS主配置
server {
listen 443 ssl http2;
server_name yourdomain.com www.yourdomain.com;
# SSL证书配置
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
# 安全协议配置
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
# 代理配置到Dify
location / {
proxy_pass http://127.0.0.1:8080; # 指向本地Dify服务
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_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# 安全响应头
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options DENY;
}
```
##### 步骤4:验证并重载配置
```bash
sudo nginx -t # 测试配置语法
sudo systemctl reload nginx # 应用新配置
```
##### 步骤5:设置证书自动续期
```bash
# 测试续期命令
sudo certbot renew --dry-run
# 添加定时任务(每周日凌晨2点续期)
(crontab -l ; echo "0 2 * * 0 /usr/bin/certbot renew --quiet && systemctl reload nginx") | crontab -
```
#### 关键配置说明
1. **证书路径**:
Let's Encrypt自动管理的证书路径为:
$$
\text{证书} \rightarrow \frac{\text{/etc/letsencrypt/live/yourdomain.com/}}{\begin{cases}
\text{fullchain.pem} \\
\text{privkey.pem}
\end{cases}}
$$
2. **HTTPS重定向原理**:
通过HTTP 301重定向实现强制HTTPS:
$$
\text{HTTP请求} \xrightarrow{\text{301重定向}} \text{HTTPS终结点}
$$
3. **代理安全配置**:
```nginx
proxy_set_header X-Forwarded-Proto $scheme; # 传递原始协议
add_header Strict-Transport-Security "max-age=31536000"; # 启用HSTS[^1]
```
#### 验证方法
```bash
# 检查HTTPS连接
curl -I https://yourdomain.com
# 应返回:HTTP/2 200
# 检查证书有效期
openssl x509 -enddate -noout -in /etc/letsencrypt/live/yourdomain.com/cert.pem
# 输出:notAfter=Sep 30 12:00:00 2023 GMT (90天有效期)
```
#### 常见问题解决
1. **证书申请失败**:
- 确认域名解析已生效(`dig yourdomain.com`)
- 开放防火墙端口:`sudo firewall-cmd --add-service={http,https} --permanent`
2. **混合内容警告**:
- 在Dify配置中设置`APP_URL=https://yourdomain.com`
- 确保所有资源使用相对路径或HTTPS URL
3. **性能优化**:
```nginx
ssl_session_tickets off; # 减少TLS握手开销
ssl_buffer_size 4k; # 优化小数据包传输
```
[^1]: Let‘s Encrypt免费安全证书的步骤及使用-基于centos9
[^2]: 全民https时代,Let's Encrypt免费SSL证书的申请及使用(Tomcat版)
---
### 相关问题
1. 如何为多个子域名配置HTTPS代理?
2. Nginx如何实现HTTP/3(QUIC)支持?
3. Let's Encrypt证书续期失败如何排查?
4. 如何配置Nginx的OCSP装订提升HTTPS性能?
5. 企业级证书与Let's Encrypt有何区别?
阅读全文
相关推荐








之前我配置了libinjection.so到防火墙上,现在修改我发给你的代码,将modsecurity配置到防火墙上
app.py:
from flask import Flask, request, jsonify
import ctypes
import numpy as np
from tensorflow.keras.models import load_model
from tensorflow.keras.preprocessing.sequence import pad_sequences
import pickle
import json
from urllib.parse import unquote
import html
import sys
import base64
import re
from utils.makelog import log_detection
import os
import logging
from logging.handlers import RotatingFileHandler
os.environ['TF_KERAS'] = '1'
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' # 1=警告,2=错误,3=静默
os.environ['TF_ENABLE_ONEDNN_OPTS'] = '0' # 关闭 oneDNN 提示
app = Flask(__name__)
log_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'utils')
os.makedirs(log_dir, exist_ok=True)
# 配置文件日志处理器(10MB轮换,保留10个备份)
file_handler = RotatingFileHandler(
os.path.join(log_dir, 'app.log'),
maxBytes=10*1024*1024,
backupCount=10
)
file_handler.setFormatter(logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
))
# 设置日志级别(DEBUG/INFO/WARNING/ERROR/CRITICAL)
app.logger.setLevel(logging.INFO)
file_handler.setLevel(logging.INFO)
app.logger.addHandler(file_handler)
# --- 加载 libinjection ---
try:
libinjection = ctypes.CDLL('/usr/local/lib/libinjection.so', mode=ctypes.RTLD_GLOBAL)
libinjection.libinjection_sqli.argtypes = [
ctypes.c_char_p,
ctypes.c_size_t,
ctypes.c_char_p,
ctypes.c_size_t
]
libinjection.libinjection_sqli.restype = ctypes.c_int
app.logger.info("Libinjection 加载成功")
print("Libinjection 加载成功(控制台输出)")
except Exception as e:
app.logger.error(f"Libinjection 加载失败: {str(e)}", exc_info=True)
exit(1)
# --- 解码辅助函数 ---
def try_base64_decode(s):
try:
if len(s) % 4 != 0:
return s
decoded = base64.b64decode(s).decode('utf-8', errors='ignore')
if all(32 <= ord(c) <= 126 or c in '\t\r\n' for c in decoded):
return decoded
return s
except Exception:
return s
def deep_url_decode(s, max_depth=3):
decoded = s
for _ in range(max_depth):
new_decoded = unquote(decoded)
if new_decoded == decoded:
break
decoded = new_decoded
return decoded
# --- 提取 HTTP 请求中的潜在 SQL 内容 ---
def extract_sql_candidates(data):
candidates = []
def extract_strings(obj):
EXCLUDED_KEYS = {'uri', 'path', 'security', 'PHPSESSID', 'session_id','Login', 'login', 'submit', 'Submit'}
STATIC_RESOURCES = {'.css', '.js', '.png', '.jpg', '.jpeg', '.gif', '.ico', '.woff', '.woff2'}
if isinstance(obj, dict):
for key, value in obj.items():
if key in EXCLUDED_KEYS:
continue
# 检查值是否为静态资源(无需检测)
if isinstance(value, str) and any(ext in value.lower() for ext in STATIC_RESOURCES):
continue
extract_strings(value) # 递归调用,仅传递值
elif isinstance(obj, list):
for item in obj:
extract_strings(item)
elif isinstance(obj, str):
text = obj
# 多层 URL 解码
text = deep_url_decode(text)
# HTML 实体解码
text = html.unescape(text)
# Unicode 转义解码
try:
text = text.encode().decode('unicode_escape')
except Exception:
pass
# Base64 解码
text = try_base64_decode(text)
if len(text) < 1000:
candidates.append(text)
extract_strings(data)
return candidates
# --- 检测逻辑 ---
def detect_one(query):
if re.match(r'^\/.*\.(php|html|js)$', query):
return {
"检测结果": "正常",
"检测方式": "URI过滤",
"可信度": 1.0
}
result_buf = ctypes.create_string_buffer(8)
is_libi_sqli = libinjection.libinjection_sqli(query.encode('utf-8'), len(query),result_buf,ctypes.sizeof(result_buf))
if is_libi_sqli:
return {
"检测结果": "存在SQL注入",
"检测方式": "Libinjection",
}
else:
return {
"检测结果": "正常",
"检测方式": "Libinjection",
}
@app.route('/')
def home():
return "SQL 注入检测系统已启动"
@app.route('/detect', methods=['POST'])
def detect():
app.logger.info(f"接收到请求: {request.json}")
try:
data = request.get_json()
if not data:
return jsonify({"error": "缺少 JSON 请求体"}), 400
ip = request.remote_addr
candidates = extract_sql_candidates(data)
results = []
for query in candidates:
result = detect_one(query)
log_detection(ip, query, result)
results.append(result)
return jsonify({"detections": results})
except Exception as e:
return jsonify({"error": f"检测过程中发生错误: {str(e)}"}), 500
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)
nainx.conf:
# 全局作用域(仅保留一份)
user user;
worker_processes 1;
events {
worker_connections 1024;
}
http {
lua_package_path "/usr/local/openresty/lualib/?.lua;;";
include mime.types;
default_type text/html;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name 10.18.47.200;
location /dvwa {
rewrite_by_lua_file /usr/local/openresty/lualib/parse.lua;
proxy_pass http://192.168.159.100/DVWA-master/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_redirect http://10.18.47.200/DVWA-master/ http://10.18.47.200/dvwa/;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
charset utf-8;
}
#屏蔽图标
location = /favicon.ico {
access_log off;
log_not_found off;
}
}
}
parse.lua:
local cjson = require "cjson.safe"
local http = require "resty.http"
-- 1) 解析 Nginx 内置变量和 Headers
local method = ngx.req.get_method()
local uri = ngx.var.request_uri
local headers = {
user_agent = ngx.var.http_user_agent or "",
cookie = ngx.var.http_cookie or "",
host = ngx.var.http_host or "",
content_type = ngx.var.http_content_type or ""
}
-- 2) 解析 GET 参数
ngx.req.read_body() -- 必须先读取 body,否则取不到 POST
local args = ngx.req.get_uri_args()
local query_params = {}
for k, v in pairs(args) do
query_params[k] = v
end
-- 3) 解析 POST 数据: 根据 content_type 判断JSON或表单
local post_data = {}
if headers.content_type and string.find(headers.content_type, "application/json") then
local body_data = ngx.req.get_body_data()
if body_data then
local json_data = cjson.decode(body_data)
if json_data then
post_data = json_data
else
ngx.log(ngx.ERR, "JSON 解析失败")
end
end
else
local post_args = ngx.req.get_post_args()
for k, v in pairs(post_args) do
post_data[k] = v
end
end
-- 4) 整合请求数据并日志输出
local request_data = {
method = method,
uri = uri,
headers = headers,
query_params = query_params,
post_data = post_data,
client_ip = ngx.var.remote_addr
}
ngx.log(ngx.ERR, "OpenResty 解析的数据: " .. cjson.encode(request_data))
-- 5) 调用 Flask WAF 后端
local httpc = http.new()
local res, err = httpc:request_uri("http://127.0.0.1:5000/detect", {
method = "POST",
body = cjson.encode(request_data),
headers = {
["Content-Type"] = "application/json"
}
})
if not res then
ngx.log(ngx.ERR, "Flask WAF 请求失败: ", err)
ngx.status = 500
ngx.header["Content-Type"] = "text/html; charset=utf-8"
ngx.say("WAF 检测异常")
return ngx.exit(500)
end
-- 6) 复用连接
local ok, err_keep = httpc:set_keepalive(60000, 100)
if not ok then
ngx.log(ngx.ERR, "设置 keepalive 失败: ", err_keep)
end
ngx.log(ngx.ERR, "Flask 返回: ", res.body)
-- 7) 解析Flask响应并处理(修正pcall返回值)
if res.status ~= 200 then
ngx.log(ngx.ERR, "Flask 返回非200状态码: ", res.status)
ngx.status = 500
ngx.header["Content-Type"] = "text/html; charset=utf-8"
ngx.say("Flask 服务异常")
return ngx.exit(500)
end
local success, decoded_data = pcall(cjson.decode, res.body)
if not success then
ngx.log(ngx.ERR, "Flask 响应JSON解析失败: ", decoded_data)
ngx.status = 500
ngx.header["Content-Type"] = "text/html; charset=utf-8"
ngx.say("WAF 响应格式错误")
return ngx.exit(500)
end
local waf_result = decoded_data
-- 8) 判断是否存在SQL注入(根据app.py的响应结构)
local is_sqli = false
local detections = waf_result.detections or {}
for i = 1, #detections do
local detection = detections[i]
-- 检查检测结果是否为表类型且包含检测结果字段
if type(detection) == "table" and detection["检测结果"] then
if detection["检测结果"] == "存在SQL注入" then
is_sqli = true
break
end
end
end
-- for _, detection in ipairs(waf_result.detections or {}) do
-- if detection["检测结果"] == "存在SQL注入" then
-- is_sqli = true
-- break
-- end
-- end
-- 9) 根据检测结果决定是否拦截
if is_sqli then
ngx.log(ngx.ERR, "WAF阻断 SQL注入")
ngx.status = ngx.HTTP_FORBIDDEN
ngx.header["Content-Type"] = "text/html; charset=utf-8"
ngx.say([[
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8"/>
<title>访问受限</title>
<style>
/* 全局样式重置 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background-color: #000;
color: #fff;
font-family: "Microsoft YaHei", Arial, sans-serif;
}
.container {
width: 90%;
max-width: 600px;
padding: 40px;
text-align: center;
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 8px;
background: rgba(10, 10, 10, 0.8);
backdrop-filter: blur(5px);
box-shadow: 0 0 15px rgba(255, 255, 255, 0.05);
}
h1 {
margin-bottom: 20px;
font-size: 2.5rem;
letter-spacing: 2px;
color: #fff;
text-shadow: 0 0 10px rgba(0, 255, 255, 0.5);
}
p {
margin-bottom: 30px;
font-size: 1.1rem;
line-height: 1.8;
color: rgba(255, 255, 255, 0.9);
}
.btn-back {
display: inline-block;
padding: 12px 30px;
background: transparent;
color: #00ffcc;
border: 1px solid #00ffcc;
border-radius: 4px;
font-size: 1rem;
font-weight: bold;
text-decoration: none;
transition: all 0.3s ease;
cursor: pointer;
}
.btn-back:hover {
background: rgba(0, 255, 204, 0.1);
box-shadow: 0 0 15px rgba(0, 255, 204, 0.3);
transform: translateY(-2px);
}
</style>
</head>
<body>
访问受限
检测到疑似SQL注入/跨站脚本攻击(XXS)的恶意行为特征,
为保障系统安全,本次请求未被授权执行
返回上一页
<script>
// 点击按钮返回前一个页面状态
document.getElementById('backBtn').addEventListener('click', function() {
history.back();
});
</script>
</body>
]])
else
ngx.log(ngx.ERR, "WAF 判断正常,放行请求")
return -- 关键:放行请求,继续执行proxy_pass
end








