Latest Posts (8 found)
Hugo 2 days ago

Securing File Imports: Fixing SSRF and XXE Vulnerabilities

You know who loves new features in applications? Hackers. Every new feature is an additional opportunity, a potential new vulnerability. Last weekend I added the ability to migrate data to writizzy from WordPress (XML file), Ghost (JSON file), and Medium (ZIP archive). And on Monday I received this message: > Huge vuln on writizzy > > Hello, You have a major vulnerability on writizzy that you need to fix asap. Via the Medium import, I was able to download your /etc/passwd Basically, you absolutely need to validate the images from the Medium HTML! > > Your /etc/passwd as proof: > > Micka Since it's possible you might discover this kind of vulnerability, let me show you how to exploit SSRF and XXE vulnerabilities. ## The SSRF Vulnerability SSRF stands for "Server-Side Request Forgery" - an attack that allows access to vulnerable server resources. But how do you access these resources by triggering a data import with a ZIP archive? The import feature relies on an important principle: I try to download the images that are in the article to be migrated and import them to my own storage (Bunny in my case). For example, imagine I have this in a Medium page: ```html ``` I need to download the image, then re-upload it to Bunny. During the conversion to markdown, I'll then write this: ```markdown ![](https://cdn.bunny.net/blog/12132132/image.jpg) ``` So to do this, at some point I open a URL to the image: ```kotlin val imageBytes = try { val connection = URL(imageUrl).openConnection() connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36") connection.setRequestProperty("Referer", "https://medium.com/") connection.setRequestProperty("Accept", "image/avif,image/webp,*/*") connection.connectTimeout = 10000 connection.readTimeout = 10000 connection.getInputStream().use { it.readBytes() } } catch (e: Exception) { logger.warn("Failed to download image $imageUrl: ${e.message}") return imageUrl } ``` Then I upload the byte array to Bunny. Okay. But what happens if the user writes this: ```html ``` The previous code will try to read the file following the requested protocol - in this case, `file`. Then upload the file content to the CDN. Content that's now publicly accessible. And you can also access internal URLs to scan ports, get sensitive info, etc.: ```html ``` The vulnerability is quite serious. To fix it, there are several things to do. First, verify the protocol used: ```kotlin if (url.protocol !in listOf("http", "https")) { logger.warn("Unauthorized protocol: ${url.protocol} for URL: $imageUrl") return imageUrl } ``` Then, verify that we're not attacking private URLs: ```kotlin val host = url.host.lowercase() if (isPrivateOrLocalhost(host)) { logger.warn("Blocked private/localhost URL: $imageUrl") return imageUrl } ... private fun isPrivateOrLocalhost(host: String): Boolean { if (host in listOf("localhost", "127.0.0.1", "::1")) return true val address = try { java.net.InetAddress.getByName(host) } catch (_: Exception) { return true // When in doubt, block it } return address.isLoopbackAddress || address.isLinkLocalAddress || address.isSiteLocalAddress } ``` But here, I still have a risk. The user can write: ```html ``` And this could still be risky if the hacker requests a redirect from this URL to /etc/passwd. So we need to block redirect requests: ```kotlin val connection = url.openConnection() if (connection is java.net.HttpURLConnection) { connection.instanceFollowRedirects = false } connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36") connection.setRequestProperty("Referer", "https://medium.com/") connection.setRequestProperty("Accept", "image/avif,image/webp,*/*") connection.connectTimeout = 10000 connection.readTimeout = 10000 val responseCode = (connection as? java.net.HttpURLConnection)?.responseCode if (responseCode in listOf(301, 302, 303, 307, 308)) { logger.warn("Refused redirect for URL: $imageUrl (HTTP $responseCode)") return imageUrl } ``` Be very careful with user-controlled connection opening. Except it wasn't over. Second message from Micka: > You also have an XXE on the WordPress import! Sorry for the spam, I couldn't test to warn you at the same time as the other vuln, you need to fix this asap too :) ## The XXE Vulnerability XXE (XML External Entity) is a vulnerability that allows injecting external XML entities to: - Read local files (/etc/passwd, config files, SSH keys...) - Perform SSRF (requests to internal services) - Perform DoS (billion laughs attack) Micka modified the WordPress XML file to add an entity declaration: ```xml ]> ... &xxe; ``` This directive asks the XML parser to go read the content of a local file to use it later. It would also have been possible to send this file to a URL directly: ```xml %dtd; ]> ``` And on [http://attacker.com/evil.dtd](http://attacker.com/evil.dtd): ```xml "> %all; ``` Finally, to crash a server, the attacker could also have done this: ```xml ]> &lol9; 1 publish post ``` This requests the display of over 3 billion characters, crashing the server. There are variants, but you get the idea. We definitely don't want any of this. This time, we need to secure the XML parser by telling it not to look at external entities: ```kotlin val factory = DocumentBuilderFactory.newInstance() // Disable external entities (XXE protection) factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true) factory.setFeature("http://xml.org/sax/features/external-general-entities", false) factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false) factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false) factory.isXIncludeAware = false factory.isExpandEntityReferences = false ``` I hope you learned something. I certainly did, because even though I should have caught the SSRF vulnerability, honestly, I would never have seen the one with the XML parser. It's thanks to Micka that I discovered this type of attack. FYI, [Micka](https://mjeanroy.tech/) is a wonderful person I've worked with before at Malt and who works in security. You may have run into him at capture the flag events at Mixit. And he loves trying to find this kind of vulnerability.

