Category Archives: Uncategorized

Let’s talk about Radio Clocks

After arriving in Berlin in January of 2019, I treated myself to a Casio F-91W. It was my first (non-essential) purchase in Germany, and for less than 10 Euros, it was a steal. Plus, a colleague of mine wore it and it looked cool. Easy decision!

Little did I know that this watch had a cult following on the internet. People collected all versions and variants of it and wore it with pride. I got pulled into the hype surrounding the F-91W and eventually more of the Casio retro digital watches.

It was an easy “hobby” to pursue given it is basically just buying more stuff. Nevertheless, it isn’t all that bad given how cheap these retro watches are. It is all relative of course, but given I’m also into photography, acquiring Casio watches is easier on my wallet.

Today, I own a couple more and I’m low-key proud of my collection.

Learning about Radio Clocks

Like with anything I get obsessed with, I spent a lot of time reading and watching YouTube videos about Casios. At some point I stumbled upon the Casio Wave-Ceptor series of watches. It struck me as something different: This fairly low tech wrist watch could set and correct its time automatically!

“But wait, it isn’t a smart watch” I thought. How does it do that?

That sent me down the rabbit hole which I’m still on my way down right now. Turns out, the watch can receive signals transmitted by a time signal transmitter and correct its local time.

In Germany, there’s Mainflingen longwave transmitter transmitting DCF77 time signal. The carrier signal has a 77.5 kHz frequency and the time signal is generated from the local atomic clock which syncs with the main atomic clock in Braunschweig. It is operated by the Physikalisch-Technische Bundesanstalt which also hosts four public NTP servers:


ptbtime1.ptb.de

ptbtime2.ptb.de

ptbtime3.ptb.de

ptbtime4.ptb.de

Querying the time using the commandline utility sntp and watching the network activity in Wireshark gives us some interesting data.

Wireshark

What’s next?

I’ve ordered a cheap radio receiver to play around with the radio time signal. On the watch side, I’ll look for cheap used radio Casio watches on the local Ebay here and try to acquire. I hope this new obsession ends up teaching me a thing or three about analog signals.

That is it for this side-quest post. Thank you for reading!

Letter To My 18 Year Old Self

I have a love-hate relationship with articles such as this. On one hand, the thought of going back in time and changing certain decisions is always fascinating, and documenting some of those thoughts into text that I can keep coming back is alluring. What is also appealing is to have this documented as a reference for my future self who’d probably (read: most likely) also want to write back to his 18 year old (or 27 year old self 🤷‍♂️, who knows).

But on the other hand, articles with titles such as this are so cliche that they don’t feel sincere and make me cringe. What’s even the point of publishing it? Don’t I believe that my current state defines the combined success or failure of all my past decisions and events in my life? If I’m happy with the state of affairs in my life right now, doesn’t that mean everything in the past kinda worked out in the end?

I don’t know. My current philosophy doesn’t allow for an article like this one, for it doesn’t make any sense to wish for the possibility of a different present when I have the certainty of my current present, which I believe I’m okay with. But then, why stop myself from writing down all the lessons that I’ve learned ever since I turned 18. I guess I could post that under a less cringey title along the lines of “Generic useful lessons in life”.

But then, useful to whom? When? Would I like it to be handed over to me when I was 5? 10?

Exactly. We’ll move in circles here and never get anywhere. That’s probably the reason this article has been a draft for a year or more. But I think I’ll have to get it out of my head first, and deal with the above thought later. Having cleared that, let’s get started * snaps finger *

The satisfaction of reaching a goal is so much smaller than the satisfaction of doing something you love, so love the process for that’s where most of the satisfaction lies

Quotes that urge us to enjoy the climb more than the peak, the journey more than the destination or something along those lines are dime a dozen. Being a quote-collector, I’ve known them forever.

But it is only after repeatedly going through the same journey over and over again — desiring something, working my way towards it and then eventually getting it — have I learned that the joy of having achieved something is minuscule compared to the joy I got along the way doing the thing I loved.

Happiness derived from external sources is very limited, and once the basics are covered, very superficial

Very similar to the previous point, but with an important distinction. Time and again, something has repeated:

  • I’ve desired something materialistic
  • Spent countless hours reading about it and contemplating about the joys its possession will bring me
  • Eventually acquiring it
  • Only to have the joy feel underwhelming after a matter of minutes of doing it

And then recently I came across this quote

“Traveling is a fool’s paradise. Our first journeys discover to us the indifference of places. At home I dream that at Naples, at Rome, I can be intoxicated with beauty, and lose my sadness. I pack my trunk, embrace my friends, embark on the sea, and at last wake up in Naples, and there beside me is the stern fact, the sad self, unrelenting, identical, that I fled from. I seek the Vatican, and the palaces. I affect to be intoxicated with sights and suggestions, but I am not intoxicated. My giant goes with me wherever I go.”

Ralph Waldo Emerson wrote in his 1841 essay, “Self-Reliance.”

I think what Emerson is trying to say is that trying to fix something that’s an issue on the inside by changing something on the outside seldom, if ever, works. But if you’ve ever spent hours browsing Amazon out of boredom thinking the new iPhone or Sony’s new mirrorless camera will make you content and solve all of your life problems, and were lucky to actually be able to buy it, you’ll know that the satisfaction was very short lived and things go back to being exactly the way they were before.

We also vastly overestimate how much joy we’d get from achieving our material goals. Getting to a higher salary, or a dream job, or the latest iPhone; I realized how little joy the outcome eventually brought compared to the misery that the desire for it brought.

Friends and family are what keep you going even on the gloomiest days

I arrived in Berlin on a very cold and gloomy day, and the next few weren’t any different. Unable to cope, and having had my luggage misplaced, I was naturally sad after the second day, asking why was I even there.

