摘要:在本文中,我们将深入探讨DOM型XSS(DOM-based XSS)这一完全发生在客户端的、传统扫描器难以发现的漏洞类型。我们将详细解释DOM-XSS的成因,以及为什么基于requests的扫描器对此“视而不见”。为了破局,我们将引入一个革命性的技术——无头浏览器(Headless Browser)。你将学习如何通过Python的Selenium库,来驱动一个真实的浏览器内核(如Chrome),在内存中渲染目标页面、执行其JavaScript代码,并通过注入探测Payload到URL片段(#)等客户端数据源中,来自动化地检测和发现DOM型XSS漏洞。

关键词:Python, XSS, DOM-XSS, Selenium, 无头浏览器, 漏洞扫描, Web安全


正文

⚠️ 警告:仅用于授权的教育和安全测试目的

本文及配套代码用于探测真实的安全漏洞。未经授权,对任何线上系统进行漏洞扫描都是违法行为。所有读者必须在自己的本地测试环境或授权的漏洞靶场中使用本文所学知识。

1. 客户端的“自我伤害”:DOM-XSS原理

我们之前讨论的反射型和存储型XSS,其共同点是服务器端代码处理不当,将恶意Payload嵌入到了HTML响应中。而DOM-XSS则完全不同,它的“犯罪现场”只在客户端,即用户的浏览器中。

漏洞原理:服务器返回的页面本身是安全的、不包含任何恶意代码。但是,页面中包含的JavaScript代码,在执行时,从一个客户端可控的“源(Source)”获取了数据,并且在未经过充分安全处理的情况下,将这些数据写入了页面的DOM(Document Object Model)中的一个危险“汇点(Sink)”,从而导致了脚本执行。

  • 常见的源 (Source)

    • document.URL

    • document.location.hash (URL中#后面的部分)

    • document.referrer

    • window.name

  • 危险的汇点 (Sink)

    • element.innerHTML = ...

    • document.write(...)

    • eval(...)

一个典型的易受攻击场景

  • URL: http://example.com/welcome.html#David

  • 页面上的JavaScript代码:

    JavaScript

    // 这段脚本从URL的hash中获取名字,并显示欢迎信息
    var username = location.hash.substring(1); // Source
    document.getElementById('welcomeMessage').innerHTML = "Welcome, " + username; // Sink
    
  • 攻击者构造的恶意URL: http://example.com/welcome.html#<img src=x onerror=alert(1)>

  • 攻击流程

    1. 服务器安全地返回了welcome.html的原始内容。这个恶意Payload从未发送到服务器

    2. 受害者的浏览器开始渲染页面,并执行其中的JavaScript。

    3. location.hash获取到了#<img src=x onerror=alert(1)>

    4. innerHTML将这段HTML字符串写入到了DOM中。

    5. 浏览器尝试渲染这个新的<img>标签,图片加载失败,触发onerror事件,恶意脚本被执行。

检测的挑战:因为Payload不经过服务器,所以我们之前使用requests库的扫描器,无论如何分析服务器的响应,都永远无法发现这个漏洞。要找到它,我们必须像一个真实的用户一样,用一个真实的浏览器去渲染和执行页面。

2. 破局之道:引入无头浏览器引擎 (Selenium)

Selenium是一个强大的Web自动化测试框架,它允许我们用代码来驱动一个真实的浏览器(Chrome, Firefox等)。当以**无头(Headless)**模式运行时,浏览器会在后台内存中完成所有的页面加载、渲染和JS执行,而不会弹出任何图形界面。这正是我们进行自动化DOM-XSS检测所需要的完美工具。

3. 环境准备

这是本课程中环境配置最复杂的一步,请务必仔细操作。

  1. 安装Selenium:

    Bash

    pip install selenium
    
  2. 安装WebDriver: Selenium需要一个名为WebDriver的“驱动程序”来与具体的浏览器进行通信。

    • 检查你的Chrome版本:在浏览器地址栏输入chrome://version,记下你的版本号(例如 118.0.5993.88)。

    • 下载ChromeDriver:访问Chrome for Testing availability 官方镜像站。找到与你的Chrome主版本号(例如118)对应的ChromeDriver,下载适合你操作系统的chromedriver-win64.zip, chromedriver-mac-x64.zipchromedriver-linux64.zip

    • 配置路径:解压下载的文件,得到一个chromedriver.exe(或chromedriver)。将这个文件放在你的Python脚本所在的目录,或者一个已经加入到系统PATH环境变量的目录中。

4. 完整代码实现 (dom_xss_scanner.py)

我们将编写一个脚本,它使用Selenium打开一个URL,在#后附加一个探测Payload,然后检查这个Payload是否出现在了最终渲染的DOM中。

Python

# dom_xss_scanner.py
import argparse
import sys
import time
from selenium import webdriver
from selenium.webdriver.chrome.service import Service as ChromeService
from selenium.webdriver.chrome.options import Options

def scan_dom_xss(url):
    """
    使用Selenium检测基于hash的DOM-XSS。
    """
    print(f"[*] 正在对 {url} 进行DOM-XSS扫描...")
    
    # 一个独特的、无害但包含HTML特殊字符的Payload
    payload = "#<test_dom_xss_tag>"
    test_url = url + payload

    # --- 配置Selenium ---
    chrome_options = Options()
    chrome_options.add_argument("--headless") # 启用无头模式
    chrome_options.add_argument("--no-sandbox")
    chrome_options.add_argument("--disable-dev-shm-usage")
    # 很多网站有反爬虫机制,伪装User-Agent
    chrome_options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36")
    
    # 启动WebDriver
    # 如果chromedriver不在PATH中,你需要指定其路径:
    # service = ChromeService(executable_path='/path/to/chromedriver')
    # driver = webdriver.Chrome(service=service, options=chrome_options)
    driver = None
    try:
        driver = webdriver.Chrome(options=chrome_options)
        
        print(f"[*] 正在使用无头浏览器访问: {test_url}")
        driver.get(test_url)
        
        # 等待几秒钟,让页面上的JavaScript有时间执行
        time.sleep(3)
        
        # 获取页面渲染后的DOM
        page_source = driver.page_source
        
        # 检查我们的Payload是否出现在DOM中
        # 我们只检查标签部分,因为它可能被修改
        if "<test_dom_xss_tag>" in page_source:
            print("\n[+] 高危: 检测到潜在的DOM型XSS漏洞!")
            print(f"    - URL: {test_url}")
            print(f"    - 证据: 注入的Payload '<test_dom_xss_tag>' 出现在了最终的DOM中。")
            return True
        else:
            print("[-] 未在DOM中发现注入的Payload。")
            return False

    except Exception as e:
        print(f"[!] 运行Selenium时发生错误: {e}", file=sys.stderr)
        print("[!] 请确保ChromeDriver已正确安装并与你的Chrome浏览器版本匹配。", file=sys.stderr)
        return False
    finally:
        if driver:
            driver.quit() # 确保浏览器进程被关闭


def main():
    parser = argparse.ArgumentParser(description="一个基于Selenium的DOM-XSS扫描器。")
    parser.add_argument("url", help="待测试的URL (不包含'#'部分)。")
    args = parser.parse_args()
    scan_dom_xss(args.url)

if __name__ == "__main__":
    main()

总结

我们成功地将一个真实的浏览器引擎,集成到了我们的安全工具中,从而具备了检测DOM-XSS这一高级漏洞的能力。这标志着我们的扫描器从只能分析“静态蓝图”(服务器响应)进化到了能够观察“动态建筑”(客户端渲染)的阶段。

局限性

  • 速度慢:启动和操作一个完整的浏览器,其开销远大于发送一个简单的HTTP请求。

  • Payload单一:我们的工具只测试了location.hash这一个“源”,真实的DOM-XSS源和汇点组合非常多。

  • 依赖环境:需要正确配置浏览器和WebDriver,部署起来更复杂。

尽管有这些缺点,但对于现代富客户端应用来说,基于浏览器引擎的动态分析是不可或缺的。

我们已经构建了三种不同类型的XSS检测器。但它们的Payload都相对简单,很容易被WAF或输入过滤器拦截。

Logo

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

更多推荐