目前WEB自动化安全测试一般分为两类:主动式自动化安全测试、被动式自动化安全测试,两者区别在于获取测试数据源的方式,主动式自动化安全测试主要依靠爬虫对目标站点进行爬取获得测试目标链接,被动式自动化安全测试主要依靠代理等方式采集测试数据源。由于两者在测试数据来源的不同,导致测试适用范围及测试结果有所区别:
测试方式 | 主动式自动化安全测试 | 被动式自动化安全测试 |
---|---|---|
测试覆盖率 | 低,依赖于爬虫爬取质量及范围,一般情况下测试人员无法干预爬虫爬取的具体页面 | 高,依赖于数据源质量,可以通过扩大日志、代理服务器数据等数据源的质量,测试人员可通过数据源干预测试的具体目标页面、接口 |
速度 | 低,需要主动爬取数据,同等条件下速度低于被动式自动化安全测试工具 | 高,无需主动探测数据,同等条件下速度高于主动式自动化安全测试 |
开发难度 | 复杂,需要实现爬虫模块及测试模块两个核心模块,爬虫算法多样,复杂度高 | 相对简单,无需实现爬虫模块,核心功能模块为数据采集模块及测试模块,数据采集模块在有限使用场景下复杂度较低 |
测试精准度 | 较低,该类扫描器测试模块主要测试通用漏洞,通常不涉及越权测试等复杂扫描项目 | 较高,可测试通用漏洞,也可定制越权测试等复杂扫描项目 |
在本文中,将会构建一个基于http代理的被动式扫描器探测SQL注入。
被动式自动化安全测试涉及数据采集、测试两个核心模块,netty负责实现通过代理服务器采集测试目标的功能,测试模块使用SQLMapApi对SQL注入漏洞进行检测。下面分别介绍并搭建开发环境。
JDK1.8.0.201+Eclipse+Tomcat,属于基础软件,常规安装即可。
在maven中引入netty及fastjson,netty主要实现代理服务器模块,fastjson负责与SQLMapApi交互
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.XXX</groupId>
<artifactId>autoTestV4</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.37.Final</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.59</version>
</dependency>
</dependencies>
</project>
SQLMap是基于Python运行的,安装教程可以自行查找,安装好后可启动SQLMapApi测试。
代理服务器部分,我们使用netty服务器内置的http编解码器进行服务器代理并将代理数据镜像一份至测试模块。
public class Properties {
//******************** 代理服务器部分 ********************
public static int ProxyPort=8889; //服务器监听的端口号
//************************ HttpConnet 的SUCCESS响应(http协议规定) ************************
public final static HttpResponseStatus SUCCESS = new HttpResponseStatus(200, "Connection established");
}
public class StartProxy {
//*** 主方法 ***
public static void main(String[] args) {
//*** 启动代理服务器(netty) ***
startProxy();
}
//*** 启动代理服务器(netty) ***
private static void startProxy() {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup(2);
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
ch.pipeline().addLast("httpCodec",new HttpServerCodec());
ch.pipeline().addLast("httpObject",new HttpObjectAggregator(65536));
//转发给代理服务器处理数据包
ch.pipeline().addLast("serverHandle",new HttpProxyServerHandle());
}
});
//**** 绑定代理服务器端口号 ****
ChannelFuture f = b
.bind(Properties.ProxyPort)
.sync();
f.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
public class HttpProxyServerHandle extends ChannelInboundHandlerAdapter {
private ChannelFuture cf;
private String host;
private int port;
// *********** 重写channelRead方法 *************
@Override
public void channelRead(final ChannelHandlerContext ctx, final Object msg) throws Exception {
// *********** 如果传入消息是能解析的HttpRequest *************
if (msg instanceof FullHttpRequest) {
// *** 转发并处理能解析的HttpRequest ***
forwardAndCheckHttpRequest(ctx, msg);
} else {
// *********** 如果传入消息无法解析,原样转发 *************
forwardButNoCheck(ctx, msg);
}
}
// *********** 如果传入消息无法解析,原样转发 *************
private void forwardButNoCheck(final ChannelHandlerContext ctx, final Object msg) {
if (cf == null) {
// 连接至目标服务器
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(ctx.channel().eventLoop()) // 复用客户端连接线程池
.channel(ctx.channel().getClass()) // 使用NioSocketChannel来作为连接用的channel类
.handler(new ChannelInitializer() {
@Override
protected void initChannel(Channel ch) throws Exception {
ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(ChannelHandlerContext ctx0, Object msg) throws Exception {
ctx.channel().writeAndFlush(msg);
}
});
}
});
cf = bootstrap.connect(host, port);
cf.addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
future.channel().writeAndFlush(msg);
} else {
ctx.channel().close();
}
}
});
} else {
cf.channel().writeAndFlush(msg);
}
}
// *** 转发并处理能解析的HttpRequest ***
private void forwardAndCheckHttpRequest(final ChannelHandlerContext ctx, final Object msg) throws Exception {
// *** 消息赋值 ***
FullHttpRequest request = (FullHttpRequest) msg;
// *** 确定转发地址及端口 ***
getHostAndPort(request);
// *** HTTPS建立代理握手 ***
if ("CONNECT".equalsIgnoreCase(request.method().name())) {
httpsCONNECT(ctx);
return;
}
//*
// !!!!!!!!!!!!!!!!!!!! 调用检测Payload模块 !!!!!!!!!!!!!!!!!!!!
//*** IllegalReferenceCountException refCnt: 0 异常修复 采用copy()复制writeAndFlush前的FullHttpRequest对象 ***
FullHttpRequest payloadRequest = request.copy();
Payload payload = new Payload(payloadRequest);
payload.start();
//*/
// *** 使用netty处理,并使用HttpProxyInitializer类处理response包 ***
// 连接至目标服务器
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(ctx.channel().eventLoop()) // 注册线程池
.channel(ctx.channel().getClass()) // 使用NioSocketChannel来作为连接用的channel类
.handler(new HttpProxyInitializer(ctx.channel())); // 将response包使用HttpProxyInitializer处理
// 发送request包
ChannelFuture cf = bootstrap.connect(host, port);
cf.addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
future.channel().writeAndFlush(msg);
} else {
ctx.channel().close();
}
}
});
}
// *** HTTPS建立代理握手 ***
private void httpsCONNECT(ChannelHandlerContext ctx) {
HttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, Properties.SUCCESS);
ctx.writeAndFlush(response);
ctx.pipeline().remove("httpCodec");
ctx.pipeline().remove("httpObject");
return;
}
// *** 确定转发地址及端口 ***
private void getHostAndPort(FullHttpRequest request) {
String host = request.headers().get("host");
String[] temp = host.split(":");
int port = 80;
if (temp.length > 1) {
port = Integer.parseInt(temp[1]);
} else {
if (request.uri().indexOf("https") == 0) {
port = 443;
}
}
this.host = temp[0];
this.port = port;
}
}
public class HttpProxyInitializer extends ChannelInitializer{
private Channel clientChannel;
public HttpProxyInitializer(Channel clientChannel) {
this.clientChannel = clientChannel;
}
@Override
protected void initChannel(Channel ch) throws Exception {
ch.pipeline().addLast(new HttpClientCodec());
ch.pipeline().addLast(new HttpObjectAggregator(6553600));
ch.pipeline().addLast(new HttpProxyClientHandle(clientChannel)); //使用HttpProxyClientHandle处理response
}
}
public class HttpProxyClientHandle extends ChannelInboundHandlerAdapter {
private Channel clientChannel;
public HttpProxyClientHandle(Channel clientChannel) {
this.clientChannel = clientChannel;
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
clientChannel.writeAndFlush(msg); //将response返回客户端
}
}
public class Payload extends Thread{
FullHttpRequest request; // 用于测试的请求
// *** 构造函数(将代理服务器采集到的FullHttpRequest传入)
public Payload(FullHttpRequest request) {
this.request = request;
}
// *** 开始测试(异步开始测试) ***
public void run() {
try {
// *** SQL注入测试 ***
FullHttpRequest request = this.request.copy(); // 复制request对象用于测试,防止refCnt: 0 异常
SQLPayload sqlpayload = new SQLPayload();
sqlpayload.startSqlInjectTest(request);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public class SQLPayload {
//*** 调用SQL注入测试的总接口 ***
public void startSqlInjectTest(FullHttpRequest request) throws Exception {
//*** 调用SQLMAPAPI进行SQL注入测试 ***
SQLMAPApi sqlmapapi = new SQLMAPApi();
//*** 使用SQLMAPAPI创建扫描任务 ***
sqlmapapi.createTask();
//*** 传入SQLMAPAPI需要的数据,开始扫描 ***
sqlmapapi.startScan(request);
//*** 查询任务扫描状态 ***
sqlmapapi.status();
//*** 查询任务扫描结果并保存至文件 ***
sqlmapapi.result();
}
}
public class SQLMAPApi {
String taskid; // SQLMAP任务ID
String uri = null;
// *** 创建新任务 ***
public void createTask() throws Exception {
// 参考资料:https://www.jianshu.com/p/11814875d793
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(workerGroup).channel(NioSocketChannel.class).option(ChannelOption.SO_KEEPALIVE, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast("codec", new HttpClientCodec());
socketChannel.pipeline().addLast("httpAggregator", new HttpObjectAggregator(512 * 1024)); // http
// 消息聚合器
socketChannel.pipeline().addLast(new NewTaskResponse());
}
});
Channel channel = bootstrap.connect(Properties.SQLMapApiAdr, Properties.SQLMapApiPort).sync().channel();
URI uri = new URI("/task/new");
HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri.toASCIIString());
HttpHeaders headers = request.headers();
headers.set(HttpHeaderNames.HOST, "127.0.0.1");
headers.set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE);
headers.set(HttpHeaderNames.ACCEPT_ENCODING, HttpHeaderValues.GZIP + "," + HttpHeaderValues.DEFLATE);
headers.set(HttpHeaderNames.ACCEPT_CHARSET, "ISO-8859-1,utf-8;q=0.7,*;q=0.7");
headers.set(HttpHeaderNames.ACCEPT_LANGUAGE, "fr");
headers.set(HttpHeaderNames.USER_AGENT, "Netty Simple Http Client side");
headers.set(HttpHeaderNames.ACCEPT, "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
// send request
channel.writeAndFlush(request);
channel.closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
}
}
// *** 创建任务response处理 ***
private class NewTaskResponse extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof HttpResponse) {
HttpResponse httpResponse = (HttpResponse) msg;
}
if (msg instanceof HttpContent) {
HttpContent httpContent = (HttpContent) msg;
// 提取taskid
String json = httpContent.content().toString(0, httpContent.content().capacity(),
Charset.defaultCharset());
JSONObject obj = JSON.parseObject(json);
taskid = (String) obj.get("taskid");
}
}
}
// *** 传入SQLMAPAPI需要的数据,开始扫描 ***
public void startScan(FullHttpRequest request) {
// *** 保存request文件到本地 ***
// **** 创建文件 ****
String filePath = Properties.requestFileSaveBasePath + taskid; // taskid为对应的SQLMAPAPI任务ID
Result.saveFullHttpRequestToFile(filePath, request);
uri = request.uri();
// 使用sqlmapapi的start命令传入文件开始测试
// 向SQLMAPAPI传送开始测试的指令
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(workerGroup).channel(NioSocketChannel.class).option(ChannelOption.SO_KEEPALIVE, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast("codec", new HttpClientCodec());
socketChannel.pipeline().addLast("httpAggregator", new HttpObjectAggregator(512 * 1024)); // http
// 消息聚合器
socketChannel.pipeline().addLast(new StartTestResponse());
}
});
Channel channel = bootstrap.connect(Properties.SQLMapApiAdr, Properties.SQLMapApiPort).sync().channel();
// *** 生成post传送的文件名(设置向SQLMAPAPI发送request的content内容)
Start start = new Start();
start.setUrl("http://" + Properties.TomcatServerIP + ":" + Properties.TomcatPort
+ Properties.requestFileSavePath + taskid);
String jsonStr = JSON.toJSONString(start);
// *** 生成post传送的uri
URI uri = new URI("/scan/" + taskid + "/start");
FullHttpRequest requestToSQLMAPAPI = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST,
uri.toASCIIString(), Unpooled.wrappedBuffer(jsonStr.getBytes("UTF-8")));
requestToSQLMAPAPI.headers().set(HttpHeaders.Names.HOST, "127.0.0.1");
requestToSQLMAPAPI.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
requestToSQLMAPAPI.headers().set(HttpHeaders.Names.CONTENT_LENGTH,
requestToSQLMAPAPI.content().readableBytes());
requestToSQLMAPAPI.headers().set(HttpHeaders.Names.CONTENT_TYPE, "application/json");
// send request
channel.writeAndFlush(requestToSQLMAPAPI).sync();
channel.closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (URISyntaxException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} finally {
workerGroup.shutdownGracefully();
}
}
// *** 创建任务response处理 ***
private class StartTestResponse extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof HttpResponse) {
HttpResponse httpResponse = (HttpResponse) msg;
}
if (msg instanceof HttpContent) {
HttpContent httpContent = (HttpContent) msg;
}
}
}
// 任务状态
boolean isEnd = false;
int count = 1;
// *** 查询任务是否结束 ***
public String status() {
// 当任务未结束时,不停查询
while (isEnd == false) {
// *** 每次查询间隔1秒钟 ***
long startTime = System.currentTimeMillis();
do {
} while (System.currentTimeMillis() < (startTime + 1000));
count++;
// *** 向SQLMAPAPI传送查询任务状态的指令
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(workerGroup).channel(NioSocketChannel.class).option(ChannelOption.SO_KEEPALIVE, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast("codec", new HttpClientCodec());
socketChannel.pipeline().addLast("httpAggregator",
new HttpObjectAggregator(512 * 1024)); // http 消息聚合器
socketChannel.pipeline().addLast(new StatusResponse());
}
});
Channel channel = bootstrap.connect(Properties.SQLMapApiAdr, Properties.SQLMapApiPort).sync().channel();
// *** 生成post传送的uri
URI uri = new URI("/scan/" + taskid + "/status");
FullHttpRequest requestToSQLMAPAPI = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET,
uri.toASCIIString());
requestToSQLMAPAPI.headers().set(HttpHeaders.Names.HOST, "127.0.0.1");
requestToSQLMAPAPI.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
// send request
channel.writeAndFlush(requestToSQLMAPAPI).sync();
channel.closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (URISyntaxException e) {
e.printStackTrace();
} finally {
workerGroup.shutdownGracefully();
}
}
return "task_Finish";
}
// *** 查询任务response处理 ***
private class StatusResponse extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof HttpResponse) {
HttpResponse httpResponse = (HttpResponse) msg;
}
if (msg instanceof HttpContent) {
HttpContent httpContent = (HttpContent) msg;
String json = httpContent.content().toString(0, httpContent.content().capacity(),
Charset.defaultCharset());
JSONObject obj = JSON.parseObject(json);
String status = (String) obj.get("status");
if (status.equalsIgnoreCase("terminated")) {
isEnd = true;
}
}
}
}
// *** 查询任务扫描结果并保存至文件 ***
public void result() {
// *** 请求sqlmapapi获取任务扫描结果
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(workerGroup).channel(NioSocketChannel.class).option(ChannelOption.SO_KEEPALIVE, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast("codec", new HttpClientCodec());
socketChannel.pipeline().addLast("httpAggregator", new HttpObjectAggregator(512 * 1024)); // http
// 消息聚合器
socketChannel.pipeline().addLast(new DataResponse());
}
});
Channel channel = bootstrap.connect(Properties.SQLMapApiAdr, Properties.SQLMapApiPort).sync().channel();
// *** 生成post传送的uri
URI uri = new URI("/scan/" + taskid + "/data");
FullHttpRequest requestToSQLMAPAPI = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET,
uri.toASCIIString());
requestToSQLMAPAPI.headers().set(HttpHeaders.Names.HOST, "127.0.0.1");
requestToSQLMAPAPI.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
// send request
channel.writeAndFlush(requestToSQLMAPAPI).sync();
channel.closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (URISyntaxException e) {
e.printStackTrace();
} finally {
workerGroup.shutdownGracefully();
}
}
// *** 查询任务扫描结果response处理 ***
private class DataResponse extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
String data = null;
if (msg instanceof HttpResponse) {
HttpResponse httpResponse = (HttpResponse) msg;
}
if (msg instanceof HttpContent) {
HttpContent httpContent = (HttpContent) msg;
String json = httpContent.content().toString(0, httpContent.content().capacity(),
Charset.defaultCharset());
JSONObject obj = JSON.parseObject(json);
data = obj.get("data").toString();
}
// *** 当data内容为空或为“[]”时,不存在注入,否则存在注入 ***
if (data.equalsIgnoreCase("[]") || data.isEmpty() || data==null) {
Result.writeToFile("SQL注入\t不存在\t问题链接\t\t请求数据包\t" + Properties.requestFileSaveBasePath + taskid
+ "\tSQLMAP响应\t" + Properties.SingleSQLTestResultFileName + taskid);
Result.saveResultToFile(Properties.SingleSQLTestResultFileName + taskid, data);
} else {
Result.writeToFile("SQL注入\t存在\t问题链接\t"+uri+"\t请求数据包\t" + Properties.requestFileSaveBasePath + taskid
+ "\tSQLMAP响应\t" + Properties.SingleSQLTestResultFileName + taskid);
Result.saveResultToFile(Properties.SingleSQLTestResultFileName + taskid, data);
}
}
}
}
public class Start {
private String url;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
public class Result {
private static BufferedWriter bw;
public static void init() {
try {
Result.bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(Properties.resultFilePath)),"UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public static void writeToFile(String content) {
try {
bw.write(content);
bw.newLine();
bw.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void saveResultToFile(String filePathAndName, String result) {
// *** 将SQLMAPAPI测试结果写入结果文件中
FileWriter fw = null;
BufferedWriter bufw = null;
File file = new File(filePathAndName);
try {
if (file.exists()) {
//如果文件已存在
} else {
//如果文件目录不存在,创建文件
String filePath = filePathAndName.substring(0, filePathAndName.lastIndexOf("\\"));
File folder = new File(filePath);
folder.mkdirs();
//如果文件不存在,创建文件
file.createNewFile();
}
//创建输出流
fw = new FileWriter(file,true);
bufw = new BufferedWriter(fw);
//输出
bufw.write(result);
bufw.newLine();
bufw.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (bufw != null) {
try {
bufw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fw != null) {
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static void saveFullHttpRequestToFile(String filePathAndName, FullHttpRequest result) {
// *** 将SQLMAPAPI测试结果写入结果文件中
FileWriter fw = null;
BufferedWriter bufw = null;
File file = new File(filePathAndName);
try {
if (file.exists()) {
//如果文件已存在
} else {
//如果文件目录不存在,创建文件
String filePath = filePathAndName.substring(0, filePathAndName.lastIndexOf("\\"));
File folder = new File(filePath);
folder.mkdirs();
//如果文件不存在,创建文件
file.createNewFile();
}
//创建输出流
fw = new FileWriter(file,true);
bufw = new BufferedWriter(fw);
// **** 文件中的三个字段 ****
String top = "";
String header = "";
String content = "";
// **** 将uri由“http://.../...”处理为“/...” ****
String uri = result.uri();
for (int i = 0; i < 2; i++) {
uri = uri.substring(uri.indexOf("/") + 1);
}
uri = uri.substring(uri.indexOf("/"));
// 组装由HTTP方法、访问uri、HTTP协议版本组成的文件头
top += result.method();
top += " " + uri;
top += " " + result.protocolVersion();
// 提取request包中header内容
Set names = result.headers().names();
Iterator<String> it = names.iterator();
while (it.hasNext()) {
String name = it.next();
header += name + ": ";
header += result.headers().get(name);
header += "\r\n";
}
// 提取request包中content内容
content = result.content().toString(0, Integer.valueOf(result.headers().get("Content-Length")),
Charset.defaultCharset());
// 组装request包内容
String dataPackage = top + "\r\n" + header + "\r\n" + content;
//输出
bufw.write(dataPackage);
bufw.newLine();
bufw.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (bufw != null) {
try {
bufw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fw != null) {
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static void saveHttpResponseAndContentToFile(String originalResponsefilePathAndName,
HttpResponse httpResponse, HttpContent responseContent) {
FileWriter fw = null;
BufferedWriter bufw = null;
File file = new File(originalResponsefilePathAndName);
try {
if (file.exists()) {
//如果文件已存在
} else {
//如果文件目录不存在,创建文件
String filePath = originalResponsefilePathAndName.substring(0, originalResponsefilePathAndName.lastIndexOf("\\"));
File folder = new File(filePath);
folder.mkdirs();
//如果文件不存在,创建文件
file.createNewFile();
}
//创建输出流
fw = new FileWriter(file,true);
bufw = new BufferedWriter(fw);
//输出
bufw.write(httpResponse.toString());
bufw.newLine();
bufw.write(responseContent.content().toString(0, responseContent.content().capacity(),Charset.defaultCharset()));
bufw.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (bufw != null) {
try {
bufw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fw != null) {
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
启动SQLMapapi作为测试模块
启动Tomcat作为文件服务器及被测试网站
运行StartProxy类,启动测试工具
在浏览器中设置代理
访问Tomcat的默认页面:
http://127.0.0.1:8881/
测试结果及过程文件