文章目录
简介
最近发现公司线上的应用老出现访问异常的情况,排查nginx的access.log日志文件,发现了大量恶意攻击的代码,内容如下
主要内容为"\x03\x00\x00/*\xE0\x00\x00\x00\x00\x00Cookie: mstshash=Administr" 400 173 “-” “-”
一般黑客的ip地址查询都是在国外的
使用nginx的deny属性禁止黑名单IP访问
然后想起用nginx的ip黑名单来限制
在nginx.conf配置文件中http标签中加入
#把94.232.40.111列入黑名单
deny 94.232.40.111;
由于在nginx.conf中直接写入deny代码不怎么优雅,把信息提取成一个配置文件中
在nginx.conf文件的路径同级写入一个blackListIp.conf文件
blackListIp.conf文件内容如下
deny 94.232.40.111;
在nginx中的http标签中引入黑名单配置文件
http {
#屏蔽黑名单IP
include blackListIp.conf;
}
运行一段时间发现还是会出现nginx被恶意攻击宕机了,后面查看nginx的access.log日志发现了大量模式的黑客IP,再排查error.log内容如下
关键信息 access forbidden by rule 是拒绝访问意思,表示黑名单是生效了
但是手动去access.log日志找黑客IP然后加入黑名单配置文件太麻烦,于是手撸了一个java工具类解决此问题.
Java工具类
1.定义恶意攻击代码信息列表
可以根据需求自行扩展
/**
* 自定义匹配恶意攻击的信息
*/
private static final List<String> MATCHER_LIST = Arrays.asList("mstshash=Administr");
2.解析nginx日志中的非法访问的IP地址
/**
* 解析nginx日志中的非法访问的IP地址
* @param logPath
* @return 非法IP地址
* @throws Exception
*/
private static Set<String> readNginxAccess(String logPath) throws Exception {
File file = new File(logPath);
if (!file.exists()) {
logger.error("{} not exists", logPath);
return null;
}
Set<String> set = new HashSet<>();
InputStreamReader ir = new InputStreamReader(new FileInputStream(file));
LineNumberReader input = new LineNumberReader(ir);
String line;
while ((line = input.readLine()) != null) {
final String fLine = line;
for (String str : MATCHER_LIST) {
//如果匹配上了,把ip加入set集合并结束for循环
if (fLine.contains(str)) {
System.out.println(fLine);
set.add(fLine.split(" ")[0]);
break;
}
}
}
return set;
}
3.写入nginx的黑名单文件中
/**
* 换行符
*/
private static String lineSeparator = "\r\n";
/**
* 日期格式
*/
private final static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy_MM_dd_HH_ss");
/**
* 写入黑名单列表
* @param blackListConfigPath 黑名单配置文件路径
* @param blackListSet 扫描出来的黑客ip集合
* @return (如果扫描出来的黑客IP地址和已存在的不完全重叠, 返回true, 否则false)
* @throws Exception
*/
private static boolean writeBlacklist(String blackListConfigPath, Set<String> blackListSet) throws Exception {
File file = new File(blackListConfigPath);
if (!file.exists()) {
file.createNewFile();
}
List<String> list = new ArrayList<>();
InputStreamReader ir = new InputStreamReader(new FileInputStream(file));
LineNumberReader input = new LineNumberReader(ir);
String line;
String ip;
while ((line = input.readLine()) != null) {
if ("".equals(line.trim()) || line.startsWith("#")) {
continue;
}
ip = line.split(" ")[1];
ip = ip.substring(0, ip.length() - 1);
list.add(ip);
}
//遍历添加黑名单列表中不存在的IP
Set<String> filterSet = new HashSet();
blackListSet.forEach(ipaddr -> {
if (!list.contains(ipaddr)) {
filterSet.add(ipaddr);
}
});
if (filterSet.size() > 0) {
FileWriter out = new FileWriter(file, true);
BufferedWriter bw = new BufferedWriter(out);
for (String ipaddr : filterSet) {
bw.write(lineSeparator);
bw.write(String.format("deny %s;", ipaddr));
logger.info("黑客IP:{} 已写入nginx黑名单列表", ipaddr);
}
//写入文件修改时间
bw.write(lineSeparator);
bw.write("#" + simpleDateFormat.format(new Date()));
bw.flush();
bw.close();
return true;
} else {
logger.info("未发现新的黑客IP地址");
return false;
}
}
4.工具类集成方法
- 1.先找到非法访问IP的列表
- 2.把获取列表信息写入nginx的黑名单配置文件
- 3.如果写入成功,则备份access.log文件,然后执行nginx -s reload命令刷新配置文件
/**
* 扫描黑名单
* @param logPath access.log文件的路径
* @param blackListPath 黑名单配置文件路径
* @param binPath nginx的启动文件路径
* @throws Exception
*/
public static void scanningBlackList(String logPath, String blackListPath, String binPath) throws Exception {
//第一步 找到非法访问IP
Set<String> set = NginxUtil.readNginxAccess(logPath);
if (set!=null && set.size() > 0) {
//第二步,写入非法IP进nginx的blackList文件
boolean flag = NginxUtil.writeBlacklist(blackListPath, set);
//第三步,备份之前的access.log,刷新nginx配置使黑名单生效
if (flag) {
//备份nginx日志文件
String backUpLogPath = logPath + "_backup_" + simpleDateFormat.format(new Date());
Process process = Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", String.format("mv %s %s", logPath, backUpLogPath)}, null, null);
process.waitFor();
//执行 /usr/local/nginx/nginx -s reload 会重新生成一个新的access.log文件
process = Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", String.format("%s -s reload", binPath)}, null, null);
process.waitFor();
}
}
}
5.测试Main函数
下面通过使用java源生的定时任务线程池**(scheduledThreadPool** )来执行任务,如果不需要集成了web应用中,则下面的代码打包完仍在服务器上面运行就可以启动,如果需要集成到web应用中,则请查看下面的步骤6
public static void main(String[] args) throws Exception {
//nginx的工作空间
String nginxPath = "/usr/local/nginx/";
//nging的access.log路径
String logPath = nginxPath + "logs/access.log";
//黑名单配置文件路径
String blackListPath = nginxPath + "conf/blackListIp.conf";
//启动脚本文件路径
String binPath = nginxPath + "bin/nginx";
//使用单线程线程池执行定时任务
ScheduledExecutorService scheduledThreadPool = Executors.newSingleThreadScheduledExecutor();
//应用启动10秒后,每隔30秒执行一次任务
scheduledThreadPool.scheduleAtFixedRate(()->{
try {
scanningBlackList(logPath, blackListPath, binPath);
} catch (Exception e) {
e.printStackTrace();
}
}, 10, 30, TimeUnit.SECONDS);
}
6.扩展到springboot应用里使用
配置文件
application.yml
nginx:
#工作空间
workspace: /usr/local/nginx/
#启动脚本
sbin: ${nginx.workspace}sbin/nginx
#日志文件
log: ${nginx.workspace}logs/access.log
#黑名单IP存储文件
blackListIp: ${nginx.workspace}conf/blackListIp.conf
配置类
@Configuration
@ConfigurationProperties(prefix = "nginx")
public class NginxParam {
private String sbin;
private String log;
private String blackListIp;
//省略get set
}
定时任务类
@Component
public class NginxScheduling {
@Autowired
NginxParam nginxParam;
/**
* 扫描非法IP
* 每隔30秒扫描一次非法IP并加入黑名单
*/
@Scheduled(fixedDelay = 30000)
public void ScanForIllegalIP() throws Exception {
NginxUtil.scanningBlackList(nginxParam.getLog(),nginxParam.getBlackListIp(),nginxParam.getSbin());
}
}
启动类添加开启定时任务注解
@SpringBootApplication
@EnableScheduling
public class GuardApplication {
public static void main(String[] args) {
SpringApplication.run(GuardApplication.class, args);
}
}
实战效果
查看nginx的log目录,发现备份文件多了几个,代表着已经有新的黑客IP被列入黑名单了
查看/usr/local/nginx/conf/blackListIp.conf文件发现已经新增了几个IP了,代表着任务已经完成了.