Безопасные/Шифрованные файлы log4j
У меня проблема; Безопасность. У меня есть приложение java swing, у которого есть файлы журнала, сгенерированные с помощью log4j для поддержки проблем в случае отслеживания
ошибка.
Мне нужно ecrypt/cypher/защитить файлы, чтобы клиент не мог их открыть и увидеть (по крайней мере, не так понятно для человека), и в то же время, когда техническая команда поддержки принимает эти файлы они будут знать, как читать (дешифровать).
Я сделал много поисков, и я попробовал свой лучший вариант, который нашел, который создает пользовательский appender, расширяя SkeletonAppender
.
Теперь знайте, что у меня log4j работает отлично, как описано ниже, но я создал новый класс для его шифрования, но я не могу заставить его работать даже с простой настройкой, чтобы доза не создавала файл, поэтому я могу продолжить в части ecnryption.
Любая помощь, ссылки хорошие.
Рабочая... версия
<appender name="cache" class="com.MyAppender">
<param name="Threshold" value="ALL" />
<param name="ImmediateFlush" value="true" />
<param name="File" value="${home}/logs/cache.log"/>
<param name="Append" value="true"/>
<param name="Threshold" value="ALL" />
<param name="Encoding" value="UTF-8" />
<layout class="org.apache.log4j.EnhancedPatternLayout">
<param name="ConversionPattern" value="%-5p %d{MMM-dd-yyyy HH:mm:ss,SSS} %c{1} - %m%n" />
</layout>
</appender>
Не работает... версия
<appender name="cache" class="com.MyAppender">
<param name="Threshold" value="ALL" />
<param name="ImmediateFlush" value="true" />
<param name="File" value="${home}/logs/cache.log"/>
<param name="Append" value="true"/>
<param name="Threshold" value="ALL" />
<param name="Encoding" value="UTF-8" />
<rollingPolicy class="org.apache.log4j.rolling.TimeBasedRollingPolicy">
<param name="FileNamePattern"
value="${home}/logs/cache.%d{yyyy-MM-dd-HH}.gz" />
<param name="ActiveFileName" value="${home}/logs/cache.log" />
</rollingPolicy>
<layout class="org.apache.log4j.EnhancedPatternLayout">
<param name="ConversionPattern"
value="%-5p %d{MMM-dd-yyyy HH:mm:ss,SSS} %c{1} - %m%n" />
</layout>
</appender>
Простой тест класса
http://www.javaworld.com/article/2075817/core-java/customized-appender-extending-org-apache-log4j-fileappender.html
package com.MyAppender;
import org.apache.log4j.spi.LoggingEvent;
public class MyAppender extends org.apache.log4j.RollingFileAppender {
private String file;
private boolean initialized = false;
private String baseFileName = null;
// private static final Log log = LogFactory.getLog(MyAppender.class);
/**
*
* write to ActivityLog
*
* @param event
* logging event invoked.
*
*/
@Override
protected void subAppend(LoggingEvent event) {
if (!initialized) {
createNewFile();
}
synchronized (this) {
super.subAppend(event);
}
}
/**
*
* create a new ActivityLog File
*
*/
public void createNewFile() {
try {
baseFileName = file;
super.setFile(baseFileName);
super.activateOptions();
initialized = true;
} catch (Exception e) {
// log.error("*#*Error in configuration of log4j params,unable to create ActivityLog file");
}
}
/**
*
* invokes File Appender activateOptions() which controls the creation of
* log files.
*
*/
@Override
public void activateOptions() {
super.setFile(file);
super.activateOptions();
}
/**
*
* Close and rename the current ActivityLog file and reset counter and
* timestamp.
*
*/
public void rollOver() {
closeFile();
initialized = false;
}
@Override
public void setFile(String file) {
this.file = file;
}
}
Затем я планирую реализовать код в
Cipher OutputStream
Ответы
Ответ 1
Возможным обходным решением проблемы является запись журналов во встроенную базу данных, которая поддерживает шифрование, например. H2 изначально поддерживает шифрование и SQLite имеет расширения шифрования с открытым исходным кодом - таким образом вы можете просто использовать JDBCAppender
и позволить базе данных заботиться о шифровании, не беспокоясь о пользовательском приложении.
Из этого вопроса, конфигурация SQLite будет выглядеть примерно так:
<appender name="jdbcAppender" class="org.apache.log4j.jdbc.JDBCAppender">
<param name="URL" value="jdbc:sqlite:D:/download/mapLogic/sf_log.db" />
<param name="user" value="" />
<param name="password" value="" />
<param name="driver" value="org.sqlite.JDBC" />
<param name="sql"
value="INSERT INTO Log(Message,Priority,Logger,Date) VALUES ('%m','%p','%c','%d{ABSOLUTE}')" />
</appender>
где ваша таблица журналов выглядит как
CREATE TABLE Log (
LogId INTEGER PRIMARY KEY,
Date DATETIME NOT NULL,
Level VARCHAR(50) NOT NULL,
Logger VARCHAR(255) NOT NULL,
Message TEXT DEFAULT NULL
);
Документацию по JDBCAppender
можно найти здесь
Существует официальное расширение шифрования для SQLite, а также в менее одного стороннего расширения с открытым исходным кодом; Мне никогда не приходилось шифровать SQLite, но если бы мне пришлось это сделать, я бы пошел с официальным расширением, если у меня не возникло проблем с ним.
Если вы используете это на клиенте, то в идеале вы сможете иметь домашний телефон программы во время загрузки, чтобы получить ключ шифрования базы данных, чтобы ключ никогда не существовал на клиентском диске (игнорируя возможность что он идет в файл подкачки) - клиент все еще может использовать отладчик или что-то еще, чтобы попытаться извлечь ключ из памяти, но, по-видимому, они недостаточно заинтересованы в расшифровке журналов, чтобы перейти к этой проблеме. Если вам нужно сохранить ключ на стороне клиента, вы можете как минимум обфускать его, хешируя его несколько раз, прежде чем использовать его, например. hard-code base_key в программе, затем во время загрузки вы создаете actual_key, запустив base_key через SHA512 (или что-то еще) несколько раз; клиент все еще мог понять, что вы делаете, используя отладчик, но опять же они, надеюсь, не захотят идти на поводу.
Ответ 2
Вариант 1: используйте пользовательский SocketAppender
В качестве альтернативы Zim-Zam ответим об использовании JDBC-совместимого приложения (не забудьте также включить безопасный транспорт, кстати, если вы спуститесь по этому маршруту), вы также можете изучить использование SocketAppender
и разверните свой собственный метод шифрования.
Вариант 2: используйте Flume и FlumeAppender
Обратитесь к документации log4j в appenders и посмотрите на использование FlumeAppender
, которая поддерживает шифрование событий:
Пример конфигурации FlumeAppender, которая настроена с помощью первичного и вторичного агентов, сжимает тело, форматирует тело с помощью RFC5424Layout и сохраняет зашифрованные события на диск. Этот образец "сжимает тело, форматирует тело с помощью RFC5424Layout и сохраняет зашифрованные события на диске:"
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" name="MyApp" packages="">
<Appenders>
<Flume name="eventLogger" compress="true" type="persistent" dataDir="./logData">
<Agent host="192.168.10.101" port="8800"/>
<RFC5424Layout enterpriseNumber="18060" includeMDC="true" appName="MyApp"/>
<Property name="keyProvider">MySecretProvider</Property>
</Flume>
</Appenders>
<Loggers>
<Root level="error">
<AppenderRef ref="eventLogger"/>
</Root>
</Loggers>
</Configuration>
Интересные чтения
Это не отвечает на ваш вопрос напрямую, но также довольно интересен: Создание зашифрованного файла журнала
Ответ 3
Я думаю, вы смотрите что-то вроде этого. Хотя я не буду предлагать его использовать. Если вы отправляете ключ с кодом, есть много декомпиляторов для декомпиляции файлов jar/class и получения "ключа". В противном случае вы должны использовать PKI и все..? Я не рассматриваю этот вариант здесь.
расширить класс RollingFileAppender
public class EncryptedRollingFileAppender extends RollingFileAppender
так как класс RollingFileAppender не является окончательным, поэтому вы можете сделать.
перезаписать метод
public synchronized void setFile(String fileName, boolean append,
boolean bufferedIO, int bufferSize) throws IOException
что-то вроде этого.
LogLog.debug("setFile called: " + fileName + ", " + append);
// It does not make sense to have immediate flush and bufferedIO.
if (bufferedIO) {
setImmediateFlush(false);
}
reset();
OutputStream ostream = null;
try {
//
// attempt to create file
//
// ostream = new FileOutputStream(fileName, append);
ostream = this.createEncryptedOutputStream(fileName, append);
} catch (FileNotFoundException ex) {
//
// if parent directory does not exist then
// attempt to create it and try to create file
// see bug 9150
//
String parentName = new File(fileName).getParent();
if (parentName != null) {
File parentDir = new File(parentName);
if (!parentDir.exists() && parentDir.mkdirs()) {
// ostream = new FileOutputStream(fileName, append);
try {
ostream = this.createEncryptedOutputStream(fileName, append);
} catch (Exception e) {
e.printStackTrace();
}
} else {
throw ex;
}
} else {
throw ex;
}
} catch (Exception e) {
throw new FileNotFoundException();
}
Writer fw = createWriter(ostream);
if (bufferedIO) {
fw = new BufferedWriter(fw, bufferSize);
}
this.setQWForFiles(fw);
this.fileName = fileName;
this.fileAppend = append;
this.bufferedIO = bufferedIO;
this.bufferSize = bufferSize;
writeHeader();
LogLog.debug("setFile ended");
if (append) {
File f = new File(fileName);
((CountingQuietWriter) qw).setCount(f.length());
}
большая часть кода копируется из базового класса.
закрытый метод шифрования должен выглядеть примерно так.
private OutputStream createEncryptedOutputStream(String filename, boolean append) throws FileNotFoundException,
NoSuchAlgorithmException,
NoSuchPaddingException,
InvalidKeyException,
InvalidAlgorithmParameterException {
CipherOutputStream cstream = null;
try {
byte[] keyBytes = "1234123412341234".getBytes(); //example
final byte[] ivBytes = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }; //example
final SecretKey key = new SecretKeySpec(keyBytes, "AES");
final IvParameterSpec IV = new IvParameterSpec(ivBytes);
final Cipher cipher = Cipher.getInstance("AES/CFB8/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key, IV);
cstream = new CipherOutputStream(new FileOutputStream(filename, append), cipher);
} catch (FileNotFoundException e) {
throw e;
}
return (cstream);
}
Вы не потеряете возможности RollingFileAppender. Вы можете сделать подобное кодирование и других приложений.
Отказ от ответственности: - Пожалуйста, не используйте эти клавиши @production, если вы используете его. Опять же, вы отправляете ключи со своей банкой. Умный хакер может легко взломать вещи. Модифицировать код, который нужно выполнить, поскольку он "регистрирует". Я не тестировал этот код.