Security Tip: Fix Your Leaky APIs!
[Tip#70] This is your periodic reminder to check your app for any leaky APIs and fix them ASAP, otherwise you might end up with an email from Have I Been Pwned's Troy Hunt...

We’ve covered this topic before, with Sensitive Model Attributes, and Leaking Data After Changes, but in light of the Spoutible data breach, which was loaded into Have I Been Pwned (HIBP), I felt it was a good time for a reminder.
Firstly, if you’re unfamiliar with the breach, Troy Hunt has written up a great post outlining what happened, and I highly recommend you give it a read. It’s one of these stories that just keeps getting worse and worse, the more you read!
Regarding the actual data breach, here’s the summary from HIBP:
In January 2024, Spoutible had 207k records scraped from a misconfigured API that inadvertently returned excessive personal information. The data included names, usernames, email and IP addresses, phone numbers (where provided to the platform), genders and bcrypt password hashes. The incident also exposed 2FA secrets and backup codes along with password reset tokens.
Yes, you read that right: Spoutible had an API endpoint which returned:
- names
- usernames
- email addresses
- IP addresses
- phone numbers (if provided)
- genders
- bcrypt password hashes
- 2FA secrets
- 2FA backup codes
- password reset tokens
- And more…
We’re talking a whole boatload of Personally Identifiable Information (PII) and Authentication Data, which would be incredibly useful for stealing identities, hijacking user accounts, and a bunch of other malicious activities! The sheer amount of data available in this breach, especially via an API like this, is rarely seen.
While Troy’s post doesn’t answer the “Why was this data available?” question, it’s pretty easy to assume that the folks who built the API did something like this:
class User
{
public function index()
{
return User::all();
}
public function show(User $user)
{
return $user;
}
}
Unless protections are put in place, these controller actions will return everything on the user model. This may include all sorts of PII and auth data, such as what happened with Spoutible. That said, Laravel does give us some tools and helpers for avoiding this in our own apps, so protecting against it is easy - as long as you’re intentional with your data and your APIs.
Here are my recommendations:
- Use
$hidden
to protect sensitive attributes from being returned viatoArray()
, andtoJson()
, etc.
See: Security Tip: Sensitive Model Attributes - Use
only()
on the model or collection to limit the returned attributes on a per-request basis.
See also: Security Tip: Leaking Data After Changes - Use API Resources and manually define each returned model attribute.
Don’t forget: This doesn’t just affect public APIs, but it can also occur for internal APIs used within SPAs, or even components that serialise model data into attributes. Any time you’re sending data to the browser or over an API, be aware of who can access it and what data is being sent!
If you found this security tip useful, subscribe to get weekly Security Tips straight to your inbox. Upgrade to a premium subscription for exclusive monthly In Depth articles, or drop a coin in the tip jar to show your support.
When was the last time you had a penetration test? Book a Laravel Security Audit and Penetration Test, or a budget-friendly Security Review!
You can also connect with me on Bluesky, or other socials, and check out Practical Laravel Security, my interactive course designed to boost your Laravel security skills.