2019年10月5日 星期六

監控程式實作

程式主旨:
  1. 網際網路下駭客發動攻擊通常少不了木馬程式的植入。因此阻絕攻擊的第一步在於如何防止惡意檔案或程式的植入。
  2. 以Java Web而言,若被植入程式到部署目錄,就可能被以URL的方式執行。
  3. 藉由監控特定資料夾的檔案寫入,避免被植入惡意檔案。
程式架構:
  1. 使用NIO2的WatchService監看特定資料夾是否有檔案異動。
  2. 使用「樣板方法模式(template method)」建立告警程式,負責在WatchService接收到檔案異動事件後,觸發告警程式進行後續作為,如:
    • 日誌紀錄(log)
    • 馬上移除檔案
  3. 打包程式進行部署。
程式內容:

Guardian
  1. package fsWatch;
  2.  
  3. import java.nio.file.*;
  4. import static java.nio.file.StandardWatchEventKinds.*;
  5. import static java.nio.file.LinkOption.*;
  6. import java.nio.file.attribute.*;
  7. import java.io.*;
  8. import java.util.*;
  9.  
  10. import org.apache.log4j.Logger;
  11.  
  12. public class Guardian {
  13.  
  14. static Logger logger = Logger.getLogger(Guardian.class);
  15. static Properties myProps = new Properties();
  16.  
  17. private final WatchService watchService;
  18. private final Map<WatchKey, Path> keys;
  19. private final boolean recursive;
  20. private boolean trace = false;
  21.  
  22. // 1. 將路徑dir指定給WatchService監看
  23. private void register(Path dir) throws IOException {
  24. WatchKey key = dir.register(watchService, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);//
  25. if (trace) {
  26. Path prev = keys.get(key);
  27. if (prev == null) {
  28. logger.info(String.format("register: %s", dir));
  29. } else {
  30. if (!dir.equals(prev)) {
  31. logger.info(String.format("update: %s -> %s", prev, dir));
  32. }
  33. }
  34. }
  35. keys.put(key, dir);
  36. }
  37.  
  38. // 2. 使用Files.walkFileTree遞迴走訪所有目錄,都指定給WatchService監看
  39. private void registerAll(final Path start) throws IOException {
  40. Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
  41. @Override
  42. public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
  43. throws IOException {
  44. register(dir);
  45. return FileVisitResult.CONTINUE;
  46. }
  47. });
  48. }
  49.  
  50. // 3. 建立建構子
  51. Guardian(Path dir, boolean recursive) throws IOException {
  52. this.watchService = FileSystems.getDefault().newWatchService();
  53. this.keys = new HashMap<WatchKey, Path>();
  54. this.recursive = recursive;
  55.  
  56. if (recursive) {
  57. logger.info(String.format("Scanning %s ...", dir));
  58. registerAll(dir);
  59. logger.info("Done.");
  60. } else {
  61. register(dir);
  62. }
  63.  
  64. this.trace = true;
  65. }
  66.  
  67. // 4. 建立與告警程式的關聯
  68. private List<AbstractAlertListener> listeners = new ArrayList<>();
  69. void addListener(AbstractAlertListener alarmListener) {
  70. listeners.add(alarmListener);
  71. }
  72. // 5. 建立監控核心程式
  73. // 5.1 將WatchService與Map<WatchKey, Path>結合
  74. // 5.2 觸發事件時一併觸發其他告警程式
  75. @SuppressWarnings("unchecked")
  76. void watch() throws Exception {
  77. for (;;) {
  78.  
  79. WatchKey key;
  80. try {
  81. key = watchService.take();
  82. } catch (InterruptedException x) {
  83. return;
  84. }
  85.  
  86. Path dir = keys.get(key);
  87. if (dir == null) {
  88. logger.error("WatchKey not recognized!!");
  89. continue;
  90. }
  91.  
  92. for (WatchEvent<?> event : key.pollEvents()) {
  93. WatchEvent.Kind<?> kind = event.kind();
  94. if (kind == OVERFLOW) {
  95. continue;
  96. }
  97. WatchEvent<Path> ev = (WatchEvent<Path>) event;
  98. Path name = ev.context();
  99. Path child = dir.resolve(name);
  100. // action
  101. for (AbstractAlertListener l : listeners) {
  102. l.alertTemplate(event.kind(), child);
  103. }
  104. if (recursive && (kind == ENTRY_CREATE)) {
  105. try {
  106. if (Files.isDirectory(child, NOFOLLOW_LINKS)) {
  107. registerAll(child);
  108. }
  109. } catch (IOException x) {
  110. }
  111. }
  112. }
  113. // reset key and remove from set if directory no longer accessible
  114. boolean valid = key.reset();
  115. if (!valid) {
  116. keys.remove(key);
  117. // all directories are inaccessible
  118. if (keys.isEmpty()) {
  119. break;
  120. }
  121. }
  122. }
  123. }
  124. // 6. 使用Thread啟動Guardian
  125. private static void launchGuardian(final String path) throws Exception {
  126. boolean recursive = true;
  127. Path dir = Paths.get(path);
  128. Guardian guardian = new Guardian(dir, recursive);
  129. guardian.addListener(new LogAlertListener());
  130. guardian.addListener(new FileMoveAlertListener());
  131. guardian.watch();
  132. }
  133.  
  134.  
  135. // 7. 載入參數檔並啟動Guardian
  136. public static void main(String[] args) {
  137. try {
  138. FileInputStream fis = new FileInputStream("watchDog.properties");
  139. myProps.load(fis);
  140.  
  141. String watchDir = myProps.getProperty("path.include");
  142. String moveTo = myProps.getProperty("path.moveTo");
  143. Files.createDirectories(Paths.get(watchDir));
  144. Files.createDirectories(Paths.get(moveTo));
  145.  
  146. launchGuardian(watchDir);
  147.  
  148. } catch (Exception e) {
  149. logger.error(e.getMessage(), e);
  150. System.exit(0);
  151. }
  152.  
  153. }
  154. }
  155.  
  156.  
