12

I require access logs enabled, but for compliance reasons, cannot log a sensitive GET request parameter's data in the access logs. While I know, I could parse the logs (after-the-fact) and sanitize them, this is not an acceptable solution -- because for compliance reasons logs can't be tampered with.

http://www.example.com/resource?param1=123&sensitive_param=sensitive_data

How can I prevent the "sensitive_data" parameter value from being written to the logs? Here were some ideas:

  • Send in POST request -- is not an option with JSONP.
  • Use a new location rule for "resource" and set an access log to use a log_format the uses a different format (ie does not use $remote_addr). See this for reference: http://nginx.org/en/docs/http/ngx_http_log_module.html
  • Log a $sanitized_remote_addr, and set it (somehow parse the $remote_addr or something else?) before it makes it to the log. We're not sure if this is easy to accomplish.

How should this be done?

Domino
  • 361
  • 1
  • 2
  • 7
  • 1
    You might also want to consider [mod_security for nginx](http://www.modsecurity.org/projects/modsecurity/nginx/) and have a look at the [naxsi project](https://github.com/nbs-system/naxsi) – Burhan Khalid Oct 17 '13 at 16:37

2 Answers2

8

Previous answer will not work since log_format module can only be used at http level config.

For fix of this, we can remove the log_format configuration from location directive and keep it as it in http level config.

http {

    log_format filter '$remote_addr - $remote_user [$time_local] '
        '"$temp" $status $body_bytes_sent "$http_referer" "$http_user_agent"';

    # Other Configs
}

log_format directive can have variables defined later in our location directive block.

So final config will look like:

http {
    
    log_format filter '$remote_addr - $remote_user [$time_local] '
        '"$temp" $status $body_bytes_sent "$http_referer" "$http_user_agent"';
    
    # Other Configs

    server {
        #Server Configs
        location / {
            set $temp $request;
            if ($temp ~ (.*)password=[^&]*(.*)) { 
                set $temp $1password=****$2;
            }

            access_log /opt/current/log/nginx_access.log filter;
        }
    }
}

Note that error logs might still log this information. Use error_logs off; in your location block to disable this (assuming you can log those errors elsewhere in your application)

Shashank Agrawal
  • 25,161
  • 11
  • 89
  • 121
  • Would anyone happen to know how I could apply this to an AWS Elastic Beanstalk environment? I did not do any sort of manual set up for nginx, it was all the default configurations from launching the application environment. I know I have a `./ebextensions/nginx.config` file, but it is virtually empty. Just a setting to update the max file size, but the format is not the same as what is shown here at all. – Jake T. Aug 14 '18 at 22:42
  • I've figured out *what* I need to do for AWS EB. Working on how to do it. The config I mentioned allows me to create files and run "container commands" on the server when it is finished setting up but before it's deployed and connected. The Server directive is also tucked away in an auto generated file that gets imported inside the http directive in the main nginx.config. So, I must either write a bash script or use unix commands to modify the auto generated file to include the log_format and locations directive then reload the nginx server through container commands. – Jake T. Aug 15 '18 at 14:23
  • I had an issue where the token was the only parameter, and the regex in this answer also matched the HTTP version at the end of the request line. The fix for me was to change the regex to 'if ($temp ~ (.*)password=[^&\s]*(.*))'. This adds \s which matches space characters as the end of the sensitive portion. – Peter Jul 03 '22 at 00:16
1

The solution I found so far is here. In short:

location /any_sensitive... {
    # Strip password in access.log
    set $temp $request;
    if ($temp ~ (.*)password=[^&]*(.*)) {
        set $temp $1password=****$2;
    }

    log_format filter '$remote_addr - $remote_user [$time_local] '
        '"$temp" $status $body_bytes_sent "$http_referer" "$http_user_agent"';

    access_log logs/access.log filter;
}

Maybe this used to work at some point, now it says:

nginx: [emerg] unknown "temp" variable

or

nginx: [warn] the "log_format" directive may be used only on "http" level in ...
Doncho Gunchev
  • 2,159
  • 15
  • 21
  • Maybe if it's just one/two log files you can use pipes and sanitize the output with a filter listening on these. The filter can then write the real log files. – Doncho Gunchev Nov 30 '13 at 00:15
  • 3
    No, you have it right, as long as you have the variable set somewhere, nginx shouldn't complain about an unknown variable. I adapted your answer into a working solution, described here: https://github.com/sunlightlabs/congress/tree/master/config/nginx#suppressing-user-latitudelongitude-in-our-logs – Eric Mill Jan 21 '14 at 18:51