Just sitting quietly in my bedroom having exactly 0 friends in this new city, I was feeling (what I’d later learn) the seasonal low mood sprinkled over homesickness. Then I got a call from a friend back home. Then my parents called me. And within an hour or so, I was actually happy I was in Berlin, went out for a walk and had some kebab 🥙

Case in point, I learned that my brain is easily tricked into happiness by interacting with people I love and care about. Just a casual chat a day with a friend keeps me in generally good mood. It is like a tonic for my mental health, doubling as first aid when time comes.

Hence, I think it is important to always have people that one can open up with. These people don’t magically appear but have to be invested in over years to cultivate such relationships and that’s something I really value in my life today.

Bad decisions attract bad circumstances which bring a bad state of mind, and a bad state of mind brings more bad decisions. Works just as well with good decisions

Or the snowball effect. Habits, outcomes of decisions and events in our lives, both good or bad, generally start small and they slowly spiral and become greater in magnitude and have a greater influence on our lives.

For this reason, I think it is important to 1. Be mindful of smaller problems that might have the potential to spiral into bigger ones, 2. Have people around you who can point out and alert you of the small cracks developing and 3. Believe that little good habits, however small, will compound with other little good habits and bring much greater good than their perceived insignificance.

Be aware and intentional

David Foster Wallace’s speech titled this is water talks about learning how to think, how to chose what we think about inside our minds irrespective of what’s happening outside of it, to be in control of our thoughts. I’d highly recommend you listen to it.

Like many, I found the speech very educational, and decided to not only be more attentive of my thoughts and the way I think about things, but also be very intentional about my actions. For me, it means to jump out of the default setting and steering the direction in which I mind decides to run with a thought after having passed it through my values filter.

It also means that when something doesn’t work out, it is usually not a sad outcome, but an educational one because I chose this version of reality over the others, and accepted the risks and rewards associated.

In closing

Phew, that was not easy to write. But I’m happy I did. I can already see myself coming back to this article from time to time. I hope you found some value in the text above

Thank you for reading.

Top Reasons To Use Mozilla Firefox 🔥🦊 Right Now – Part 2

Like many of my friends during the late 2000s, I embarked on my internet journey with Firefox. It started with Firefox being the only browser that could reliably resume downloads in the event of a power outage, which were frequent in my part of India, and that was very useful with a slow internet connection that gave me 10KB/s on a good day.

A few years later I learned about free and open source software, and started thinking of the internet as a public resource and a great equalizer of access to knowledge and opportunity. Firefox was a very natural fit in this newly discovered world of mine.

Around 8 years ago, I published top reasons why you should start using Mozilla Firefox right now documenting the various reasons why Firefox should be the browser of choice for anyone who desires a safe, private and customizable web browsing experience.

Since I’m now part of the organization that I’ve so long revered, I thought an update to the original post is appropriate, and started listing some reasons why Firefox is still my browser of choice today.

I am slightly biased towards Firefox, but in my defense that’s hardly ever changed.


Features that enhance your web experience

One of the reasons people love Firefox is its customizability. The functionality can be extended using Addons and for the truly adventurous, Firefox’s UI is customizable using a bit of custom CSS. Given how personal web browsing is and how much time we spend using a browser, a bit of customization can go a long way.

Powerful Adblocking with uBlock Origin

TL;DR from Ghostery

With enforcement of Manifest V3, Google dramatically limits capabilities of browser extensions. It removes access to powerful APIs that allowed us to provide innovation in privacy protection. Being subjected to those constraints, we have to re-invent the way our extensions operate. Intended or not, Manifest V3 takes choice away from users, exposing them to new threats. Manifest V3 is ultimately user hostile.

https://www.ghostery.com/blog/manifest-v3-the-ghostery-perspective

Ads are the de facto way of monetization on the internet, and many creators rely on it for making a living on the internet. However, since there’s so much money to be made through harvesting data for targeting ads, internet ad companies try to “spy” on people across the internet learning more and more about their browsing habits to show them the most relevant ads.

Many people face a dilemma of having to choose between giving back to the content creators they’ve come to love and depend upon, and not being okay with third party companies looking at their browsing habits all over the internet.

Tools like uBlock Origin prevent “cross site tracking” and block ads and other annoyances from loading on webpages saving bandwidth and energy, and enabling a fast and pleasant web experience.

uBlock Origin also allows enabling ads on certain websites, which is something we should definitely do to support digital creators we rely upon for news, knowledge and entertainment.

Note that online advertising can be both effective and useful, without being creepy as this page from DuckDuckGo describes.

Further reading:

Picture-in-Picture

Picture-in-picture mode detaches the currently playing video on many video streaming websites like YouTube, enabling you to watch a football game while reading an article on Wikipedia all in a resizable little window that can be moved around.

Multi-Account Containers

Want to keep work, social media and finance related websites all separate but don’t want to bother having two browsers or separate web history? Multi-Account containers help you do exactly that.

Now you can browse Facebook in one “container”, access your banking apps on another and keep your work and personal email logged into a third and fourth container. Yes, logged into two Google accounts from the same browser.

None of the websites in one container “see” the websites open in another, either directly or with third party cookie based tracking.

Tree Style Tabs

You’d have to pay me to have me move back to the old way of using browser tabs and you’d fail. They’re that good. Seriously.

Tabs arranged in a “tree” shape

Look maa, no Tabs bar!

Tree Style Tabs give you a quick way to visually see the which tab something came out of, essentially answering the question of “how did I even reach here?” when you are 6 levels down in a Wikipedia rabbit hole about deep sea internet cables or something.

P.S. Annie is one of my favorite creators on the internet. Go follow her page @depthsofwikipedia on Instagram for weird Wikipedia content.


Better privacy controls for all

Because privacy is a fundamental right and most people prefer not having third parties snooping over their shoulders as they browse the internet.

Total Cookie Protection

Firefox rolled out total cookie protection earlier this year which creates separate “cookie jars” for websites preventing cross domain tracking using shared cookies.

