简体   繁体   English

保护/加密 log4j 文件

[英]Secure / Encrypt log4j files

I have a problem ;我有个问题 ; security requirement i have java swing app that have logging files generated with log4j for support issues in case of tracking a bug.安全要求 我有 java swing 应用程序,其中包含使用log4j生成的日志文件,用于跟踪错误时的支持问题。

I have to ecrypt /cypher/secure the files so the client cant open them and see them (at least not as human readable way) and at the same time when support tech team take these files they will know how to read ( decrypt ) them .我必须ecrypt /cypher/secure 文件,以便客户端无法打开它们并查看它们(至少不是人类可读的方式),同时当支持技术团队获取这些文件时,他们将知道如何读取(解密)它们.

I did a lot of searches and i tried my best option i found which is build custom appender by extending SkeletonAppender .我进行了大量搜索,并尝试了我发现的最佳选择,即通过扩展SkeletonAppender来构建自定义 appender。

Now know that i have log4j working great as below configuration, but i created new class to encrypt it but i cant get it work even with simple setup it dose not create the file , so i can continue in the ecnryption part.现在知道我的log4j在下面的配置中工作得很好,但是我创建了新的类来加密它,但即使使用简单的设置我也无法让它工作,它不会创建文件,所以我可以继续加密部分。

Any help , links are good.任何帮助,链接都很好。

Working...version工作...版本

<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>

Not Working...version不工作...版本

   <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>

The simple class test简单的课堂测试

http://www.javaworld.com/article/2075817/core-java/customized-appender-extending-org-apache-log4j-fileappender.html 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's 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;
    }

}

Then i plan to implement the code in Cipher OutputStream然后我打算在Cipher OutputStream 中实现代码

A possible workaround to the problem is to write the logs to an embedded database that supports encryption, eg H2 natively supports encryption and SQLite has open source encryption extensions - this way you can just use the JDBCAppender and let the database take care of encryption without having to worry about a custom appender.该问题的一个可能解决方法是将日志写入支持加密的嵌入式数据库中,例如H2本身支持加密并且SQLite具有开源加密扩展 - 这样您就可以只使用JDBCAppender并让数据库处理加密而无需担心自定义附加程序。


From this question , SQLite config would look something like这个问题来看,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>

where your log table looks like你的日志表看起来像

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
);

Documentation on the JDBCAppender can be found here可以在此处找到有关JDBCAppender文档


There's an official encryption extension for SQLite as well as at least one third party open source extension; SQLite 有一个官方加密扩展以及至少一个第三方开源扩展; I've never had to encrypt SQLite, but if I had to do so then I'd go with the official extension unless I ran into problems with it.我从来没有加密 SQLite,但如果我必须这样做,那么我会使用官方扩展,除非我遇到问题。


If you're running this on the client, then ideally you'll be able to have the program phone home at boot time to get the database encryption key so that the key never exists on the client's disk drive (ignoring the possibility that it goes to the swap file) - the client could still use a debugger or whatever to try to get the key out of memory, but presumably they're not interested enough in decrypting the logs to go to that amount of trouble.如果您在客户端上运行它,那么理想情况下,您将能够在启动时让程序回拨以获取数据库加密密钥,以便该密钥永远不会存在于客户端的磁盘驱动器上(忽略它的可能性)到交换文件) - 客户端仍然可以使用调试器或其他任何东西来尝试从内存中获取密钥,但大概他们对解密日志没有足够的兴趣来解决那么多麻烦。 If you've got to store the key on the client side then you can at a minimum obfuscate it by hashing it several times before using it, eg hard-code the base_key in the program, then at boot time you create actual_key by running base_key through SHA512 (or whatever) several times;如果您必须将密钥存储在客户端,那么您至少可以通过在使用它之前对其进行多次散列来混淆它,例如在程序中对 base_key 进行硬编码,然后在启动时通过运行 base_key 创建 actual_key通过SHA512 (或其他)多次; the client could still figure out what you're doing by using a debugger, but again they hopefully won't want to go to the trouble.客户仍然可以通过使用调试器来弄清楚你在做什么,但他们希望他们不想再惹麻烦。

Option 1: Use a Custom SocketAppender选项 1:使用自定义 SocketAppender

As an alternative to Zim-Zam's answer about using a JDBC-capable appender (remember to enable secure transport as well, by the way, if you go down this route), you could also look into using a SocketAppender and roll out your own encryption method.作为 Zim-Zam 关于使用支持 JDBC 的 appender 的回答的替代方案(顺便说一下,如果您沿着这条路线走,请记住也启用安全传输),您还可以考虑使用SocketAppender并推出您自己的加密方法。

Option 2: Use Flume and a FlumeAppender选项 2:使用 Flume 和 FlumeAppender

Refer to the log4j documentation on appenders and have a look at using a FlumeAppender , which supports event encryption:请参阅有关 appenderlog4j 文档并查看使用FlumeAppender ,它支持事件加密:

A sample FlumeAppender configuration that is configured with a primary and a secondary agent, compresses the body, formats the body using the RFC5424Layout, and persists encrypted events to disk.一个示例 FlumeAppender 配置,它配置了一个主要和一个辅助代理,压缩主体,使用 RFC5424Layout 格式化主体,并将加密的事件持久化到磁盘。 This sample "compresses the body, formats the body using the RFC5424Layout, and persists encrypted events to disk:"此示例“压缩正文,使用 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>

Interesting Reads有趣的读物

This does not answer your question directly, but is fairly interesting as well: Creating an encrypted log file这不会直接回答您的问题,但也相当有趣: 创建加密的日志文件

I think you are looking something like this.我想你看起来像这样。 Though I won't suggest to use it.虽然我不会建议使用它。 If you are shipping key with the code, there are plenty de-compilers to decompile jar/class files and get "key".如果您随代码一起发送密钥,则有很多反编译器可以反编译 jar/class 文件并获取“密钥”。 Otherwise you should use PKI and all..?否则你应该使用PKI和所有..? I haven't look into that option here.我还没有在这里研究这个选项。

extend RollingFileAppender class扩展 RollingFileAppender 类

public class EncryptedRollingFileAppender extends RollingFileAppender

since RollingFileAppender class not final class so you can do.因为 RollingFileAppender 类不是最终类,所以你可以这样做。

overwrite method覆盖方法

public synchronized void setFile(String fileName, boolean append,
        boolean bufferedIO, int bufferSize) throws IOException

something like this.像这样的东西。

        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());
    }

most of the code is copied from base class.大部分代码是从基类复制的。

encryption private method should look something like this加密私有方法应该是这样的

    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);
}

You won't loose any capabilities of RollingFileAppender.您不会失去 RollingFileAppender 的任何功能。 You can do similar coding @ other appenders as well.您也可以在其他 appender 上进行类似的编码。

Disclaimer :- Please don't these keys @ production, if you are using it.免责声明:- 如果您正在使用它,请不要使用这些密钥@生产。 Again, you are shipping keys with your jar.再一次,你用你的罐子运送钥匙。 A smart hacker can hack things easily.Fine tune of code to be done since it is 'logging'.一个聪明的黑客可以很容易地破解。因为它是“记录”的,所以要对代码进行微调。 I haven't tested this code.我还没有测试过这段代码。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM