0x01 效果演示
都是在社区版下展示,可以看到功能跟专业版无差别(可能会有小bug)
b站视频地址: https://www.bilibili.com/video/BV1bwWWeAEqk/
源码下载地址: https://pan.quark.cn/s/2b460d59f88b
0x02 使用
首先来到应用商店已安装然后选择php点击参数按钮
点击编辑
点击高级设置->编辑 compose 文件
然后在挂载文件处加上
- /opt/1panel/apps/openresty/openresty/1pwaf/data/db:/www/db
然后确定重建即可
来到容器
进入终端
ls可以看到db目录然后给db目录权限
chmod 777 -R db/
然后就可以在网站里面添加php文件,文件名随意内容如下,为了方便所以把内容都写在了一个文件,代码比较简陋有需要的大佬可以自己在改改(也不是不能用)
<?php
/**
* 1panel-WAF数据查看接口API文件
*
* @author Juneha juneha@qq.com
* @version 1.0
* @website https://blog.mo60.cn/
* @created 2024-08-19
*/
error_reporting(0);
// error_reporting(E_ALL);
// ini_set('display_errors', 1);
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE");
header("Access-Control-Allow-Headers: X-Requested-With, Content-Type");
header("Access-Control-Allow-Credentials: true");
class SQLite
{
function __construct($file)
{
try
{
@$this->connection=new PDO('sqlite:'.$file);
}
catch(PDOException $e)
{
try
{
$this->connection=new PDO('sqlite2:'.$file);
}
catch(PDOException $e)
{
exit('error!');
}
}
}
function __destruct()
{
$this->connection=null;
}
function query($sql)
{
return $this->connection->query($sql);
}
function getlist($sql)
{
$stmt = $this->query($sql);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
function Execute($sql)
{
return $this->query($sql)->fetch();
}
function RecordArray($sql)
{
return $this->query($sql)->fetchAll();
}
function RecordCount($sql)
{
return count($this->RecordArray($sql));
}
function RecordLastID()
{
return $this->connection->lastInsertId();
}
}
function getDocumentRoot() {
$scriptPath = __FILE__;
$documentRoot = $_SERVER['DOCUMENT_ROOT'];
while (true) {
$parentDir = dirname($scriptPath);
if ($parentDir === $documentRoot) {
break;
}
if (is_dir($parentDir . '/index.php') || is_dir($parentDir . '/public')) {
$documentRoot = $parentDir;
}
$scriptPath = $parentDir;
}
return $documentRoot;
}
function doubleSingleQuotes($string) {
$result = str_replace("'", "''", $string);
return $result;
}
function getParameterValue($key, $default = '')
{
return isset($_GET[$key]) ? doubleSingleQuotes(trim($_GET[$key])) : $default;
}
function translateWAFParams($param) {
$translations = [
'urlDefense' => 'URL 规则',
'urlHelper' => '禁止访问的 URL',
'dirFilter' => '目录过滤',
'sqlInject' => 'SQL 注入',
'xss' => 'XSS',
'phpExec' => 'PHP 脚本执行',
'oneWordTrojan' => '一句话木马',
'appFilter' => '应用危险目录过滤',
'webshell' => 'Webshell',
'protocolFilter' => '协议过滤',
'javaFileter' => 'Java 危险文件过滤',
'scannerFilter' => '扫描器过滤',
'escapeFilter' => '转义过滤',
'customRule' => '自定义规则',
'httpMethod' => 'HTTP 方法过滤',
'fileExt' => '文件上传限制',
'fileExtHelper' => '禁止上传的文件扩展名',
'fiveSeconds'=> "5 秒验证",
'acl'=> "ACL",
'captcha'=> "人机验证",
'uaBlack' => "User-Agent 黑名单",
'urlBlockList' => "URL 黑名单",
'ipBlack'=> "IP 黑名单",
'unknownWebsite'=> "未授权域名访问",
'geoRestrict'=> "地区访问限制",
'attackCount'=> "攻击频率限制",
'notFoundCount'=> "404 频率限制",
'cookieDefense'=> "Cookie 规则",
'headerDefense'=> "Header 规则",
'defaultUaBlack'=> "User-Agent 规则",
'argsDefense'=> "参数规则",
'httpRule'=>"HTTP 规则",
'defaultUrlBlack'=> "URL 规则",
'defaultIpBlack'=> "恶意 IP 组",
'fileExtCheck'=> "文件上传限制",
];
return isset($translations[$param]) ? $translations[$param] : $param;
}
function buildInClause($data) {
if (empty($data)) {
return "IN ('')";
}
if (!is_array($data)) {
$data = array($data);
}
$escaped_values = array_map(function($value) {
return "'" . doubleSingleQuotes($value) . "'";
}, $data);
$in_clause = "IN (" . implode(', ', $escaped_values) . ")";
return $in_clause;
}
$rootPath = getDocumentRoot();
$onepwafPath=$rootPath.'/../../../db/1pwaf.db';
$req_logPath=$rootPath.'/../../../db/req_log.db';
if (!file_exists($onepwafPath)){
die("1pwaf.db file does not exist!");
}
if (!file_exists($req_logPath)){
die("req_log.db file does not exist!");
}
$onepwafDB=new SQLite($onepwafPath);
$req_logDB=new SQLite($req_logPath);
$sql='';
$action = @$_GET['action'];
switch ($action) {
case 'wafstat':
$wafData = $onepwafDB->getlist("SELECT * FROM main.waf_stat order by id desc LIMIT 1");
exit(json_encode($wafData[0]));
break;
case 'sitelist':
$webList=$req_logDB->getlist("SELECT DISTINCT website_key FROM main.req_logs where website_key <> 'unknown' order by time desc");
exit(json_encode($webList));
break;
case 'list':
$page = intval(getParameterValue('page', 1));
$size = intval(getParameterValue('size', 10));
$websiteKey = getParameterValue('website_key');
$exec_rule = $_GET['exec_rule'];
$uri = getParameterValue('uri');
$ip = getParameterValue('ip');
$offset = ($page - 1) * $size;
$conditions = [];
if (!empty($websiteKey)) {
$conditions[] = "website_key='{$websiteKey}'";
}
if (!empty($exec_rule)) {
$conditions[] = "( exec_rule ".buildInClause($exec_rule)." or rule_type ".buildInClause($exec_rule)." )";
}
if (!empty($uri)) {
$conditions[] = "uri LIKE '%{$uri}%'";
}
if (!empty($ip)) {
$conditions[] = "ip='{$ip}'";
}
$sql = "SELECT * FROM main.req_logs WHERE 1=1";
if (!empty($conditions)) {
$sql .= " AND " . implode(' AND ', $conditions);
}
if (!empty($conditions)) {
$total=$req_logDB->RecordCount($sql);
}else{
$total=$req_logDB->RecordCount("SELECT * FROM main.req_logs");
}
$sql .= " ORDER BY time DESC LIMIT {$offset}, {$size}";
$req_logData['data'] = $req_logDB->getlist($sql);
$req_logData['total'] = $total;
// var_dump($sql );
for($i=0;$i<count($req_logData['data']);$i++){
$req_logData['data'][$i]['rule_type']=translateWAFParams($req_logData['data'][$i]['rule_type']);
$req_logData['data'][$i]['exec_rule']=translateWAFParams($req_logData['data'][$i]['exec_rule']);
}
exit(json_encode($req_logData));
break;
case 'clearlogs':
$status = $req_logDB->query("DELETE FROM main.req_logs");
if($status){
exit(json_encode(['status'=>1]));
}else{
exit(json_encode(['status'=>0]));
}
break;
default:
header("HTTP/1.1 404 Not Found");
exit;
break;
}
?>
前端代码如下,也是找朋友 @n1l.cn 花了半天手搓的,也是为了方便全部放在了单页里面,需要修改API_BASE_URL 为自己部署的api地址
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>拦截记录</title>
<script src="https://unpkg.com/vue@3"></script>
<link rel="stylesheet" href="https://unpkg.com/element-plus/dist/index.css" />
<link rel="icon" type="image/png" href="https://1panel.cn/img/favicon.png">
<script src="https://unpkg.com/element-plus"></script>
<script src="https://unpkg.com/@element-plus/icons-vue"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.5/codemirror.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.5/codemirror.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.5/mode/javascript/javascript.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.5/mode/nginx/nginx.min.js"></script>
<style>
body {
padding: 0;
margin: 0;
font-family: Arial, sans-serif;
}
.centered-container {
margin-top: 20px;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
min-height: 100vh;
}
.el-card {
width: 90%;
margin-bottom: 20px;
}
.home-card .header span:before {
position: absolute;
top: 4px;
left: -14px;
width: 4px;
height: 14px;
content: "";
background: var(--el-color-primary);
border-radius: 10px;
}
.home-card .header span {
position: relative;
font-size: 16px;
font-weight: 500;
margin-left: 18px;
}
.el-row {
margin-bottom: 20px;
}
.el-row:last-child {
margin-bottom: 0;
}
.el-col {
border-radius: 4px;
text-align: center;
}
.el-col span {
color: #646a73;
font-size: 14px;
}
.count {
margin-top: 10px;
}
.count span {
font-size: 25px;
color: #005eeb;
font-weight: 500;
line-height: 32px;
}
.host {
width: 240px;
margin-right: 10px;
}
.execrule {
width: 380px;
}
.form-container {
display: flex;
justify-content: space-between;
align-items: center;
gap: 10px;
}
.select-container,
.search-container {
display: flex;
gap: 10px;
}
.search-container>.el-button {
flex-shrink: 0;
}
.rounded-input {
margin-right: 10px;
}
.pag {
margin-top: 20px;
display: flex;
justify-content: flex-end;
}
tbody {
font-size: 14px;
color: #646a73;
}
.el-drawer__header {
margin-bottom: 0;
padding-bottom: 14px;
border-bottom: 1px solid #f2f2f2;
}
.margin-top {
margin-top: 20px;
}
.ad-container {
position: fixed;
top: 20px;
right: 20px;
border: 1px solid #ddd;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
overflow: hidden;
z-index: 9999;
}
.ad-image {
display: block;
width: 180px;
height: auto;
}
.close-btn {
position: absolute;
top: 5px;
right: 5px;
background: rgba(0, 0, 0, 0.5);
color: white;
border: none;
border-radius: 50%;
width: 30px;
height: 30px;
text-align: center;
line-height: 30px;
font-size: 20px;
cursor: pointer;
}
.close-btn:hover {
background: rgba(0, 0, 0, 0.7);
}
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-thumb {
background-color: #b1b3b8;
border-radius: 4px;
border: 2px solid #ffffff;
}
::-webkit-scrollbar-track {
background-color: #f5f7fa;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background-color: #909399;
}
::-webkit-scrollbar-track:hover {
background-color: #e4e7ed;
}
@media (max-width: 992px) {
.form-container {
flex-direction: column;
align-items: flex-start;
}
.select-container,
.search-container {
width: 100%;
}
.search-container>.el-button {
margin-top: 10px;
}
}
@media (max-width: 768px) {
.host {
width: 100%;
margin-right: 10px;
}
.execrule {
width: 100%;
}
.form-container {
padding: 0 10px;
}
.select-container,
.search-container {
flex-direction: column;
align-items: stretch;
gap: 5px;
}
.select-container>.el-select,
.search-container>.el-input,
.search-container>.el-button {
width: 100%;
}
.search-container>.el-input {
margin-right: 0;
}
.search-container>.el-button {
margin-top: 10px;
}
}
</style>
</head>
<body>
<div id="app" class="centered-container">
<el-card class="home-card">
<div class="header">
<span>今日状态</span>
</div>
<div style="margin-top: 20px;">
<el-row :gutter="20">
<el-col :span="6">
<span>请求</span>
<div class="count">
<span>{{ req_count }}</span>
</div>
</el-col>
<el-col :span="6">
<span>拦截</span>
<div class="count">
<span>{{ attack_count }}</span>
</div>
</el-col>
<el-col :span="6">
<span>4xx数量</span>
<div class="count">
<span>{{ count4xx }}</span>
</div>
</el-col>
<el-col :span="6">
<span>5xx数量</span>
<div class="count">
<span>{{ count5xx }}</span>
</div>
</el-col>
</el-row>
</div>
</el-card>
<el-card>
<div class="form-container">
<div class="select-container">
<el-select v-model="website_key" clearable="" placeholder="请选择" @change="handleChange" class="host">
<template #prefix="">
<span>网站</span>
</template>
<el-option v-for="item in siteList" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<el-select v-model="execRuleList" multiple="" clearable="" collapse-tags="" placeholder="请选择"
@change="handleChange" class="execrule">
<template #prefix="">
<span>命中规则</span>
</template>
<el-option v-for="item in execRuleOptions" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</div>
<div class="search-container">
<el-input v-model="searchUrl" class="rounded-input" placeholder="请输入 URL,支持模糊搜索"
clearable=""></el-input>
<el-input v-model="searchIP" class="rounded-input" placeholder="请输入 IP" clearable=""></el-input>
<el-button type="primary" plain="" @click="handleChange">
搜索
</el-button>
</div>
</div>
</el-card>
<el-card>
<div class="card-header" style="display: flex; align-items: center;">
<span style="font-weight: bold;">拦截记录
<el-divider direction="vertical" />
</span>
<el-button type="primary" plain="" @click="clearlogs">
清空日志
</el-button>
<div style="margin-left: auto;">
<el-button type="primary" plain="" @click="listData">
刷新
</el-button>
</div>
</div>
<el-table :data="tableData" style="margin-top: 20px; width: 100%;">
<el-table-column prop="ip" label="IP" min-width="130"></el-table-column>
<el-table-column prop="host" label="域名" min-width="250"></el-table-column>
<el-table-column prop="uri" label="url" :show-overflow-tooltip="true" min-width="300"></el-table-column>
<el-table-column prop="ipLocation" label="IP归属地" width="150">
<template #default="scope">
{{ scope.row.ip_country_zh }} {{ scope.row.ip_province_zh }}
</template>
</el-table-column>
<el-table-column prop="action" label="动作" min-width="100">
<template #default="scope">
<el-tag v-if="scope.row.action === 'deny'" type="error">
禁止
</el-tag>
<el-tag v-else="" type="success">
允许
</el-tag>
</template>
</el-table-column>
<el-table-column prop="exec_rule" label="命中规则" min-width="150"></el-table-column>
<el-table-column prop="rule_type" label="类型" min-width="100"></el-table-column>
<el-table-column prop="localtime" label="时间" min-width="180">
</el-table-column>
<el-table-column fixed="right" label="操作" width="100">
<template #default="scope">
<el-button style="color: #005eeb;" link="" @click="handleClick(scope.row)">
详情
</el-button>
</template>
</el-table-column>
</el-table>
<div class="pag">
<el-pagination v-if="!smallPagination" :page-sizes="[5, 10, 20, 50, 100]" size="default"
layout="total, sizes, prev, pager, next, jumper" :total="total" @size-change="handleSizeChange"
@current-change="handleCurrentChange">
</el-pagination>
<el-pagination v-else size="small" background layout="prev, pager, next" :total="total" class="mt-4"
@size-change="handleSizeChange" @current-change="handleCurrentChange" />
</div>
</el-card>
<!-- 抽屉 -->
<el-drawer v-model="drawer" :size="drawerSize" @close="handleDrawerClose">
<template #header>
<el-page-header @back="drawer = false" title="返回">
<template #content>
<span class="text-large font-600 mr-3"> 详情 </span>
</template>
</el-page-header>
</template>
<template #default>
<div>
<el-descriptions title="攻击详情" :column="1" border>
<el-descriptions-item label="攻击 IP">
{{ selectedRow.ip }}
</el-descriptions-item>
<el-descriptions-item label="时间">
{{ selectedRow.localtime }}
</el-descriptions-item>
<el-descriptions-item label="动作">
{{ selectedRow.action === 'deny' ? '禁止' : '允许'}}
</el-descriptions-item>
<el-descriptions-item label="命中规则">
{{ selectedRow.exec_rule }}
</el-descriptions-item>
<el-descriptions-item label="匹配值">
{{ decodeBase64(selectedRow.match_value) }}
</el-descriptions-item>
</el-descriptions>
<el-descriptions class="margin-top" title="HTTP">
</el-descriptions>
<textarea id="nginx-log-editor"></textarea>
</div>
</template>
</el-drawer>
<!-- 广告 -->
<div v-show="adDisplay" class="ad-container">
<a href="https://www.lxware.cn/?code=ifconfig" target="_blank">
<img src="https://src.sjtu.edu.cn/media/images/2024/08/20/b76df827-e1bb-4e79-b728-ea5c37fe1f0c-moDBtzpW.png" alt="Advertisement" class="ad-image">
</a>
<button class="close-btn" @click="adDisplay = false">×</button>
</div>
</div>
<script>
const { ElMessageBox, ElMessage } = ElementPlus;
const API_BASE_URL = '';
const App = {
data() {
return {
req_count: 0,
attack_count: 0,
count4xx: 0,
count5xx: 0,
website_key: '',
execRuleList: [],
searchUrl: '',
searchIP: '',
currentPage: 1,
pageSize: 10,
siteList: [],
drawer: false,
smallPagination: false,
selectedRow: {},
execRuleOptions: [
{ value: 'urlDefense', label: 'URL 规则' },
{ value: 'urlHelper', label: '禁止访问的 URL' },
{ value: 'dirFilter', label: '目录过滤' },
{ value: 'sqlInject', label: 'SQL 注入' },
{ value: 'xss', label: 'XSS' },
{ value: 'phpExec', label: 'PHP 脚本执行' },
{ value: 'oneWordTrojan', label: '一句话木马' },
{ value: 'appFilter', label: '应用危险目录过滤' },
{ value: 'webshell', label: 'Webshell' },
{ value: 'protocolFilter', label: '协议过滤' },
{ value: 'javaFileter', label: 'Java 危险文件过滤' },
{ value: 'scannerFilter', label: '扫描器过滤' },
{ value: 'escapeFilter', label: '转义过滤' },
{ value: 'customRule', label: '自定义规则' },
{ value: 'httpMethod', label: 'HTTP 方法过滤' },
{ value: 'fileExt', label: '文件上传限制' },
{ value: 'fileExtHelper', label: '禁止上传的文件扩展名' },
{ value: 'fiveSeconds', label: '5 秒验证' },
{ value: 'acl', label: 'ACL' },
{ value: 'captcha', label: '人机验证' },
{ value: 'uaBlack', label: 'User-Agent 黑名单' },
{ value: 'urlBlockList', label: 'URL 黑名单' },
{ value: 'ipBlack', label: 'IP 黑名单' },
{ value: 'unknownWebsite', label: '未授权域名访问' },
{ value: 'geoRestrict', label: '地区访问限制' },
{ value: 'attackCount', label: '攻击频率限制' },
{ value: 'notFoundCount', label: '404 频率限制' },
{ value: 'cookieDefense', label: 'Cookie 规则' },
{ value: 'headerDefense', label: 'Header 规则' },
{ value: 'defaultUaBlack', label: 'User-Agent 规则' },
{ value: 'argsDefense', label: '参数规则' },
{ value: 'httpRule', label: 'HTTP 规则' },
{ value: 'defaultUrlBlack', label: 'URL 规则' },
{ value: 'defaultIpBlack', label: '恶意 IP 组' },
{ value: 'fileExtCheck', label: '文件上传限制' }
],
total: 0,
tableData: [],
editor: null,
drawerSize: '40%',
adDisplay: true,
};
},
methods: {
async fetchData() {
try {
const { data } = await axios.get(API_BASE_URL, { params: { action: 'wafstat' } });
Object.assign(this, {
req_count: data.req_count,
attack_count: data.attack_count,
count4xx: data.count4xx,
count5xx: data.count5xx,
});
} catch (error) {
console.error("API 请求失败:", error);
}
},
async siteListData() {
try {
const { data } = await axios.get(API_BASE_URL, { params: { action: 'sitelist' } });
this.siteList = data.map(item => ({
value: item.website_key,
label: item.website_key,
}));
} catch (error) {
console.error("API 请求失败:", error);
}
},
async listData() {
try {
const { data } = await axios.get(API_BASE_URL, {
params: {
action: 'list',
page: this.currentPage,
size: this.pageSize,
website_key: this.website_key,
uri: this.searchUrl,
ip: this.searchIP,
exec_rule: this.execRuleList
}
});
this.total = data.total;
this.tableData = data.data;
} catch (error) {
console.error("API 请求失败:", error);
}
},
async clearlogs() {
try {
await ElMessageBox.confirm('清空日志将无法恢复,是否继续?', '清空日志', {
confirmButtonText: '确定',
cancelButtonText: '取消',
});
const { data } = await axios.get(API_BASE_URL, { params: { action: 'clearlogs' } });
if (data.status === 1) {
ElMessage({ message: '日志清空成功', type: 'success' });
this.listData();
} else {
ElMessage({ message: '日志清空失败', type: 'error' });
}
} catch (error) {
if (error === 'cancel') {
ElMessage({ type: 'info', message: '清空日志已取消' });
} else {
console.error("API 请求失败:", error);
ElMessage({ message: '请求失败,请稍后重试', type: 'error' });
}
}
},
handleClick(row) {
this.selectedRow = row;
this.drawer = true;
this.$nextTick(this.initializeCodeMirror);
},
handleChange() {
this.listData();
},
handleSizeChange(size) {
this.pageSize = size;
this.listData();
},
handleCurrentChange(page) {
this.currentPage = page;
this.listData();
},
initializeCodeMirror() {
const editorElement = document.getElementById('nginx-log-editor');
this.editor = CodeMirror.fromTextArea(editorElement, {
lineNumbers: true,
mode: 'nginx',
readOnly: true,
theme: 'default',
});
this.editor.setValue(this.selectedRow.nginx_log || '暂无日志数据');
},
handleDrawerClose() {
if (this.editor) {
this.editor.toTextArea();
}
},
decodeBase64(encodedString) {
try {
const decodedString = atob(encodedString);
return decodeURIComponent(escape(decodedString));
} catch (error) {
console.error('Base64 解码失败:', error);
return null;
}
},
updateDrawerSize() {
const width = window.innerWidth;
if (width > 1200) {
this.drawerSize = '40%';
} else if (width > 768) {
this.drawerSize = '60%';
} else {
this.drawerSize = '80%';
this.smallPagination = true;
this.adDisplay = false;
}
},
},
async mounted() {
await Promise.all([this.fetchData(), this.siteListData(), this.listData()]);
this.updateDrawerSize();
window.addEventListener('resize', this.updateDrawerSize);
},
beforeUnmount() {
window.removeEventListener('resize', this.updateDrawerSize);
},
};
const app = Vue.createApp(App);
app.use(ElementPlus);
app.mount("#app");
</script>
</body>
</html>
0x03 原理 and 文档
1panel waf的日志都是保存在openresty容器里面的db文件,目录也有映射出来,我们通过php容器也去挂载一下即可读取到db文件然后读取即可
下面是简易的api接口文档
- wafstat
- 说明:获取 WAF 状态的最新记录。
- 请求方式:GET
- 请求参数:无
- 返回格式:JSON
- 返回示例:
json
{
"id": 5,
"day": "2024-08-20",
"req_count": 2085,
"attack_count": 65,
"count4xx": 917,
"count5xx": 5,
"create_date": "2024-08-19 16:06:47"
}
2.sitelist
- 说明:获取所有已记录的网站列表。
- 请求方式:GET
- 请求参数:无
- 返回格式:JSON
- 返回示例:
json
[
{ "website_key": "example.com" },
{ "website_key": "testsite.com" }
]
3.list
- 说明:获取请求日志列表,支持分页和过滤。
- 请求方式:GET
- 请求参数:
- page:页码(默认值为 1)
- size:每页条数(默认值为 10)
- website_key:网站(可选)
- exec_rule:命中规则(可选)
- uri:请求 URI(可选)
- ip:请求 IP(可选)
- 返回格式:JSON
- 返回示例:
json
{
"data": [
{
"id": "17b7d444-9631-4bd2-a09e-8f7a4d03f6b6",
"ip": "205.210.31.16",
"ip_iso": "US",
"ip_country_zh": "美国",
"ip_country_en": "United States",
"ip_province_zh": "",
"ip_province_en": "",
"ip_longitude": "-97.822",
"ip_latitude": "37.751",
"localtime": "2024-08-20 03:26:14",
"time": 1724124372.559,
"server_name": "8.222.178.70",
"website_key": "8.222.178.70:8080",
"host": "8.222.178.70",
"method": "GET",
"uri": "/",
"user_agent": "Expanse, a Palo Alto Networks company, searches across the global IPv4 space multiple times per day to identify customers' presences on the Internet. If you would like to be excluded from our scans, please send IP addresses/domains to: scaninfo@paloaltonetworks.com",
"exec_rule": "恶意 IP 组",
"rule_type": "",
"match_rule": "",
"match_value": "MjA1LjIxMC4zMS4xNg==",
"nginx_log": "GET / HTTP/1.0\nUSER-AGENT: Expanse, a Palo Alto Networks company, searches across the global IPv4 space multiple times per day to identify customers' presences on the Internet. If you would like to be excluded from our scans, please send IP addresses/domains to: scaninfo@paloaltonetworks.com\nACCEPT: */*\n",
"blocking_time": 0,
"action": "deny",
"is_block": 0,
"is_attack": 1
}
],
"total": 13
}
4.clearlogs
- 说明:清除所有请求日志。
- 请求方式:GET
- 请求参数:无
- 返回格式:JSON
- 返回示例:
json
{
"status": 1
}
status:操作状态,1 表示成功,0 表示失败。
One comment
6666666666