Image from Mozilla

Enhanced Tracking Protection & Breach Monitoring

Firefox protects you from malice on the internet. It also does a good job at reporting the protections.

Enhanced Tracking Protection dashboard

Breach monitoring alerts you if your email address was involved in any data leaks across the internet.

Breach Monitor dashboard

Bonus section

This section will have weird things by design.

Custom CSS

As mentioned earlier, Firefox’s UI elements are made with web technologies like CSS. A bit of custom CSS goes a long way into making the Browser look exactly the way you want. A popular workflow is hiding the Tabs bar and relying on Tree Style Tabs for inter-Tab navigation.

Logo 🦊

This is very personal (that is, even more than the rest of this article), but I’m very fond of the Firefox logo. And as we’ve seen in the past, many people feel very strongly about the Fox in the logo so I’m not alone in feeling that way.

Firefox Developer Edition

I use the Firefox Developer Edition as my work browser. It is really good if you work with frontend web technologies like CSS or JavaScript. Debugging CSS or JavaScript on the Developer Edition is a joy, and I was especially impressed at how good it was with Grids.


In closing

If you had asked me 8 years ago why I recommend Firefox, I’d have gone on a long rant about how Firefox is one of the only two major non-Chromium based browsers, and the only one supported by a non-profit that fights to keep the web open and inclusive; That Firefox is built and maintained with the help of thousands of volunteers and open web enthusiasts and so on.

Today I would just say I recommend it because it is a great browser. It is also all of the above if you care, but if all you care about is the best web experience, Firefox will serve you just fine.

Go give the Fox a try! Thank you for reading.

Featured image credits: https://unsplash.com/photos/ZHS3j0_Y_KM

Twenty-seven 🎂

I turned 27 a month ago. I’ve not written a birthday post in a while (last time was turning 24, three years ago) so decided to write something for this one. Not that 27 is a special year or anything (well, I guess it is (un)special in the sense that it only comes once in a lifetime). However I do somehow feel 27 is a round number.

It is hard to explain, but I think I’m at the line that separates an early adult and a full real adult. It’s complicated, and I feel like neither to be very honest.

This year was quite exciting, from settling into my new role at CareerFoundry as a web & security engineer, to traveling around a bit, to getting my dream flat in the NeukĂślln district of Berlin (only to lose it six months later, but hey, not everything has to work out :), to getting my dream job at Mozilla, it has been a really happening year.

I’m really looking forward to this year. New job, new apartment, new part of the city, new technologies to learn and master. So much to be excited about. I decided to write more starting on my birthday, and I’ve been seeing good progress in the last month and a half since then. I’ll document my note-taking habit in an article about Obsidian soon.

My hope for the next one year is to get some stability into my life which got a bit rough in H2. Also hope to do well at work and life in general, but we’ll see that.

Currently I’m into this game called Life is Strange and have been listening to sound tracks from it. I play chess around once or twice per day, and sometimes paint.

Work takes up most of my time, but that’s expected and I’m really enjoying the new learnings coming my way. Overall, life’s good. I’ll go into more detail regarding some of the things I’m up to in separate articles.

Thank you for reading!

office space at evening time with some desks and postit notes

6 Lessons learned during my time at CareerFoundry

I started working at CareerFoundry (“CF”) in January 2019. Ever since then, I’ve changed in many ways. Change is inevitable and we have very limited control over it. In fact, I think that that only tiny bit of control we have over it is the kind of change we’d like to see in ourselves.

One way to dictate positive change is by being curious about new environments and people, embracing new and often uncomfortable situations with an open mind.

Getting hired at CF carved a way to some of my most cherished memories, meaningful relationships and enabled me to pursue the hobbies and interests I always wanted to pursue and be the person I always wanted to be.

It also brought me to the beautiful city of Berlin that had space for me and my eccentricities.

I’ve met some amazing people at CF — people who’ve guided me, mentored me, praised me and then schooled me when I needed it. People who’ve shaped the person you see in me, the person I see in myself.

Inspiration for this article

The inspiration for this article came from a thought I had.

I was visualising our old office at KĂśpenicker Straße and thinking of my first day. I walked from Heinrich-Heine-Straße U-Bahn all nervous, not sure how I’ll feel about working in a new country with new people. I was scared.

I started imagining my current self; the one who has worked at CF for nearly 4 years, on my last day walking down the stairway, meeting my younger self walking up the stairway on his first day.

The young me asks if I have any tips for him that’d help his CF journey.

This is the article I’d send him.

Lesson #1 – Gratitude makes everything better, so does kindness

I admit that it was strange standing in a circle hearing everyone thank one another in my first week at CF. But I got used to it. Some time later I understood the idea itself. 

In a world of ever-increasing needs and wants, gratitude makes us look back at what we already have and feel good about it. The Friday gratitude forced us to look back and remember something good that someone did that helped us in some way. Not just remember, but also announce it.

It is like social-engineering happiness into people, pushing them to look at the beauty in the world. With practice, I got more and more comfortable thanking people and telling them I appreciate them.

Kindness is another quality that I found in abundance during my time at CF. The lengths people went to—to support one another, to assume the best of intentions and to help each other grow—was incredible. I’m a recipient of much of that kindness myself. 

Like with gossiping and complaining, gratitude and kindness are just habits. The more we practice them, the easier it becomes to do it the next time. 

The more we are exposed to any of them, the more we reciprocate it to others. Hence it becomes easier to find people who exhibit the same. It is a beautiful self-sustaining cycle.

Lesson #2 – Growth mindset, or the idea of unlocking new skills with practice

Growth mindset is simply believing that many skills can be learned and improved upon by regular practice and timely feedback as opposed to being born with an innate capacity to do them.

The first time I heard about growth mindset was through one of Martin’s Monday morning speeches. It made sense, but it was only after applying it to many of the skills I wanted to pick up that I understood how powerful a simple change to a way of thinking can be.

