-
Notifications
You must be signed in to change notification settings - Fork 116
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve client IP resolution for Stream logs #1459
Conversation
if ( is_admin() && Alerts::POST_TYPE === $screen->post_type ) { | ||
return true; | ||
if ( is_admin() && function_exists( 'get_current_screen' ) ) { | ||
$screen = get_current_screen(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Account for calls to this method when get_current_screen()
is not available.
classes/class-log.php
Outdated
|
||
// Fallback to unsafe IP extracted from the request HTTP headers. | ||
if ( empty( $ip_address ) ) { | ||
$ip_address = $this->plugin->get_unsafe_client_ip_address(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Keep the previous behaviour for now. Consider removing this fallback in the future releases (as mentioned in the changelog).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was wondering why you opted to do two separate calls in here, as opposed to having the fallback be part of get_client_ip_address()
instead.
I thought the latter would have been easier for removing the fallback behavior at a later date. Also, it would allow get_unsafe_client_ip_address()
to be private instead so we don't create an unwanted public interface for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason is we want to check just the "reliable" IP address in some instances with no fallbacks (when showing the admin notice, for example). This also clearly identifies the places where the use of get_unsafe_client_ip_address()
should be wrapped into a conditional eventually.
@@ -174,12 +167,6 @@ public function is_record_excluded( $connector, $context, $action, $user = null, | |||
$user = wp_get_current_user(); | |||
} | |||
|
|||
if ( is_null( $ip ) ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We expect all of the data arriving here to be sanitized and checked already.
@@ -138,6 +145,9 @@ public function __construct() { | |||
// Load logger class. | |||
$this->log = apply_filters( 'wp_stream_log_handler', new Log( $this ) ); | |||
|
|||
// Set the IP address for the current request. | |||
$this->client_ip_address = wp_stream_filter_input( INPUT_SERVER, 'REMOTE_ADDR', FILTER_VALIDATE_IP ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make the main plugin class responsible for knowing properties of the current request. Expose the knowledge via a helper method.
classes/class-plugin.php
Outdated
'HTTP_X_FORWARDED_FOR', | ||
'HTTP_FORWARDED_FOR', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RFC 7239 proposes a new standard 'HTTP_FORWARDED'
: https://datatracker.ietf.org/doc/html/rfc7239
It is still a comma-separated list, but contains prefixes in front of the IP address and other directives, like so:
Forwarded: for=192.0.2.43,for=198.51.100.17;by=203.0.113.60;proto=http;host=example.com
Not sure it's worthwhile adding support for that upcoming standard as of yet, though, as it doesn't seem to be widely supported => https://c960657.github.io/forwarded.html
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, let's leave this to a separate pull request.
classes/class-plugin.php
Outdated
$header_client_ip = $header_client_ips[0]; | ||
} | ||
|
||
$client_ip = wp_stream_filter_var( trim( $header_client_ip ), FILTER_VALIDATE_IP ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note for future reference:
The known drawbacks of FILTER_VALIDATE_IP
can be ignored here, as it goes through a special polyfill and ends up being implemented via WP_Http::is_ip_address()
instead of via PHP's internal filter_var
.
readme.txt
Outdated
`add_filter( | ||
'wp_stream_client_ip_address', | ||
function( $client_ip ) { | ||
// Trust the X-Forwarded-For header. | ||
if ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) { | ||
return $_SERVER['HTTP_X_FORWARDED_FOR']; | ||
} | ||
|
||
return $client_ip; | ||
} | ||
);` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note 100% sure about the readme.txt
markdown compatibility, but shouldn't this section use triple backtick notation instead?
classes/class-plugin.php
Outdated
/** | ||
* IP address for the current request to be associated with the log entry. | ||
* | ||
* @var null|false|string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the intention behind having three different types here?
From what I can gather in the PR here, we have:
- valid IP address gets returned as a
string
- invalid IP address gets returned as a
false
- no IP address gets returned as
null
As mixed types always offer extra surface for bugs, just covering the bases here:
- Are we distinguishing between these three types in other parts of the code? I.e., does an invalid IP address trigger anything differently than a missing IP address?
- Are these three types properly stored in the database?
- Are these three types properly rendered in all UI dialogs and reports?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is just mirroring the output types of filter_input()
. This is the canonical state of the REMOTE_ADDR
value and the methods can use these values as needed.
classes/class-log.php
Outdated
$ip_address = $this->plugin->get_client_ip_address(); | ||
|
||
// Fallback to unsafe IP extracted from the request HTTP headers. | ||
if ( empty( $ip_address ) ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The empty( $ip_address )
will be true for both the case where no IP address was provided as well for when an invalid IP address is provided. Is that the intended behavior?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is a bit tricky here -- we're attempting to preserve the HTTP_X_FORWARDED_FOR
usage from before but now it will only happen if REMOTE_ADDR
is not configured which might not be the case making this an equally breaking change.
I'm thinking we should probably do a breaking change release and make it consistent immediately to avoid confusion.
classes/class-log.php
Outdated
|
||
// Fallback to unsafe IP extracted from the request HTTP headers. | ||
if ( empty( $ip_address ) ) { | ||
$ip_address = $this->plugin->get_unsafe_client_ip_address(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was wondering why you opted to do two separate calls in here, as opposed to having the fallback be part of get_client_ip_address()
instead.
I thought the latter would have been easier for removing the fallback behavior at a later date. Also, it would allow get_unsafe_client_ip_address()
to be private instead so we don't create an unwanted public interface for this.
classes/class-plugin.php
Outdated
* | ||
* @return string|null | ||
*/ | ||
public function get_unsafe_client_ip_address() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As mentioned elsewhere, if this fallback behavior would be called immediately while initially setting $this->client_ip_address
, we wouldn't need to make it public
and not have it part of the public interface.
@schlessera I feel like attempting the fallback behaviour might not be in our best interest here because it would actually be different from the previous behaviour. Let's make it a breaking change instead (does this warrant a 4.0.0 release?) and encourage everyone to use the newly introduced filter for adjusting the client IP address on environments where either |
My 2C.
|
178177e
to
b42c4c0
Compare
Co-authored-by: Alain Schlesser <[email protected]>
Co-authored-by: Alain Schlesser <[email protected]>
Co-authored-by: Alain Schlesser <[email protected]>
b42c4c0
to
9bcc490
Compare
Fixes #1456.
$_SERVER['REMOTE_ADDR']
as the canonical source for the request origin IP.$_SERVER['REMOTE_ADDR']
is not specified.Fallback to$_SERVER['HTTP_X_FORWARDED_FOR']
only if$_SERVER['REMOTE_ADDR']
not present.Checklist
contributing.md
).Release Changelog
$_SERVER['REMOTE_ADDR']
for the source of user IP and fallback to$_SERVER['HTTP_X_FORWARDED_FOR']
only in caseREMOTE_ADDR
is not available. Introduce awp_stream_client_ip_address
filter to adjust the trusted client IP.