Categories
Personal

When The Dust Settles: Focusing On Positive Changes

If you’re anything like me, you’re probably tired, and a bit scared about the COVID-19 pandemic. The situation evolves quickly and dramatically, but I think we can all agree that the writing is on the wall: either us or our loved ones will likely contract the virus (current estimates are 20% to 70% of the world population will get it). Most of us will be okay, but some will die. 

Confirmed cases keep growing exponentially. At the moment of this writing, there are 145,637 cases with 72,529 recoveries and 5,436 deaths (according to Worldometer), 91% are in Mild сondition. 

So, yeah, it’s exhausting to keep up with the coronavirus news. It’s taking a mental toll: even if you think you’re not at risk of becoming “the Severe case,” you have older and immunocompromised people in your life. Unless you’re a psychopath, you’re anxious about them right now.

I found myself in a feedback loop. I scroll through the Twitter feed, see bad news, get anxious about it, go do something else. Still, my mind is preoccupied, so I go back to Twitter to read more scary news stories. Ad infinitum.

My usual coping mechanism is humor: crack a couple of jokes, make some memes, feel a bit better. This time it doesn’t work, it’s just too much. So I decided to try something else, specifically, a thought exercise. I asked myself: “what positive changes we, as a species, may create in the aftermath of the pandemic?” 

Change Is In The Air

Historically, tough times drastically change the way of life. Be it a world war or a deadly pandemic, humanity reacts, responds, and adapts. I feel like we’re in the moment that can cause profound change.

The following is a speculation based on the feelings and thoughts I have. It’s not scientific or backed by any data, but I really wish some of it becomes a reality.

Remote Work

Out of the few positive changes I came up with so far, this one seems the most realistic. As the pandemic has already shown, going to the office is absolutely not required for many modern-day jobs. All of a sudden, huge corporations don’t have issues with their employees working remotely. 

Remote work will enter the mainstream, it no longer will be a gimmick reserved for tech companies and freelancers.

Remote Learning

Just as with the remote work, people will realize that going to a school is not a hard requirement. It’s already a thing, but it’ll become more pronounced.

Carbon Footprint

This one stems from the previous two (and the travel industry having near-death experience). As more people work and learn from home, they will use their cars less. This will help with traffic congestion in the bigger cities and give our planet some much-needed breathing space. 

Healthcare And Worker Rights (US Specific)

My hope is that the currently unfolding situation will put inadequacies of the current system in a very intense spotlight. Thus, shifting the public opinion from “universal healthcare is communism” to “I can’t believe we were dealing with this for-profit insurance nonsense for such a long time.”

Another positive might be the guaranteed paid medical leave for every employee. No one has to go to work when they’re sick. It’s a straightforward concept, and the lack of grasp on it in the US is dumbfounding.

Personal Hygiene

For the last couple of months, I’ve been becoming increasingly aware of people coughing around me, and I watched them closely. Many don’t cover their mouth at all, and some do it wrong. 

I must admit, until recently, I have never washed my hands properly. I did it, but half-assed the whole thing: missed spots and rarely (if ever) did it for more than 10 seconds. If my parents ever taught me how to do it right, I totally forgot. 

I hope we collectively learn how to wash our hands thoroughly and cover our coughs or sneezes. This will help prevent or mitigate local/global outbreaks of various diseases.

Food Waste

My granda used to discipline me each time when I was playing with food. As a child, I didn’t understand it. I found it quite entertaining to kick a bread bun or throw an apple. She’d become furious and said something like, “You have to respect the food! Bread is not a toy!” Those memories came back to me as I was watching people panic-shopping on the Internet and, two weeks later, in real life. 

We toss an astonishing amount of food in the garbage. I hope this crisis will make us more mindful of what we eat and what we throw out.

Conclusion

Only time will tell how much of the above will become a reality and how much will stay wishful thinking (or a pipe dream, depending on one’s perspective).

I know one thing. The harder we’ll get hit in the following weeks, months, and maybe years (see the Spanish Flu), the likelier the changes will stick. If one cuts a finger with a knife, they might learn how to properly hold it; losing a finger will have a lasting effect for the rest of their life. 

But we’re going to be alright.

Categories
Code

How to lock down or disable WordPress REST API Endpoints.

Script kiddies hate this one little trick!

WordPress REST API is a powerful tool. However, with great power comes great responsibility. Such as, for example, to not accidentally disclose information about your users. Yet, this is exactly what happens when you hit /wp/v2/users endpoint. You can view users or you can view specific user information by adding the id argument. E.g. /wp/v2/users/31337

I can respect WordPress’ stance (as I understand it). “It’s the Web, people will access your site, WordPress is a blogging software”. But I don’t have to agree with it. IMO default permissions are too open for many cases.

There are several ways of restricting access to REST endpoints. I’m going to cover some of them.

TL;DR

Note on cookie-based auth security.