I got into many hobbies after that, learned fun skills and gained many friends due to the hobbies. It changed my way of looking at everything and made me more curious.

I’ve gamified the whole experience of knowing absolutely nothing about something, then learning more and more about it, practising, getting feedback and improving, and then magically being able to do it — something that I’d have deemed impossible a short while ago.

Lesson #3 – Good people with good intentions are the overwhelming majority

There was a rant post on Reddit about how Berlin is changing for the worse, and there was this following reply to that post. I thought it was really well written.

You leave a megaphone in a public space, and mostly as**oles pick it up to yell thinly veiled hatred in it. No one cares, except for the other assholes in the megaphone line, who cheer for the bile and can’t wait to be cheered in return. But then, when I picked the megaphone and asked for help, I found help. And when I picked up the megaphone and offered some, I found people to help. The regular people are here. They just aren’t ranting or cheering the rants.

– reddit r/berlin

It isn’t news to anyone that negativity spreads much faster than positivity, and social media only amplifies that. As a result of this, it is very easy to be cynical of everything and everyone.

What I’ve learned at CF is that most people are just like me. 

  • They will do good if given a chance. 
  • They’ll help if they can. 
  • They’ll get out of the way if told they or their actions are causing hurt

One of Martin’s Monday morning speeches was about assuming the best of intentions. I thought it was appropriate as most people have good intentions most of the time, so it makes a lot of sense to have that as our default stance.

Lesson #4 – Rules can be made up to come together with others, do good and spread happiness

Another important lesson I’ve learned at CF goes hand in hand with an amazing video I watched about Optimistic Nihilism from this youtube channel called Kurzgesagt. The premise of the video was that if the universe doesn’t have any purpose, then we get to dictate its purpose; our purpose.

An extension to that idea is creating arbitrary rules for ourselves that help us do more of what we love. At CF, many people are givers. They love doing something for others. 

So why wait for a special yearly holiday to cook for others? Just make up a couple of things like Soup Kitchen and Breakfast Thursdays to enable anyone who loves cooking for others to do it.

And why wait for Thanksgiving to thank someone when we could just thank them every Friday in our Friday Gratitude.

And why wait to take that new colleague of ours and the rest of the team to the best burger joint when you could just have Burger Fridays every Friday.

Convenient, eh?

Honestly, Burger Fridays is my most missed tradition at CF. I’d have also proposed a Donnerstag Döners but Covid had other plans.

Lesson #5 – Company culture is just the people of CF

Recently I watched this speech titled “This is water” by David Wallace in which Mr. Wallace tells us the importance of being aware of the most obvious things around us.

It made me think of this phrase “company culture” and wonder what it is. When I joined CF, I remember trying to fit into the culture. Before I knew it, I was interviewing people who were seeing me and judging the culture of the company. I went from trying to fit in, to defining what the culture at CF was. All of us did.

I then realised something interesting. Since “CF” itself is a virtual entity that only exists in our shared minds, CF’s culture is just how we perceive the rest of CF. In that sense, each and everyone at CF is playing a part in defining the culture of CF at that moment. When someone joins, they bring something new to the table. When someone leaves, they take something away. 

CF’s culture is as dynamic as the people it is made up of and suddenly, “How will we keep the culture at CF the same?” — a question that gets asked a lot whenever we speak of hiring — automatically turns into “How will we hire the kind of people we’d like representing CF?”, and I think that is a more useful question.

Lesson #6 – There’s more to life than just work

My first day at CF was 14th of January, 2019. I was busy setting up my laptop and various company accounts, and reading some documentation. At around 6.30pm, Megan came to my table and said “that’s enough for the first day — go home now”. I looked around and almost everyone had already left.

This was strange to me. It was still a couple of hours till dinner time. Why not just keep working and go straight to dinner?

You see, I didn’t have leisure time in my vocabulary back then. It was always doing something. Working, eating, sleeping, coding, studying. Something. 

I grew up with a hustle-culture mindset where if I’m not working 10 hours a day and coding in my free time, I’d not consider myself “ambitious” and probably not “make it”. Berlin and CF changed all of that.

All of a sudden, I had at least a couple of hours every day, and two full days of weekend when I wasn’t expected to keep working. I could do something else. But what? 

Anything.

I started pursuing hobbies that weren’t career related or would ever be monetized. I started biking and playing chess, painting and playing music or just sitting by the canal doing absolutely nothing. 

It is nice to not have to constantly think of free time as wasted time, and I have CF and Berlin to thank for this huge change in my way of thinking and living my life.

Thank you, dear friends

I am grateful for everything I’ve learned from the people at CF. It has made me a different and, in my opinion, better version of myself, and I can’t thank you all enough. I’ll pay it forward wherever I end up in life.

All the best – Abhi!

Crossposted from Linkedin

Puzzle Time – Distance To Horizon

A couple of months ago I was on a flight and had a thought: How far is the horizon at this very moment?

I got my notebook and pen out and tried to solve this using only the knowledge I had, since there was no internet anyway. Of course, many assumptions were made as were convenient for the calculation.

I planned on checking my answer once I landed. If you want to attempt it, stop reading here and come back later when you have a solution. Unfortunately for me, I made a little error subtracting between two big numbers and got a completely wrong answer.

Assumptions

I assumed the following

  • Perfect visibility, no degradation due to atmosphere
  • Earth is a perfect sphere with a radius of 6400km
  • Aeroplane flying at an altitude of 10km

Solution

Thanks to the assumptions, the problem reduces to primary school geometry problem and is trivial to solve once visualized clearly on a piece of paper.

Using Pythagoras Theorem we know how to calculate the third side of a right angled triangle give the first two. We also know that a tangent to a circle (the line of sight from an aeroplane) meets the circle in a way that it is perpendicular to the radius drawn from the point of contact.

Combining the two pieces of knowledge, the problem can be trivially arranged as follows

(R + r)² = R² + Dh²

Where

  • R is the radius of the earth (in Kilometers)
  • r is the distance of the aeroplane from the surface of the earth (in Kilometers)
  • Dh is the distance to the horizon

Rearranging, we get

Dh² = (R + r)² – R²

Expanding the (a + b)² equation

Dh² = R² + 2Rr + r² – R²

Dh² = 2Rr + r²

Taking square root on both sides

Dh = √(2Rr + r²)

Substituting the values

Dh = √(2*6400*10 + 10²)

Dh = √(128000 + 100)

Dh = √(128100)

Dh = 357.9

Distance to horizon is about 358 kilometers when the aeroplane is at an altitude of 10 kilometers. Now looking at the map while sitting in a flying aeroplane is a lot more insightful as I can guess which cities I’m looking at outside the window.

That’s it for this little post. Thank you for reading!

Cross Site Scripting (XSS), Cross Site Request Forgery (CSRF) And Server Side Request Forgery (SSRF)

XSS, CSRF (or XSRF) and SSRF are common vulnerability in modern web applications where an attacker tries to imitates either a legitimate client to an unsuspecting server or a legitimate server to another unsuspecting server. The basic underlying principle behind each of these attack remains the same; performing action on behalf of a legitimate entity. Let’s look at each of them in a bit more detail and learn about how to protect our web applications against each of them.

XSS (Cross Site Scripting)

XSS or Cross Site Scripting occurs when an attacker manages to execute malicious script code in a victim’s browser as the victim. Browsers store a lot of sensitive information in them. Some of this information is used to identify a user on a website.

A script loaded from a website can access information stored on your browser through that website, which is how sessions work in your browser. That’s how Facebook or any other website knows to show you your personalized information and not someone else’s.

XSS occurs if an attacker gets control over the scripts running in your browser. If they can execute code, they can steal your login credentials and trick you into installing malware on your computer.

There are different kinds of XSS attacks and they depend on where the payload is stored.

Reflected XSS

A reflected XSS vulnerability occurs when a piece of data from a URL is reflected back into the website code unsanitized and can be injected into. This can be a result of a GET or a POST request, and it is especially severe as an unauthenticated GET request as that URL can be shared on social media and anyone clicking on it gets compromised.

Remediation of reflected XSS – Sanitization of all user inputs before passing it back into the view

Stored XSS

A stored XSS vulnerability occurs when a web application stores an XSS attack payload without sanitizing it and then displays it back to the same user or a different user. A notable recent example is British Airways website getting compromised and exposing sensitive data including credit card information of 380,000 transactions.

Remediation of stored XSS – It is the same as with reflected XSS: Sanitization of all user inputs before storing the data in the database.

DOM based XSS

Unlike reflected/stored XSS, a DOM based XSS occurs only on the client’s side. This can be a result of a user typing in a string into an input field that gets parsed and executed as code. An attacker can trick a user to paste a string into their browser which will execute due to insecure parsing and compromise a user’s credentials.

Remediation against DOM based XSS – Display text as text, and nothing else. Instead of element.innerHtml use element.innerText or element.textContext to ensure the data displayed back to a user is purely text.

CSRF (Cross Side Request Forgery)

CSRF occurs when a malicious website makes a request to a legitimate server through an unsuspecting victim.

Web applications communicate with clients through HTTP requests. When a request is made, the browser attacks all information that it knows about the website along with the request, including login/authentication credentials (called cookies).

If the web server doesn’t have protective measures, a request made through a legitimate website and an attacker’s website look exactly the same (or they can be forged to look the same). As a result, an attacker can make a request telling the victim’s bank to transfer $100 to the attacker’s account, and since the request is made through the attacker’s browser, the bank’s server will process it as a legitimate request.

Remediation of CSRF – CSRF can be easily prevented by requiring any unsafe request to validate itself using a valid CSRF token that can only be found in the website’s code and changes on every use. Additionally, authentication/login cookies can be marked as sameSite only, such that any third party website making the request doesn’t contain the sensitive authentication cookies.

SSRF (Server Side Request Forgery)

SSRF is similar to CSRF, but instead of an compromised client making a request to an unsuspecting server, here a compromised server makes a request to itself or another unsuspecting server.

Since a server might be a privileged node in the network, the attacker can make the server access and return sensitive information or perform privileged actions that the attacker’s account wouldn’t allow.

SSRF can also be used to trigger code execution in servers where the vulnerability can be exploited using the privileges of the server itself.

Remediation of SSRF – Any outgoing request needs to be explicitly allowed from the application by maintaining an allowlist of domains and servers a given server can connect to. The scope of these requests should be made as narrow as possible.

In conclusion

I hope that was an interesting quick read on one of the most common vulnerabilities in modern web application. Injection and SSRF are two of OWASP’s top 10 for 2021, so it is definitely worth looking into them and protecting our web applications from potential vulnerabilities.

Read more on OWASP top 10 here: https://owasp.org/www-project-top-ten/

Thank you for reading

nginx blocking malicious request

Setting Up ModSecurity + OWASP Core Rule Set + Nginx On AWS EC2

ModSecurity is a web application firewall. It can protect your web application from preying eyes of vulnerability scanners and attackers. It is extremely customizable, and when paired with OWASP’s Core Rule Set, covers quite a lot of web technologies and frameworks.

In this article, we’ll set up ModSecurity on an AWS EC2 Server running Nginx web server.

Table of contents

Set up Nginx on Ubuntu server

For this tutorial, we’re using AWS LightSail’s Ubuntu image. Choose any instance size depending on your requirements. I’ll choose a 40$ / Month instance with 8GB RAM and 2vCPUs just so that the compilation of ModSecurity is faster.

Once the instance is created, log into the instance with SSH and update packages