AbstractAlertListener
  1. package fsWatch;
  2. import static java.nio.file.StandardWatchEventKinds.*;
  3. import java.nio.file.Path;
  4. import java.nio.file.WatchEvent;
  5. import org.apache.log4j.Logger;
  6.  
  7. public abstract class AbstractAlertListener {
  8. static int seq;
  9. protected final Logger logger = Logger.getLogger(this.getClass());
  10.  
  11. abstract void strategy(WatchEvent.Kind<?> kind, Path path);
  12.  
  13. void hook(WatchEvent.Kind<?> kind, Path path) {
  14. // default null implementation
  15. }
  16. protected void alertTemplate(WatchEvent.Kind<?> kind, Path path) {
  17. if (kind.equals(ENTRY_CREATE)) {
  18. strategy(kind, path);
  19. }
  20. hook(kind, path);
  21. }
  22. }
  23.  

LogAlertListener
  1. package fsWatch;
  2.  
  3. import java.nio.file.Path;
  4. import java.nio.file.WatchEvent.Kind;
  5.  
  6. public class LogAlertListener extends AbstractAlertListener {
  7.  
  8. @Override
  9. void strategy(Kind kind, Path path) {
  10. logger.warn(kind.name() + ", " + path);
  11. }
  12.  
  13. }

FileMoveAlertListener
  1. package fsWatch;
  2.  
  3. import java.io.File;
  4. import java.io.IOException;
  5. import java.io.RandomAccessFile;
  6. import java.nio.file.Files;
  7. import java.nio.file.Path;
  8. import java.nio.file.Paths;
  9. import java.nio.file.StandardCopyOption;
  10. import java.nio.file.WatchEvent.Kind;
  11.  
  12. public class FileMoveAlertListener extends AbstractAlertListener {
  13.  
  14. @Override
  15. void strategy(Kind kind, Path path) {
  16. move(path);
  17. }
  18.  
  19. private void move(Path path) {
  20. wait4Ready(path);
  21. Path moveTo = Paths
  22. .get(Guardian.myProps.getProperty("path.moveTo") + File.separator
  23. + path.getFileName() + "." + ++seq);
  24. try {
  25. Files.move(path, moveTo, StandardCopyOption.REPLACE_EXISTING);
  26. logger.info("File Moved: " + path + " -> " + moveTo);
  27. } catch (IOException e) {
  28. logger.error(e.getMessage(), e);
  29. }
  30. }
  31.  
  32. private void wait4Ready(Path path) {
  33. boolean locked = true;
  34. RandomAccessFile raf = null;
  35. while (locked) {
  36. File file = null;
  37. try {
  38. file = new File(path.toString());
  39. raf = new RandomAccessFile(file, "r");
  40. raf.seek(file.length());
  41. locked = false;
  42. } catch (IOException e) {
  43. locked = file.exists();
  44. if (locked) {
  45. logger.debug("File locked: '" + file.getAbsolutePath() + "'");
  46. try {
  47. Thread.sleep(1000);
  48. } catch (InterruptedException e1) {
  49. logger.error(e1.getMessage(), e);
  50. }
  51. } else {
  52. logger.debug("File was deleted while copying: '"
  53. + file.getAbsolutePath() + "'");
  54. }
  55. } finally {
  56. if (raf != null) {
  57. try {
  58. raf.close();
  59. } catch (IOException e) {
  60. logger.error(e.getMessage(), e);
  61. }
  62. }
  63. }
  64. }
  65. }
  66.  
  67. }
  68.  
watchDog.properties
  1. path.include = D:\\security\\include
  2. path.moveTo = D:\\security\\bad

log4j.properties
  1. log4j.rootLogger=INFO, stdout, logfile
  2.  
  3. log4j.appender.stdout=org.apache.log4j.ConsoleAppender
  4. log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
  5. log4j.appender.stdout.layout.ConversionPattern=%-5p %l - %m%n
  6. log4j.appender.stdout.encoding=UTF-8
  7.  
  8. log4j.appender.logfile=org.apache.log4j.DailyRollingFileAppender
  9. log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
  10. #log4j.appender.logfile.layout.ConversionPattern=%d [%t] %-5p (%F:%L) - %m%n
  11. log4j.appender.logfile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %l - %m%n
  12. log4j.appender.logfile.File=D:/security/watchDog.log
  13. log4j.appender.logfile.DatePattern='-'yyyyMMdd'.log'
  14. log4j.appender.logfile.encoding=UTF-8


參考資料: http://152.92.236.11/tutorial_java/essential/io/examples/WatchDir.java

沒有留言:

張貼留言