REST API requires requests to be signed with a nonce to mitigate the possibility of a CSRF attack: if someone steals your user’s cookies, they won’t be able to pose as that user without that nonce. You can use capability checks (e.g., current_user_can in all of the approaches listed below, but, in case you see unintended results in your testing, the chances are that you may have forgotten to pass the correct nonce to the request. Be sure to check the REST API cookbook for more details on cookie-based auth.

The hardcore way: rest_endpoints

You may have seen advice to use the rest_endpoints filter. I think it’s not a good option for the majority of the cases. Here’s an example:

// Nuclear option: disable all endpoints
add_filter( 'rest_endpoints', function( $endpoints ) {
	// No REST for the restless
	$endpoints = [];
	return $endpoints;
} );

There, easy, right? All endpoints are gone, and we’re entirely “safe.” The only problem? Now you might have a broken site. Both Core and User-land code (plugins) use REST API extensively, so filtering out endpoints like that can break things. Depending on what was filtered, it can blow up either spectacularly (try the above with Gutenberg on) or subtly.

My issue with that approach is that the filter is unwieldy because it’s a huge array with routes as keys. From the docs:

(array) The available endpoints. An array of matching regex patterns, each mapped to an array of callbacks for the endpoint. These take the format '/path/regex' => array( $callback, $bitmask ) or '/path/regex' => array( array( $callback, $bitmask ).

https://developer.wordpress.org/reference/hooks/rest_endpoints/#parameters

Now you either have to iterate over the array to figure out which endpoints to remove or call unset on specific ones:

// Remove a specific or several endpoints
add_filter( 'rest_endpoints', function( $endpoints ) {
	// Unset a specific endpoint
	unset( $endpoints['/wp/v2/users/(?P<id>[\\d]+)'] );
	// or iterate and figure out which ones to remove
	foreach( $endpoints as $route => $handler ) {
		// somehow decide this endpoint needs to be removed
	}

	return $endpoints;
} );

If you want to use a nuclear option to “disable” REST API, this is your jam. Removing endpoints using this approach results in a 404 error:

{
  "code": "rest_no_route",
  "message": "No route was found matching the URL and request method",
  "data": {
    "status": 404
  }
}

More balanced approach: rest_authentication_errors

I think this method, while not without drawbacks, is preferable.

Filters REST authentication errors.

WP_Error instance can be returned if an error occurs, and this should match the format used by API methods internally (that is, the status data should be used). A callback can return true to indicate that the authentication method was used, and it succeeded.

https://developer.wordpress.org/reference/hooks/rest_authentication_errors/

NB: You have to be mindful about the return value in this filter, returning true effectively disables the nonce check in the Core’s rest_cookie_check_errors, thus leaving your site vulnerable.

But there are situations where you wouldn’t care as much about the nonce check. Suppose you want to bump the default permissions for all endpoints to be at least edit_posts except for pages endpoint:

// More balanced approach
add_filter( 'rest_authentication_errors', function( $maybe_error ) {
	// Only allow authors and up, except for the pages endpoint
	if ( ! ( current_user_can( 'edit_posts' ) || false !== stripos( $_SERVER['REQUEST_URI'], 'wp/v2/pages' ) ) ) {
		return new \WP_Error(
			'rest_auth_required',
			'No REST for the restless!',
			['status' => 401 ] 
		);
	}

	// Pass through
	return $maybe_error;
} );

Let’s unpack what’s happening here: we’re checking the permissions and the specific path. If a user request is legit, we achieved our goal. If it’s an attacker that stole cookies, somehow they become “unauthenticated” in rest_cookie_check_errors. So the worst-case scenario here is a fallback to default permissions. To be on the safer side, though, it makes sense to modify the above code to also include the nonce check.

This method also has a drawback: no context is available about the request itself. It’s why the example above relies on $_SERVER to detect the need for intervention.

But wait, there’s more!

Are you still with me? Because there are, indeed, more places where we can intercept the result and block the request.

rest_pre_dispatch

Allow hijacking the request before dispatching by returning a non-empty. The returned value will be used to serve the request instead.

https://developer.wordpress.org/reference/hooks/rest_pre_dispatch/
// More context
add_filter( 'rest_pre_dispatch', function( $res, \WP_REST_Server $wp_rest_server, \WP_REST_Request $request ) {
	if ( this_needs_to_be_restricted() )
	return new \WP_Error(
		'rest_auth_required',
		'No REST for the restless!',
		[ 'status' => 401 ]
	);

	return $res;
}, 10, 3 );

What’s nice about this filter is that it runs very early during routing & dispatch, and you have access to the WP_REST_Server instance and to the request itself. This allows for finer granularity, as well as reducing wasted CPU cycles.

Logic-wise its description tells us that it’s for serving something as a substitute for the routed and processed request, something like serving a cached response for a very slow endpoint comes to mind.

So while it’s totally fine to return a WP_Error here, to me, it sounds a bit dirty.

rest_request_before_callbacks

Allows plugins to perform additional validation after a request is initialized and matched to a registered route, but before it is executed.

Note that this filter will not be called for requests that fail to authenticate or match to a registered route.

https://developer.wordpress.org/reference/hooks/rest_request_before_callbacks/
add_filter( 'rest_request_before_callbacks', function( $response, $handler, $request ) {
	if ( endpoint_needs_to_be_blocked() )
		return new \WP_Error(
			'rest_auth_required',
			'No REST for the restless!',
			[ 'status' => 401 ]
		);

	return $response;
}, 10, 3 );

This filter happens after all request parameters were validated and sanitized. If validation fails, this filter won’t be called.

rest_dispatch_request

Filters the REST dispatch request result.

https://developer.wordpress.org/reference/hooks/rest_dispatch_request/

This filter fires right before the request is dispatched (meaning it’s handler function/method is called).

This filter doesn’t fire if the route’s permission callback returned WP_Error.

add_filter( 'rest_dispatch_request', function( $res, \WP_REST_Request $request, string $route, $handler ) {
	if ( ! current_user_can( 'edit_others_posts' ) )
		return new \WP_Error(
			'rest_auth_required',
			'No REST for the restless!',
			[ 'status' => 401 ] 
		);

	return $res;
}, 10, 4 );

rest_request_after_callbacks

Filters the response immediately after executing any REST API callbacks.

https://developer.wordpress.org/reference/hooks/rest_request_after_callbacks/
add_filter( 'rest_request_after_callbacks', function( $response, $handler, $request ) { 
// you know the drill at this point 
return $response;
} );

Filtering at this point is wasteful, you have already generated the response, why block it now?

Conclusion

Hopefully, you’ve learned something new after reading this post (I know I did while writing it).

Treat yourself to this timeless classic.
Categories
Personal

Building Felice Donna Sleepwear: Part 1

One day my wife Maria’s friend Veronika was shopping for a silk robe or a silk pajama. Much to her chagrin, products she looked at were either costly or poorly made or combination of both. You’d think that the price would correlate with quality, but it’s definitely not the case for this category of products. She called Maria and told all about it. Maria, in turn, performed her own independent research, which ultimately confirmed Veronika’s findings.

So they had an idea: what if silk sleepwear products could be both affordable and made of quality fabric, what if these products were both comfortable and good looking? Thus, Felice Donna Sleepwear was born. Felice Donna [felitʃe dˈɔnna] means “Happy Woman” from Italian.

The work has begun in mid-2018. It was a lengthy process: from the inception to sourcing the Italian fabric and French lace to tinkering with numerous variants of patterns for prototypes to finally nailing it down and sewing the finished product.

None of it was easy, and I applaud Maria, who was juggling her primary job, pregnancy, and the new business. And I wanted to support her beyond encouraging words and an occasional idea or commentary.

Seeing how I don’t know much about the fashion industry in general, and even less so about silk sleepwear, I could only offer one thing. I promised to build the brand’s website when the time comes.

After all, I’ve been a web developer for more than half of my life, and it seemed only logical. Sure they could use one of the shopping platforms, but that would mean they’d lose a chunk of the profit and be subject to a vendor lockdown. Sure they could hire somebody to build a website, but that would mean extra expenses on a razor-thin budget with a hard-to-predict outcome. All my career, I’ve been developing someone else’s products, so it’s time to create a product for myself (well, not for me; for her, but she’s my better half, so that checks out). And with that thought, I’ve embarked on a fascinating journey.

Admittedly, there were challenges I haven’t anticipated. Some of them were technical. Others laid purely in social dynamics because, of course, having your immediate family as your client might get tricky for many reasons. I’m going to spare you from the “soft skills” part and instead focus on the technical aspects.

In the next part of this series, I’ll talk about some architectural and design decisions I had to make and the reasons behind them.

Categories
Personal

Hot Take: Problems and Solutions

Coming up with complex solutions for complex problems is easy.
Coming up with simple solutions for complex problems is hard.

What I mean by this is, generally speaking, a problem can be solved by breaking it down into smaller problems and addressing those. Once the individual pieces are taken care of, one can tie those together neatly, thus addressing the original issue.

But that’s in theory. In practice, each new small piece might create its own set of problems. It’ll likely introduce new inflection points at which things might go wrong. Little did you know, instead of one big problem, you now have a set of small ones, each of them potentially leading to new ones.

So yeah, coming up with simple solutions is hard, but, at the end of the day, you’ll do yourself a favor by not having to deal with the complexity you may have created otherwise.

Categories
Personal

Welcome to the N-th Iteration of My Blog

Most likely, it’ll end up abandoned just as previous iterations were. But I’ll give it a shot. I hope it’s going to consist of a healthy mix of personal rants and web development tips and tricks.