0 views
Hugo 2 weeks ago

Someone tried to hack me

Writizzy is barely two weeks old. Last week, I faced my first Black Hat attack. It wasn't a Bug Bounty probe; it was a **malicious attempt to delete production data**. This is how I detected and remediated the attack, and why this event cemented my philosophy of **'Pain in the Ass Driven Development'** for early-stage products. ## White Hat vs. Black Hat Why am I calling this a black hat attack? You might say, a hacker is a hacker, regardless of intent. However, there are nuances to consider. You have the **White Hats**, who test your site and then report the vulnerabilities they find. It's quite common to be contacted by white hats, who also expect some compensation. However, this remains risky because the legal framework is often unclear, and the researcher could get into trouble. Therefore, the vast majority of white hats work within legally framed **Bug Bounty programs** provided by companies. The secondary benefit of these programs is guaranteeing payment for the vulnerabilities found. Within the white hat category, there is a second group, often referred to as [beg bounty](https://www.troyhunt.com/beg-bounties/). These are often people who have industrialized basic checks on websites, finding often minor flaws, and randomly contact sites hoping for a reward. And then you have the **Black Hats**, with many different motivations: - **Data theft:** The goal is to resell the information elsewhere. - **Data destruction:** Driven by ideology, for fun, or for personal reasons. It is undoubtedly this last scenario that presented itself to us last week. The attacker attempted to delete articles specifically from the [thomas-sanlis.com](https://thomas-sanlis.com) blog, and only that one. ## Detection The attack took place on November 7th. I started receiving Sentry alerts regarding inconsistent usage, for example, **attempts to delete data that did not belong to the requesting author**. Since the application is a Nuxt SSR application, the client communicates with the backend via an API. The attacker attempted to directly exploit the API by replacing the identifiers used, which is known as an **IDOR (Insecure Direct Object Reference)** vulnerability or, more generally, **Broken Access Control**. Fortunately, the API performs numerous server-side checks, notably by verifying the session information and its consistency with the received request. I was able to investigate the traces to find the author and the target. The author created an account using a **disposable email address**. These mailboxes are frequently used to avoid leaving a trace. I’ll come back to how to prevent this later. The target was the blog of Thomas Sanlis. A bit later, the same person attempted to change their account email to use Thomas's, likely with the goal of logging into his account, given the first attack failed. ## Remediation Actions Writizzy was launched less than 2 weeks ago, so I hadn't yet implemented all the necessary protections. What was in place was enough to fend off the attack—hopefully, it's always hard to be 100% sure. But I knew certain parts needed to be more robust. I now have an arsenal of actions available: - **Account blocking**. - **Temporary blocking for brute force attacks** (temporary to prevent an attacker from also blocking all accounts they know are using the site). - **Rate limiting** on certain actions and per IP. I will also likely prohibit the use of disposable emails on the site. There are two main approaches for this: - Calling an external API to validate the email (e.g., [debounce.io](http://debounce.io), [emailable.com](http://emailable.com), [captainverify.com](), etc.). - Checking against an open-source list yourself. One of the best known seems to be: [disposable-email-domains](https://github.com/disposable-email-domains/disposable-email-domains), but this requires a regular and automated update mechanism for the list. ## Pain in the Ass Driven Development (PITADD) This reminded me of a previous company I created. Moderation did not exist until the day one person contacted 1,000 people at once overnight. We created a moderation feature the very next day to prevent it from happening again. It is always a question of timing. It's difficult to anticipate everything, and it's expensive, too. So there is always a **balance between cost and risk**. This balance changes depending on the context—if you work in the medical field, for example. But you cannot build a bunker on Day 1 for a non-critical site. Of course, a necessary minimum is required and some basic stuff are non-negotiable, especially regarding data protection. But the rest comes as risks increase, such as when the company grows. Here is my framework for risk arbitration (examples): - **Data theft** is critical. It must be anticipated on **Day 1**. - **Data loss** is important, but can be mitigated by backups. Implementation can be postponed a bit. - **Spam** is an annoyance. It can be handled manually as long as the volume remains low. etc... I categorize this arbitration as: **Pain in the Ass Driven Development.** This applies just as much to security, design, architecture, and so on. It's simply not possible to have the same level of maturity as a 10,000-person company from the start. You have to wait until it hurts, or until it risks hurting very badly. The trade-off is that you have to be responsive and have the right measurement tools in place (monitoring, alerts, logs, etc.), otherwise it's not a strategy, it's negligence.

0 views
Hugo 3 weeks ago

Being Opinionated

I built a company that ended up getting big (more than 600 people). And that's not always easy to manage. Especially because you constantly need to reaffirm what you are, and what you're not. We often talk about enshittification, and it happens a lot with software that grows too much. They get worse because they need to keep pleasing new users, new needs, address every edge case. This can create incredibly powerful tools, but also incredibly complex ones, and sometimes that complexity gets dumped straight onto the user. To be clear, I'm not saying it's inevitable. There are good products that have managed to grow well. But people don't realize the difficulty behind that growth: - knowing how to say no - knowing how to hide complexity. Because complexity doesn't mean complicated - knowing when to cut things Building a product is **making choices**. And all of this also applies to how you talk about your product. In the beginning, it's authentic by definition, because it's the creators talking directly. Sometimes it's clumsy, but it's direct, it's opinionated. The creator says where they want to go, what they are, and what they're not. Then over time, teams take over communications. You need to appeal to more people, attract new user segments, have a smoother message. There's a real risk at that point: trying to please everyone, you end up pleasing no one. I was having this conversation earlier with Thomas about [writizzy](https://writizzy.com)'s homepage. It's deliberately very clean, very simple. Just a manifesto. That's it. It's rare to do that for a company with several million dollars in annual revenue. Because **you need to convert, right?!** I think in the beginning, you should focus on saying who you are. With my previous company, we had a manifesto for the first year. That manifesto laid out who we were and what we wanted to do. > You're free to work freely People coming to the site didn't need to scroll for 5 minutes through dozens of beautifully designed blocks. The truth is, for a product you don't know, nobody does that. A new visitor needs to decide whether to continue in less than 10 seconds. There needs to be a strong statement somewhere that hooks them. And for us, that statement is who we are. And who we're not. **A blogging platform that doesn't waste your time.** This phrase isn't positive. Someone pointed that out to us. Why not just say "A blogging platform that saves your time"? True. It's more consensual. But we just showed up. We're inevitably going to be compared to others. That's natural. What the hell are we doing here? We're frustrated, and we're building this product in opposition to something. We want to create a simple product. But not simplistic. And beyond that, we want to give people back the ability to write easily, without being forced to show off on their professional network or create fake engagement on some bot-filled social media platform. Maybe tomorrow our homepage will be full of colorful blocks with flashy marketing slogans. Or not.

0 views
Hugo 3 weeks ago

Can You Still Learn to Draw in the Age of AI?

I started drawing as a teenager because I loved comics and several people around me worked or had worked in bookstores. I copied everything I could—comic book characters, European BDs. I got good at copying, but without mentors, I eventually plateaued. Then studies took over, professional life began, and I stopped drawing. Here are some of the last drawings made in 1999 : ::Gallery --- photos: - src: "https://writizzy.b-cdn.net/blogs/48b77143-02ee-4316-9d68-0e6e4857c5ce/1762035986119-latlktl.jpg" alt: "Magneto" - src: "https://writizzy.b-cdn.net/blogs/48b77143-02ee-4316-9d68-0e6e4857c5ce/1762035990247-zmqo757.jpg" alt: "Xmen" - src: "https://writizzy.b-cdn.net/blogs/48b77143-02ee-4316-9d68-0e6e4857c5ce/1762035995600-0vi7vkk.jpg" alt: "Witchblade" --- :: ## The golden age of Youtube But in 2018, I thought we were living in amazing times. There were now tons of online resources to learn from. I found hundreds of talented artists on YouTube who reignited my desire to learn. I finally had everything at my fingertips to understand the basics I'd missed when I was younger: the Loomis method, proportions, perspective, inking techniques... So [I started again](https://www.instagram.com/corwinhakanai/). I drew traditionally with pencil, then ink, tried watercolor, and finally digital drawing. I felt steady progress, even though drawing is one of those skills where learning happens in plateaus. There are tough moments where you stagnate, and then something clicks and you leap forward again. ![Moon knight I drew for Inktober 2021](https://writizzy.b-cdn.net/blogs/48b77143-02ee-4316-9d68-0e6e4857c5ce/1762036626305-0pqp4c5.jpg)It was better, but I still I felt limited, especially since I wanted to write stories and tell them through drawings. The gap between being able to reproduce a drawing correctly and bringing a story to life is massive. But that's part of the game. Drawing is learned slowly, in stages. I'd learned a lot, but now I needed to tackle composition rules, master lighting, understand how to structure a story, how to chain shots to guide the reader's eye to the right place, lettering, and many other things. ## And then AI arrived. DALL-E was born in 2021. Midjourney followed in 2022. Today in 2025, these AIs are incredible at creating drawings that I still can't produce after several years of training. A professional will always be better, but only after many, many years of experience. A beginner like me is quickly discouraged by the gap between my current knowledge and the minimum level needed to at least match an AI. That’s what I call **“the AI wall”**. Digital tools got me back into drawing in 2018, but AI cooled my enthusiasm a few years later. I haven't drawn in a year. My last drawing post [on Instagram](https://www.instagram.com/corwinhakanai/) was at the end of 2023. And I'm conflicted. I see people who, on the contrary, found new motivation in it. They couldn't draw but are now able to tell a story in comic form. Overall, I could do that because the story I have in mind could use this medium. You could say it's the story that matters more than the drawing. There are plenty of counter-examples, but let's assume it's true for the sake of argument. Except I don't want to learn how to prompt an AI. It's less exciting than learning to master your line work and thinking about character design or composition. ## What about software development ? And in those moments, I draw a parallel with my job as a developer. I've reached a point where, like the professional artists I admire, AI doesn't scare me too much. I know how to use it as a tool and I'm still capable of doing what it produces. I can go faster with it, but also because I master the equivalent of the basic rules of drawing for my profession. I understand architecture, UX, security, etc. I am above the AI wall and I'm looking down from above. But what about young people entering the tech industry today who face the same AI wall I have to climb as an artist? Who's going to take the time to learn the basics? I was born in the last century. The year 2000 made me dream. There was a kind of technological mythology where we'd end up delegating all the tedious tasks to machines so we could focus on what interests us most: discussion, games, arts, creativity. And I admit I feel like we got a bit lost along the way and did the opposite. I hope I'm wrong. On the other hand, will they be just as important in the future? In the past, driving meant adjusting the dynamo, setting the choke to enrich the air-fuel mixture, cranking a handle to start the engine. I imagine you needed to know much more about mechanics than you do today. But that doesn't stop me from driving today. Cars have evolved. Computing tools are evolving too. Once AI masters all the basic aspects, won't the issue simply be knowing how to properly dialogue with an AI? In the same way that some new comic book authors can produce by prompting an AI. In fact, their job has changed compared to their equivalents from 20 years ago. And it's quite likely that this will be the case for software development in the future.

0 views
Hugo 1 months ago

Having a good customer support isn't optional

I'm one of those people who consider support service crucial. I hate products where customer service is absent, when they give irrelevant answers, or when they half-ass issues by dragging out resolution times hoping you'll just give up. It's a differentiating factor between two products. It can take a tool from "ok tier" to "top tier" just because you have humans who are competent and actually solve your problems. The reverse is true. A good product can become "meh tier" if it's poorly served by its customer service. I talked a while back with [Jonathan Lefèvre](https://jonathanlefevre.com/) who wrote a book about how much of Capitaine Train's strength came from the quality of their support. If you're interested in the book: ["L'obsession du service client"](https://amzn.to/4n0TUge), you know what to do (sorry, it’s in French). Customer service is part of UX in the broadest sense. Many people think UX is just a coat of paint you slap on a website. That's not what UX is - it's the entire user experience: how you're addressed, how your problems are solved, how APIs are designed, the documentation. It's what makes Stripe beloved for dev experience, why Amazon keeps crushing it. Anyway, I care about this stuff. And right now, it's pretty cool - I've had a bunch of recent exchanges on [hakanai.io](http://hakanai.io) with users. And I'm quite proud of the feedback: > I have also looked at other newsletter systems, but yours is the best so far because of the RSS feeds and the ability to edit the email appearance through the MJML coding. > By the way, very good support > Oh, great, let me tell you that this is one of the best supports I have experienced, thank you so much > Hey, just wanted to thank you for this feature, already tested and everything was perfect!!! Will do any future recommendations I see for the platform. Thanks!!!! So yeah. Building a company has ups and downs. This kind of message helps put the rough moments in perspective.

0 views
Hugo 1 months ago

What I Missed Working Solo

Building a product alone is hard. Not exactly breaking news, I know. But I don't think everyone gets why it's actually hard. - Some say it's the financial risk. - Others think it's not having all the necessary skills—development, sales, marketing, etc. - And some imagine it can't scale. I have trouble taking these three obstacles seriously. Developing software doesn't cost much, at least for building a prototype and testing it with small audiences. Scaling is a false problem. There are plenty of examples of solopreneurs making substantial revenue without needing to copy Netflix or Google. And even if it became an issue one day, that's what I'd call a good problem to have. As for learning, with blogs, online videos, and AI, it's now possible to do a lot on your own. But that last point hides another problem. The issue isn't so much not having all the necessary skills, but not having conversations. ## Why I Know This I started working as a freelancer in 2010. And even though I loved it, I quickly connected with a group of Parisian freelancers called "les zindeps." In 2011, I created a kind of freelance collective, an experimental company called Lateral-thoughts. That led to another company I founded in early 2013: Malt. Working with others, at least for me, has always been an accelerator. Every discussion within these different groups created opportunities, little sparks that each lit fires, ultimately leading me to build a company with over 600 people. So, first thing: group work is an echo chamber that stimulates creativity. Working in a group is also motivation to do more. Again, this is obviously personal, but being in a group can make the difference between procrastination and action. I'm sensitive to social pressure, plus constantly being stimulated by new ideas. Finally, working in a group allows you to challenge yourself. You're rarely right on your own. I love those discussions that sometimes challenge certainties, change your mind, and find more inventive solutions. But I became a solopreneur again a year ago. ## Going Solo Again Over the past year, I launched rssfeedpulse, which became blogtally, then [hakanai.io](http://hakanai.io). I'm happy with the result—the product is nice. But let's be honest, it's growing slowly. There are probably many reasons for that. One of them is that working alone, I deprived myself of some creativity, I set fewer constraints for myself, and I had no pushback—either to pivot the product or even kill it. I'm not going to do that. I'm not killing [hakanai.io](http://hakanai.io). On the contrary, I'd like to test other approaches around the same concepts. ## Trying Something Different Not long ago, I talked with [Thomas Sanlis](https://www.thomas-sanlis.com/). He's a solopreneur who's now entered the very exclusive circle of successful solopreneurs with [uneed.best](http://uneed.best), which is gradually becoming a solid alternative to Product Hunt. He also wanted to test other ideas and work with a partner for more stimulation. So we talked and decided to try an experiment together on a new product. I'll focus on what I love most: designing and building products. Thomas will bring his new skills in communication and marketing. Though obviously, given our respective backgrounds, there will be plenty of moments where we'll do a bit of everything. That product is [writizzy](https://writizzy.com). It's a blogging platform, like Ghost or WordPress, but simpler, more modern, and more affordable. It obviously connects to the fact that I've been blogging for 20 years, created an open-source static blog generator called [bloggrify](https://bloggrify.com), and hakanai was already aimed at becoming a toolkit for static blogs. This conversation happened just two weeks ago, the product is already live, and in just two weeks I've already felt the benefits I was talking about—not working alone. We'll see what happens next :)

0 views
Hugo 1 months ago

SEO failure

Well, this is embarrassing. I just realized the SEO for [** hakanai.io **](http://hakanai.io) has been completely broken for months. SEO is theoretically my sole and unique acquisition channel for the SAAS. But: - 10 months after purchasing the domain name, I still have a mediocre ** domain rating **: 14, compared to 36 for Bloggrify and 29 for my own blog. - if I search for the name hakanai, I don't even find [** hakanai.io **](http://hakanai.io) in the search results. - 400 unique visitors per month when I was getting double that at the beginning of the year. That's a 50% drop starting in June - not exactly the growth curve you want to see. Not great... I just discovered that a large part of my pages have been deindexed. I don't know why. I wonder if Google didn't at some point consider the site fraudulent, perhaps because of the pSEO (** programmatic SEO **) that I had implemented with the [** Blog starter Kit **](https://blog-starter-kit.hakanai.io/). It also seems to correspond to the moment I moved the documentation, the blog, and the blog starter kit to subdomains. I made this choice because it was technically simpler and many sources online seemed to say that subdomains and subdirectories were equivalent for SEO. There are quite a few conflicting opinions on this. What is certain is that the current result leans more in favor of subdirectories... Anyway, I'm trying to fix it urgently. I've moved the blog and documentation back to subdirectories. And I set the entire blog starter kit to noindex to see if that changes anything. That was a week ago. For now, no result. It's a bit worrying. Let's see... If you've dealt with Google deindexing issues before, I'm all ears. And if not, well, you'll get to watch me figure it out in real-time. Maybe :)

0 views
Hugo 1 months ago

Hello World

Hello World What better way to start a new blog than with the traditional Hello World that we all learn to code when we start programming? So yes, I'm starting a new blog on Writizzy, because I'm going to eat my own dog food, as they say. My name is Hugo Lassiège. I've been blogging since 2001. I started as a writer on [developpez.com](http://developpez.com), then got my own domain ([hakanai.free.fr](http://hakanai.free.fr)) on the hosting provider everyone in France used back then: [free.fr](http://free.fr). I moved through Joomla, then WordPress. I self-hosted for a while, then got tired of it because WordPress is a pain to maintain long-term and quickly becomes bloated. So I switched to [wordpress.com](http://wordpress.com). But I quickly found it too expensive, and I was looking for a different writing experience. So I built my own static site generator with Nuxt and eventually [open-sourced it as Bloggrify](https://bloggrify.com/). A bit later, I launched [hakanai.io](http://hakanai.io) because, let's face it, a static blog lacks certain features, and I thought I could provide them. It's a SaaS that adds features like newsletters, blog analytics, and I have other ideas to turn it into a toolbox for static blogs. Still, I'm well aware that many people are looking for a much simpler experience. So eventually, after a chance encounter, I decided to start [Writizzy](https://writizzy.com) - the platform you're reading this on - which will be a managed blogging platform representing, I hope, the synthesis of over 20 years of writing blog posts. I'm not abandoning my other blog [eventuallymaking.io](http://eventuallymaking.io) where I'll continue writing more technical articles and sharing experiences from my other life as the founder of [a startup](https://www.malt.com) that did pretty well in France and Europe. This blog will be more of a journal about building in public, thoughts on entrepreneurship, and work-life balance. So, all over the place, it'll cover building Writizzy, Hakanai, Bloggrify. What works, or doesn't. And it won't really be about technical stuff - I save that for my other blog. Instead, I'll talk about experiments and... we'll see. Anyway, welcome to this blog.

0 views