- 網際網路下駭客發動攻擊通常少不了木馬程式的植入。因此阻絕攻擊的第一步在於如何防止惡意檔案或程式的植入。
- 以Java Web而言,若被植入程式到部署目錄,就可能被以URL的方式執行。
- 藉由監控特定資料夾的檔案寫入,避免被植入惡意檔案。
- 使用NIO2的WatchService監看特定資料夾是否有檔案異動。
- 使用「樣板方法模式(template method)」建立告警程式,負責在WatchService接收到檔案異動事件後,觸發告警程式進行後續作為,如:
- 日誌紀錄(log)
- 馬上移除檔案
- 打包程式進行部署。
Guardian
AbstractAlertListener
- package fsWatch;
- import java.nio.file.*;
- import static java.nio.file.StandardWatchEventKinds.*;
- import static java.nio.file.LinkOption.*;
- import java.nio.file.attribute.*;
- import java.io.*;
- import java.util.*;
- import org.apache.log4j.Logger;
- public class Guardian {
- static Logger logger = Logger.getLogger(Guardian.class);
- static Properties myProps = new Properties();
- private final WatchService watchService;
- private final Map<WatchKey, Path> keys;
- private final boolean recursive;
- private boolean trace = false;
- // 1. 將路徑dir指定給WatchService監看
- private void register(Path dir) throws IOException {
- WatchKey key = dir.register(watchService, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);//
- if (trace) {
- Path prev = keys.get(key);
- if (prev == null) {
- logger.info(String.format("register: %s", dir));
- } else {
- if (!dir.equals(prev)) {
- logger.info(String.format("update: %s -> %s", prev, dir));
- }
- }
- }
- keys.put(key, dir);
- }
- // 2. 使用Files.walkFileTree遞迴走訪所有目錄,都指定給WatchService監看
- private void registerAll(final Path start) throws IOException {
- Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
- @Override
- public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
- throws IOException {
- register(dir);
- return FileVisitResult.CONTINUE;
- }
- });
- }
- // 3. 建立建構子
- Guardian(Path dir, boolean recursive) throws IOException {
- this.watchService = FileSystems.getDefault().newWatchService();
- this.keys = new HashMap<WatchKey, Path>();
- this.recursive = recursive;
- if (recursive) {
- logger.info(String.format("Scanning %s ...", dir));
- registerAll(dir);
- logger.info("Done.");
- } else {
- register(dir);
- }
- this.trace = true;
- }
- // 4. 建立與告警程式的關聯
- private List<AbstractAlertListener> listeners = new ArrayList<>();
- void addListener(AbstractAlertListener alarmListener) {
- listeners.add(alarmListener);
- }
- // 5. 建立監控核心程式
- // 5.1 將WatchService與Map<WatchKey, Path>結合
- // 5.2 觸發事件時一併觸發其他告警程式
- @SuppressWarnings("unchecked")
- void watch() throws Exception {
- for (;;) {
- WatchKey key;
- try {
- key = watchService.take();
- } catch (InterruptedException x) {
- return;
- }
- Path dir = keys.get(key);
- if (dir == null) {
- logger.error("WatchKey not recognized!!");
- continue;
- }
- for (WatchEvent<?> event : key.pollEvents()) {
- WatchEvent.Kind<?> kind = event.kind();
- if (kind == OVERFLOW) {
- continue;
- }
- WatchEvent<Path> ev = (WatchEvent<Path>) event;
- Path name = ev.context();
- Path child = dir.resolve(name);
- // action
- for (AbstractAlertListener l : listeners) {
- l.alertTemplate(event.kind(), child);
- }
- if (recursive && (kind == ENTRY_CREATE)) {
- try {
- if (Files.isDirectory(child, NOFOLLOW_LINKS)) {
- registerAll(child);
- }
- } catch (IOException x) {
- }
- }
- }
- // reset key and remove from set if directory no longer accessible
- boolean valid = key.reset();
- if (!valid) {
- keys.remove(key);
- // all directories are inaccessible
- if (keys.isEmpty()) {
- break;
- }
- }
- }
- }
- // 6. 使用Thread啟動Guardian
- private static void launchGuardian(final String path) throws Exception {
- boolean recursive = true;
- Path dir = Paths.get(path);
- Guardian guardian = new Guardian(dir, recursive);
- guardian.addListener(new LogAlertListener());
- guardian.addListener(new FileMoveAlertListener());
- guardian.watch();
- }
- // 7. 載入參數檔並啟動Guardian
- public static void main(String[] args) {
- try {
- FileInputStream fis = new FileInputStream("watchDog.properties");
- myProps.load(fis);
- String watchDir = myProps.getProperty("path.include");
- String moveTo = myProps.getProperty("path.moveTo");
- Files.createDirectories(Paths.get(watchDir));
- Files.createDirectories(Paths.get(moveTo));
- launchGuardian(watchDir);
- } catch (Exception e) {
- logger.error(e.getMessage(), e);
- System.exit(0);
- }
- }
- }
- package fsWatch;
- import static java.nio.file.StandardWatchEventKinds.*;
- import java.nio.file.Path;
- import java.nio.file.WatchEvent;
- import org.apache.log4j.Logger;
- public abstract class AbstractAlertListener {
- static int seq;
- protected final Logger logger = Logger.getLogger(this.getClass());
- abstract void strategy(WatchEvent.Kind<?> kind, Path path);
- void hook(WatchEvent.Kind<?> kind, Path path) {
- // default null implementation
- }
- protected void alertTemplate(WatchEvent.Kind<?> kind, Path path) {
- if (kind.equals(ENTRY_CREATE)) {
- strategy(kind, path);
- }
- hook(kind, path);
- }
- }
LogAlertListener
- package fsWatch;
- import java.nio.file.Path;
- import java.nio.file.WatchEvent.Kind;
- public class LogAlertListener extends AbstractAlertListener {
- @Override
- void strategy(Kind kind, Path path) {
- logger.warn(kind.name() + ", " + path);
- }
- }
FileMoveAlertListener
watchDog.properties
- package fsWatch;
- import java.io.File;
- import java.io.IOException;
- import java.io.RandomAccessFile;
- import java.nio.file.Files;
- import java.nio.file.Path;
- import java.nio.file.Paths;
- import java.nio.file.StandardCopyOption;
- import java.nio.file.WatchEvent.Kind;
- public class FileMoveAlertListener extends AbstractAlertListener {
- @Override
- void strategy(Kind kind, Path path) {
- move(path);
- }
- private void move(Path path) {
- wait4Ready(path);
- Path moveTo = Paths
- .get(Guardian.myProps.getProperty("path.moveTo") + File.separator
- + path.getFileName() + "." + ++seq);
- try {
- Files.move(path, moveTo, StandardCopyOption.REPLACE_EXISTING);
- logger.info("File Moved: " + path + " -> " + moveTo);
- } catch (IOException e) {
- logger.error(e.getMessage(), e);
- }
- }
- private void wait4Ready(Path path) {
- boolean locked = true;
- RandomAccessFile raf = null;
- while (locked) {
- File file = null;
- try {
- file = new File(path.toString());
- raf = new RandomAccessFile(file, "r");
- raf.seek(file.length());
- locked = false;
- } catch (IOException e) {
- locked = file.exists();
- if (locked) {
- logger.debug("File locked: '" + file.getAbsolutePath() + "'");
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e1) {
- logger.error(e1.getMessage(), e);
- }
- } else {
- logger.debug("File was deleted while copying: '"
- + file.getAbsolutePath() + "'");
- }
- } finally {
- if (raf != null) {
- try {
- raf.close();
- } catch (IOException e) {
- logger.error(e.getMessage(), e);
- }
- }
- }
- }
- }
- }
- path.include = D:\\security\\include
- path.moveTo = D:\\security\\bad
log4j.properties
- log4j.rootLogger=INFO, stdout, logfile
- log4j.appender.stdout=org.apache.log4j.ConsoleAppender
- log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
- log4j.appender.stdout.layout.ConversionPattern=%-5p %l - %m%n
- log4j.appender.stdout.encoding=UTF-8
- log4j.appender.logfile=org.apache.log4j.DailyRollingFileAppender
- log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
- #log4j.appender.logfile.layout.ConversionPattern=%d [%t] %-5p (%F:%L) - %m%n
- log4j.appender.logfile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %l - %m%n
- log4j.appender.logfile.File=D:/security/watchDog.log
- log4j.appender.logfile.DatePattern='-'yyyyMMdd'.log'
- log4j.appender.logfile.encoding=UTF-8
參考資料: http://152.92.236.11/tutorial_java/essential/io/examples/WatchDir.java
沒有留言:
張貼留言