$ apt update && apt upgrade -y

Install Nginx

$ sudo apt install nginx

Check what version of Nginx did we get from our package manager. This will be used when compiling Nginx later.

$ nginx -v

I got the following output:

nginx version: nginx/1.18.0 (Ubuntu)

To make sure the webserver is successfully installed and running, simply visit the IP address of the server. It should look something similar to this:

Set up ModSecurity

First we’ll need to install compilation and other dependencies.

$ sudo apt-get install -y apt-utils autoconf automake build-essential git libcurl4-openssl-dev libgeoip-dev liblmdb-dev libpcre++-dev libtool libxml2-dev libyajl-dev pkgconf wget zlib1g-dev git

Next we’ll clone the ModSecurity repository into the /opt directory

$ cd /opt && sudo git clone --recursive https://github.com/SpiderLabs/ModSecurity && cd ModSecurity

Next we run the build script

$ sudo ./build.sh

Next we’ll run the compile script that will fetch all the dependencies for the compilation

$ sudo ./configure

It is possible that this command fails and reports you of any dependencies that are still missing. You can simply google them with “install XYZ on Ubuntu” and run the configure command again. Ideally it will just exit without any errors.
Next we start with the actual compilation of ModSecurity

$ sudo make

A reason why I didn’t go with the smallest server was that this step is resource intensive and could take 15 minutes or more depending on your server’s CPU and memory.

If all went through, we can now install ModSecurity

$ sudo make install

If all went through without any errors, we have ModSecurity installed.

Set up ModSecurity <-> Nginx connector

We start off by downloading ModSecurity-Nginx and Nginx source code. Note that the version of Nginx in the next command must match the version installed on our system. For me, that’s 1.18.0 but it could be different for you.

$ cd /opt && git clone https://github.com/SpiderLabs/ModSecurity-nginx.git
$ cd /opt && sudo wget http://nginx.org/download/nginx-1.18.0.tar.gz

Untar the Nginx source. Replace the Nginx version in the next command if needed.

$ sudo tar -xvf nginx-1.18.0.tar.gz

Next, we need to grab configure arguments. For that, run the nginx command with a capital ‘V’ flag.

$ nginx -V
nginx version: nginx/1.18.0 (Ubuntu)
built with OpenSSL 1.1.1f  31 Mar 2020
TLS SNI support enabled
configure arguments: --with-cc-opt='-g -O2 -fdebug-prefix-map=/build/nginx-7KvRN5/nginx-1.18.0=. -fstack-protector-strong -Wformat -Werror=format-security -fPIC -Wdate-time -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now -fPIC' --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --modules-path=/usr/lib/nginx/modules --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-debug --with-compat --with-pcre-jit --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_v2_module --with-http_dav_module --with-http_slice_module --with-threads --with-http_addition_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_image_filter_module=dynamic --with-http_sub_module --with-http_xslt_module=dynamic --with-stream=dynamic --with-stream_ssl_module --with-mail=dynamic --with-mail_ssl_module

Note the “configure arguments” in the command’s output. We need to build modsecurity with these arguments.

$ sudo ./configure --add-dynamic-module=../ModSecurity-nginx <paste configure args here>

For example, here’s what I’ll run:

$ sudo ./configure --add-dynamic-module=../ModSecurity-nginx --with-cc-opt='-g -O2 -fdebug-prefix-map=/build/nginx-7KvRN5/nginx-1.18.0=. -fstack-protector-strong -Wformat -Werror=format-security -fPIC -Wdate-time -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now -fPIC' --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --modules-path=/usr/lib/nginx/modules --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-debug --with-compat --with-pcre-jit --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_v2_module --with-http_dav_module --with-http_slice_module --with-threads --with-http_addition_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_image_filter_module=dynamic --with-http_sub_module --with-http_xslt_module=dynamic --with-stream=dynamic --with-stream_ssl_module --with-mail=dynamic --with-mail_ssl_module

Don’t copy the above command. You must use the configure arguments supplied by your installation of Nginx.

Next we build the modules

$ sudo make modules

This is a compilation step and may take a little while (a minute or so) to complete. The final step here is to copy the compiled modules to a place from where we can reference them from our Nginx config.

$ sudo mkdir /etc/nginx/modules
$ sudo cp objs/ngx_http_modsecurity_module.so /etc/nginx/modules

Loading ModSecurity module in Nginx

Simply add the following line to the nginx config file at /etc/nginx/nginx.conf outside any block.

load_module /etc/nginx/modules/ngx_http_modsecurity_module.so;


Set up OWASP Core Rule Set

OWASP’s Core Rule Set is a set of rules that cover most common frameworks and technologies as well as cover signatures for common web application attack payload. It is a good place to start if you don’t want to write custom rules for many common attacks.

First, we’ll clone the modsecurity-crs repository

$ sudo git clone https://github.com/coreruleset/coreruleset /opt/coreruleset

Then we’ll rename the crs-setup config file

$ sudo mv /opt/coreruleset/crs-setup.conf.example /opt/coreruleset/crs-setup.conf

Activate the default exclusion rule file.

sudo mv /opt/coreruleset/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf.example /opt/coreruleset/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf

Next we create a place for coreruleset to live within /etc/nginx and copy some additional config files.

$ sudo mkdir -p /etc/nginx/modsec
$ sudo cp /opt/ModSecurity/unicode.mapping /etc/nginx/modsec
$ sudo cp /opt/ModSecurity/modsecurity.conf-recommended /etc/nginx/modsec/modsecurity.conf

Next we create a config file that will include our main ModSecurity config file and CRS setup files.

$ sudo touch /etc/nginx/modsec/main.conf

And add the following lines to the config file.

