Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 63 additions & 7 deletions webmention.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,39 @@ public static function query_var($vars) {
return $vars;
}

/**
* generate a valid expire code.
* Three possible values are valid at any one time, ticks: 0, 1, or 2
*
* @return array
*/
public static function expire_code( $tick = 0 ) {
$action = 'web mention endpoint';
$time_format = 'Y-m-d a';
$time_block = 12 * HOUR_IN_SECONDS;
$tick = abs( intval( $tick ) );
if ( 3 < $tick ) {
// something wrong, tick too high/
// use default
$tick = 0;
}
$expire_code = date( $time_format, time() - ( $tick * $time_block ) );
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason to not use wp_nonce_tick() instead of calculating the tick from scratch? Not sure what's the best – but perhaps add a comment with a reason for why you chose what you did would be good?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nonce_life is filterable in wp_tick & I wanted to ensure it was kept @ twelve hours.

Would like to know your thoughts on this @pfefferle @voxpelli

P


// always use logged out user code, endpoint may be looked up by a logged in user
// while the web mention comes from a logged out user (using curl or similar)
$uid = 0;

// as above, always use the lgoged out token.
$token = '';

// custom hash used rather than standard nonce to prevent session data polluting the
// web mention endpoint. The endpoint needs to remain the same for all users.
$expire_code = wp_hash( $expire_code . '|' . $action . '|' . $uid . '|' . $token, 'nonce' );
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks very similar to the code in wp_create_nonce(): https://core.trac.wordpress.org/browser/tags/4.1/src/wp-includes/pluggable.php#L1758 But without the substr()? Any reason for that? Should perhaps add a comment on why it's not possible to use wp_create_nonce() (I guess the reason is the risk of getting session-dependent data which would stop the endpoint definition from being stateless and "curlable")

Ideally, long term, a patch to WordPress core that would allow a stateless wp_create_nonce() to be used out of the box would be great as well I think :) This is a great use case for such a patch.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah ok, here is the problem https://core.trac.wordpress.org/browser/tags/4.1/src/wp-includes/pluggable.php#L1750 the logged out code is only called if there is no session! That is indeed a problem!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment added, pushing shortly.


return $expire_code;
}


/**
* Parse the WebMention request and render the document
*
Expand All @@ -81,13 +114,34 @@ public static function parse_query($wp) {
if (!array_key_exists('webmention', $wp->query_vars)) {
return;
}

$content = file_get_contents('php://input');
parse_str($content);
else {
// check if the end point has expired
$valid_ticks = array( 0, -1, -2 );
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are numbers negative here? And why have an array of numbers combined with a foreach rather than just a regular $length = 3; for ($i = 0; $i < $length; $i++) {}?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My suggestion would be:

  • add a setting to enable/disable nonce support
  • using $nonce = wp_create_nonce( 'my-action_'.$post->ID ); so it is possible to use all other wp_nonce native features
  • using the filter $uid = apply_filters( 'nonce_user_logged_out', $uid, $action ); to return a "stateless" UID

What are you thinking?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the thinking, but the $token = wp_get_session_token(); from https://core.trac.wordpress.org/browser/tags/4.1/src/wp-includes/pluggable.php#L1755 makes it impossible as far as I can see to make the built in wp_create_nonce() stateless. 'nonce_user_logged_out' doesn't solve the stateless part – it was intended to increase statefulness rather than decreasing it so as long as there's a session with a token it will be impossible to make wp_create_nonce() stateless even with it.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WP native uses negative numbers when generating the tick, I wanted to match.

Per voxpelli on session data.


$supplied_code = get_query_var( 'webmention' );
$is_valid = false;

foreach ( $valid_ticks as $tick ) {
if ( hash_equals( WebMentionPlugin::expire_code( $tick ), $supplied_code ) ) {
$is_valid = true;
break;
}
}
}

// plain text header
header('Content-Type: text/plain; charset=' . get_option('blog_charset'));

// fail if invalide endpoint
if ( false == $is_valid ) {
status_header(403);
echo "invalid endpoint";
exit;
}

$content = file_get_contents('php://input');
parse_str($content);

// check if source url is transmitted
if (!isset($source)) {
status_header(400);
Expand Down Expand Up @@ -556,17 +610,19 @@ public static function discover_endpoint($url) {
*/
public static function html_header() {
// backwards compatibility with v0.1
echo '<link rel="http://webmention.org/" href="'.site_url("?webmention=endpoint").'" />'."\n";
echo '<link rel="webmention" href="'.site_url("?webmention=endpoint").'" />'."\n";
$endpoint_code = WebMentionPlugin::expire_code();
echo '<link rel="http://webmention.org/" href="'.site_url("?webmention=" . $endpoint_code ).'" />'."\n";
echo '<link rel="webmention" href="'.site_url("?webmention=" . $endpoint_code ).'" />'."\n";
}

/**
* The WebMention autodicovery http-header
*/
public static function http_header() {
// backwards compatibility with v0.1
header('Link: <'.site_url("?webmention=endpoint").'>; rel="http://webmention.org/"', false);
header('Link: <'.site_url("?webmention=endpoint").'>; rel="webmention"', false);
$endpoint_code = WebMentionPlugin::expire_code();
header('Link: <'.site_url("?webmention=" . $endpoint_code).'>; rel="http://webmention.org/"', false);
header('Link: <'.site_url("?webmention=" . $endpoint_code).'>; rel="webmention"', false);
}

/**
Expand Down