Include /etc/nginx/modsec/modsecurity.conf
Include /opt/coreruleset/crs-setup.conf
Include /opt/coreruleset/rules/*.conf

Finally, we reference this main.conf file from our Nginx config.

$ sudo vim /etc/nginx/sites-available/default

And add the following line within the server block.

modsecurity on;
modsecurity_rules_file /etc/nginx/modsec/main.conf;


Turn ModSecurity “on” and test XSS payload

So far, we’ve configured everything but if we restart Nginx now, it won’t filter attacks but only detect them since the default operating mode of ModSecurity is to only log malicious requests. To change that, let’s open the file /etc/nginx/modsec/modsecurity.conf and change the line

SecRuleEngine DetectionOnly

to

SecRuleEngine On

For our changes to go live, we’ll need to restart Nginx.

$ sudo systemctl restart nginx

Let’s test our ModSecurity installation. Open your browser and send a sample payload in the GET parameter. It doesn’t have to be a real parameter, but just something that can trigger an XSS filter.

http://[server-ip]/index.html?xss-payload=<script>alert(1)</script>

That’s ideal. ModSecurity is working and blocking seemingly malicious requests to our web server. Now any application that sits behind our web server will be protected against many generic web application attacks, even OWASP Top 10 thanks to OWASP’s CoreRuleSet.

In conclusion

It isn’t the most straightforward of installations, but it isn’t very difficult either. The hard part, however, starts here and it is to get rid of all false positives and tweak the installation such that it fits the needs of your specific web application. Depending on how complex an application you’re trying to protect, it can be fairly time consuming.

I’ll write an article on how to tweak the parameters of ModSecurity and make it fit our needs in the future in a separate article.

That’s it for this article, thank you for reading!

WordPress Security Checklist: How To Secure Your WordPress Website

WordPress has been powering my blog since the start of last year. In fact, migrating my Jekyll template to WordPress was one of the highlights of my new year 2021 and I’m very happy that I did, although I didn’t publish as much as I had hoped for. Fortunately, I’ve learned a lot more about WordPress over the course of a year than when I started. In this short primer, I hope to go into a bit more depth on how to securely run a self hosted WordPress website.

Prerequisites

Before we get started, there are a few things that we need to make sure we have to

  • Self hosted WordPress installation with SSH access
  • Administrator account to set up plugins

Table of contents

  1. Keep plugins to the minimum and up to date
  2. Fix file permissions
  3. Two-Factor Authentication
  4. Set up auto banning of failed logins
  5. Enable regular backups
  6. Disable XML-RPC
  7. Disable file editing in WordPress admin
  8. Use a Web Application Firewall
  9. Don’t forget the usual web security measures

1. Keep plugins to the minimum and up to date

I wish I could just sticky something like this on top of most of my articles, but most people trying to attack our websites don’t have the time or resources to develop and use 0days. They use existing exploits out in the wild and some of these exploits can be months old, if not more. WordPress core and plugin authors can only do so much more than promptly releasing patches for security vulnerabilities that they find.

So then it is up to us as site admins to make sure we patch as soon as is feasible. Having worked on many large codebases, I know automatic updating isn’t always possible or even desirable, but having an eye on the changelog can definitely help not get compromised.

I’d also recommend a web security helper plugin that sends alert emails when it detects outdated plugins / themes / core.

2. Fix file permissions

During development, many files and directories permissions are way too open to make it easy to set up the website and all plugins. In production, however, the permissions can be dialed down a notch to prevent anyone with any access on the server to take over the whole website.

Similarly, attackers typically upload shell code using the uploads functionality, and if code execution is disabled in the directory, we make it harder for this attack to succeed.

A detailed guide on setting file permissions can be found on official WordPress documentation: https://wordpress.org/support/article/changing-file-permissions/

3. Two-Factor Authentication

Administrator accounts have many powers on a WordPress website, and a compromised administrator account can lead to uploading of PHP shell code leading to command execution and server compromise.

To make sure admin accounts are extra secure, enforce 2FA on all administrator accounts. This can be done by any 2FA or login security plugin on the WordPress plugin store.

4. Set up auto banning of failed logins

Since WordPress doesn’t ship with any builtin way of auto-banning failed login attempts, we have to rely on plugins like WordFence. WordFence will need to be configured with options to block login attempts after a certain number of failed attempts.

WordFence can also help you disable execution in upload directories, block IP addresses making malicious requests and much more.

5. Enable regular backups

While we can take preventive measures against mishaps, we can never be sure. Hence it is imperative that the website is backed up regularly. Backing up can be done at multiple places. The database can be backed up separately from the static assets and files. There are many plugins, like WPVivid, that help you fine tune what gets backed up and where it gets stored. It is always nice if you can afford an external backup location, like AWS S3.

The hosting provider might also have ways of backing up the website. For example, AWS Lightsail has daily instance snapshots which backs up the entire disk.

6. Disable XML-RPC

If you don’t use plugins that rely on XML-RPC or using the WordPress mobile app, it is wise to disable XML-RPC which removes another widely used attack surface by attackers. Many plugins allow the disabling of XML-RPC, including the aforementioned WordFence.

7. Disable file editing in WordPress admin

Disable editing of files from WordPress admin as that’s almost never a good idea, especially if you can achieve the same using more secure methods like SSH. To disable file editor, simply add

define( 'DISALLOW_FILE_EDIT', true );

to your wp-config.php file.

8. Use a Web Application Firewall

A firewall plugin like Sucuri or WordFence can identify attack signatures and block malicious requests. Many also include IP address block lists that prevent known malicious IP addresses from reaching your WordPress website.

For more control, there’s ModSecurity. ModSecurity needs to be installed alongside the web server and it can detect and block known attack signatures for not just WordPress but just about any popular web framework. It does require a deeper technical know how to setup and maintain ModSecurity, and a plugin might work be a better approach for most people.

9. Don’t forget the usual web security measures

A WordPress website is, at the end of it all, a website. While there are WordPress specific ways of hardening a WordPress installation, there is also a whole plethora of best practices that apply to every website, including the WordPress ones.

  1. Use HTTPS – SSL/TLS certificates are free, and usually come by default with many hosting providers and CDNs. Don’t forget to turn it on and enforce it in strict mode.
  2. Use appropriate security headers – Headers tell the browser how to handle your website’s content. Many client side attacks can be mitigated by using the right set of headers. A detailed list of useful headers can be found on OWASP’s website: https://owasp.org/www-project-secure-headers
  3. Use CAPTCHA on login page – to prevent bot submissions and more sophisticated bruteforce attacks, enforce a CAPTCHA like reCaptcha on login page. WordFence supports this out of the box (needs an API key from Google).
  4. Handle user input with care when using a custom theme – when using a custom theme that accepts user input in the form of query parameters to show filtered content, the regular best practices around user generated input has to be followed. Embedding user input in output can lead to Cross Site Scripting, while passing it straight to the database can lead to SQL Injection.

In conclusion

I hope that was useful. If you have any questions around WordPress or suggestions to improve this article, feel free to reach out to me via email. Thank you for reading!

Perfect 100/100 PageSpeed Score With WordPress

A long time ago I worked on a theme called Elementary for my Jekyll blog. The goal was simple, to create a website that just works, and works fast. In fact, I’ll just paste the line from the readme of the GitHub repository.

This is my personal blog’s Jekyll template that I’ve been optimizing for performance, accessibility, usability, readability and simplicity in general.

I personally do not approve of personal blogs bloated with hundreds of kilobytes of trackers and analytics code, and hence, this is an attempt at creating something that I’d be comfortable with using on my website.

The goal was accomplished. I managed to get a perfect score on many of the pages. But I wanted to write more and while on the go, and plaintext editing on phones is a pain. Then the other problem was to add it to git and push it. In short, working with a static blog from an Android phone wasn’t easy.

That’s when I moved to WordPress. I ported the theme to Elementary-WordPress, which is essentially the same theme but in a WordPress shell. It worked really well, but the problem was all the bloat that WordPress sends to the frontend. For a while I didn’t care enough. I was still serving a fast website, albeit with Jquery, emojis and other code that wasn’t getting used anywhere else.

Today, that changed. I finally took some time to optimize the website and got back my perfect 100/100 PageSpeed score. Here’s how I did it.

Table of contents

  1. Disable jQuery
  2. Disable wp-embed
  3. Disable block library CSS
  4. Disable emoji
  5. Serve fonts from same domain
  6. Use font-display: optional property
  7. Use an in-memory page cache like Memcached
  8. Fix conflicting cache strategies
  9. Use a CDN for asset delivery
  10. TODO: Inline all CSS and Javascript

Disable jQuery

If your website isn’t ancient, there’s a good chance you’re not using it. If some plugin you’re using is using jQuery, consider alternatives. It will save you ~30KB and an HTTP request. Adding the following to the functions.php should do it.

function jquery_dequeue() { 
  wp_deregister_script( 'jquery' );
}
add_action( 'wp_enqueue_scripts', 'jquery_dequeue' );

Disable wp-embed

The following snippet from this answer needs to be added to functions.php

function wp_embed_dequeue() {
  wp_deregister_script( 'wp-embed' );
}
add_action( 'wp_footer', 'wp_embed_dequeue' );

Disable block library CSS

*Sigh* This goes into the functions.php

function remove_wp_block_library_css(){
  wp_dequeue_style( 'wp-block-library' );
}
add_action( 'wp_enqueue_scripts', 'remove_wp_block_library_css' );

Disable emoji

I found some nice code to disable a whole bunch of unnecessary actions from this guide here: https://kinsta.com/knowledgebase/disable-emojis-wordpress/#2-disable-emojis-in-wordpress-with-code

Serve fonts from same domain

If you’re not super keen on using the smart browser detection functionality that Google Fonts offers and are happy only supporting modern browsers, simply downloading the font files and linking them with @font-face can save an additional DNS and HTTP request.

Use font-display: optional property

I’m using font-display: optional; CSS property on my @font-face and it pushed my PageSpeed score over the top. Essentially it prevents the CLS, or Cumulative Layout Shift metric of Core Web Vitals from getting affected due to page shifting due to slow loading of font files.

Read more about it here: https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display

Use an in-memory page cache like Memcached

Building pages to serve the users is expensive as it involves the database, but isn’t something that needs to be done for every visitor visiting the same page. A plugin like W3 Total Cache coupled with a Memcached instance (could be running on the same server as the website) could enable caching of pages among other resources in memory, reducing the load on the server and improving performance for cache-hit pages.

memcached

Fix conflicting cache strategies

I’m using W3 Total Cache plugin that helps minify and cache CSS and JS files. But I wasn’t seeing any minification happening. Upon some reading, it turns out that CloudFlare’s minification conflicts with W3 Total Cache’s. Disabling it on CloudFlare’s side fixed the non-minification problem for me.

Use a CDN for asset delivery

Once the thing to deliver is optimized, it is a good idea to optimize the delivery pipeline as well. Since my server is in the same country as me, it is easy to make a mistake of thinking every visitor of the website is seeing a 50 milliseconds time to connect to the server. The further the user is from the origin server, the longer it could take.

Hence, an global CDN like CloudFlare should be used which can serve static content from its edge node physically closest to the visitor.

TODO: Inline all CSS and Javascript

It doesn’t go beyond 100, but I’d still like to improve it further. For one, the little bit of CSS and JS that does exist doesn’t have to need two additional HTTP requests. Inlining that bit will mean that blog posts without an image, which for me are most of them, will get served in only three HTTP requests; the document, the font file and the favicon. Pretty cool, huh?

Conclusion

I’m pretty pumped about the 100/100 score. WordPress has a reputation for being slow and bloated, but with some simple optimizations, it starts performing like how you’d expect some text on a page to perform like.

Thank you for reading!