0x55aa
โ† Back to Blog

I Accidentally Found SQL Injection in Laravel (While Procrastinating) ๐Ÿ˜…

โ€ข9 min read

I Accidentally Found SQL Injection in Laravel (While Procrastinating) ๐Ÿ˜…

TL;DR: I was supposed to be building a "revolutionary" framework with DDD, TDD, Clean Architecture, and all the fancy buzzwords. Instead, I found SQL injection bugs in Laravel and somehow got my fix merged. My mom still doesn't understand what I do for a living.

This is the story of how procrastination, curiosity, and too much chai led to an accidental security contribution.

The Setup: Every Developer's Famous Last Words ๐ŸŽฌ

"I'm gonna build my own framework!"

Yeah, I said it. We all say it at some point, right?

There I was, trying to create the "perfect" base framework with:

  • Domain-Driven Design (because monoliths are for peasants)
  • Test-Driven Development (write tests first, they said, it'll be fun, they said)
  • Clean Architecture (Uncle Bob would be proud!)
  • All the patterns (Factory, Repository, Strategy, Singleton, you name it)

Spoiler: I didn't finish the framework. But I did find something interesting! ๐Ÿ˜‚

The Accidental Discovery ๐Ÿ•ต๏ธ

So I'm building this query builder for my framework (because obviously I need to reinvent that wheel too), and I'm like:

"Let me see how Laravel does it. Just for inspiration. Not copying, I swear."

I open up MySqlGrammar.php and I see this:

public function compileRandom($seed)
{
    return 'RAND('.$seed.')';
}

Me: ๐Ÿค” "Wait... that's just string concatenation?"

Also me: ๐Ÿคจ "That can't be right. Laravel is used by millions. They must have validation somewhere else."

Narrator: They did not have validation somewhere else.

The "Oh No" Moment ๐Ÿ’ก

I started testing my own framework's query builder against SQL injection (because TDD, right?) and thought:

"Let me make sure Laravel isn't vulnerable to the same thing. Just to be safe."

Turns out... it was.

Two methods, actually:

Bug #1: inRandomOrder($seed)

// This seemed fine...
DB::table('posts')->inRandomOrder(42)->get();

// Until I tried THIS:
$seed = "1) AND EXTRACTVALUE(1,CONCAT(0x7e,DATABASE()))--";
DB::table('posts')->inRandomOrder($seed)->get();

Result: Hello, database name! ๐Ÿ‘‹

Me: ๐Ÿ˜ณ "Okay, that's... not good."

Bug #2: forceIndex($index)

// Normal use case
DB::table('users')->forceIndex('email_idx')->get();

// What if someone passes user input?
$index = "users) UNION SELECT password FROM admin--";
DB::table('users')->forceIndex($index)->get();

Result: All your passwords are belong to us. ๐Ÿ’€

Me: ๐Ÿ˜ฐ "Oh crap. OH CRAP."

The Panic Phase ๐Ÿ˜จ

Now, normal people would:

  1. Report it responsibly
  2. Wait for a response
  3. Move on with their lives

Me? I went through these stages:

Stage 1: Denial "Nah, I must be doing it wrong. Laravel has millions of users!"

Stage 2: More Testing Tests 20 different payloads All work

Stage 3: Imposter Syndrome "I'm a nobody. Who am I to find bugs in Laravel?"

Stage 4: Responsible Adult "Okay, I need to report this properly."

Stage 5: Procrastination "But what if they laugh at me?"

Stage 6: Finally Reporting It "Here goes nothing..."

The Responsible Disclosure (AKA: Scary Email Time) ๐Ÿ“ง

January 16, 2026, 2:37 AM (because of course it's 2 AM)

I submitted a security advisory through GitHub. My hands were literally shaking. The email basically said:

"Hi, uh, I think I found something? Maybe? I'm probably wrong but here's what I found. Sorry if this is dumb. Please don't hate me."

My actual report was more professional. But that's what it felt like.

What I included:

  • The two vulnerable methods
  • Proof of concept code
  • Attack scenarios
  • A suggested fix (because if I'm reporting it, might as well help fix it)

Advisory ID: GHSA-9p82-4j4w-5hw8

Then I waited. And waited. And refreshed my email 847 times.

The Response (They Didn't Laugh!) ๐Ÿ˜…

3 days later, I got this:

"Thank you very much for your vulnerability report. We appreciate your commitment to responsible disclosure. We take all security reports seriously."

My reaction:

  1. They responded! ๐ŸŽ‰
  2. They took it seriously! ๐ŸŽ‰๐ŸŽ‰
  3. They didn't call me an idiot! ๐ŸŽ‰๐ŸŽ‰๐ŸŽ‰

They reviewed it, confirmed it was real, and started working on a fix.

The crazy part? They asked if I wanted to submit a patch.

Me: "Wait, you want MY code in Laravel? LARAVEL LARAVEL?"

The Fix (My Code in Laravel?!) ๐Ÿ› ๏ธ

Here's what I submitted:

public function compileRandom($seed)
{
    // Validate it's actually a number
    if (!is_numeric($seed)) {
        throw new InvalidArgumentException('Seed must be numeric');
    }

    // Cast to int for extra safety
    return 'RAND('.((int) $seed).')';
}

Simple fix. But it works.

They reviewed it, made some tweaks, and merged it!

Commit: 1dcf0b3 Version: Laravel 12.48.0 My name: Right there in the commit!

My mom called and asked if I was famous now. I said "kinda?" She still doesn't get it. ๐Ÿ˜‚

The Plot Twist: It's Not Really a Bug? ๐Ÿค”

After merging the fix, Laravel's team sent me this:

"We've decided not to issue a CVE. These methods aren't meant to receive user input directly. The vulnerability only exists if developers don't validate input first."

Translation: "Your fix is good, but developers shouldn't be passing raw user input to database methods anyway."

And they're right!

This is like finding out your car's gas tank isn't explosion-proof... but realizing you shouldn't be throwing matches at it in the first place. ๐Ÿ˜…

Bad code that would trigger this:

// Don't do this. Ever.
$seed = $request->input('seed');
DB::table('posts')->inRandomOrder($seed)->get();

Good code:

// Do this instead
$allowedSeeds = [1, 2, 3, 42];
$seed = $request->input('seed');

if (!in_array($seed, $allowedSeeds, true)) {
    abort(400, 'Invalid seed');
}

DB::table('posts')->inRandomOrder($seed)->get();

What I Actually Learned ๐Ÿ“š

Lesson 1: Procrastination Can Be Productive

Was I supposed to be building my framework? Yes. Did I get distracted reading Laravel's source? Also yes. Did it work out? Surprisingly yes!

Conclusion: Productive procrastination is still procrastination, but with better stories.

Lesson 2: Imposter Syndrome Is a Liar

I almost didn't report it because "who am I to find bugs in Laravel?"

Reality check: Even the best codebases have bugs. Finding them doesn't make you special, it makes you lucky. Reporting them makes you helpful.

Lesson 3: The Real Lesson (Boring But Important)

NEVER. PASS. RAW. USER. INPUT. TO. DATABASE. METHODS.

I don't care if it's Laravel, my framework, or your grandma's PHP scripts from 2005.

// โŒ BAD - How to get hacked
$sort = $_GET['sort'];
DB::table('users')->orderBy($sort)->get();

// โœ… GOOD - How to keep your job
$allowed = ['name', 'email', 'created_at'];
$sort = in_array($_GET['sort'], $allowed) ? $_GET['sort'] : 'name';
DB::table('users')->orderBy($sort)->get();

It's not rocket science. It's just validation.

The "Achievement" (I Guess?) ๐Ÿ†

Look, I'm not gonna lie and say this doesn't look good on a resume. It does.

What I got:

  • My name in Laravel's commit history (proof)
  • A cool story for interviews
  • Validation that reading source code is useful
  • Street cred with exactly 3 of my developer friends

What I didn't get:

  • Money (Laravel has no bug bounty)
  • Fame (my mom still doesn't get it)
  • A job offer from Laravel (a guy can dream)
  • My framework finished (still procrastinating on that)

Was it worth it? Hell yeah!

My "Revolutionary" Framework Status: Still Not Done ๐Ÿ˜‚

Remember that DDD/TDD/Clean Architecture framework I was building?

Current status:

  • โœ… Has a cool name
  • โœ… Has a GitHub repo
  • โŒ Has literally zero code
  • โœ… Has a bunch of TODO comments
  • โœ… Helped me find bugs in Laravel (task failed successfully!)

I'll finish it someday. Maybe. Probably not. Who am I kidding? ๐Ÿคทโ€โ™‚๏ธ

Actual Practical Advice ๐Ÿ’ก

If You're Building Your Own Framework:

  1. Don't (use Laravel, it's good!)
  2. But if you must, read other frameworks' source code
  3. Test EVERYTHING for SQL injection
  4. Use parameterized queries
  5. Validate ALL user input

If You're Using Laravel (or any framework):

Stop doing this:

// This is how you lose your job
$userInput = $request->input('column');
DB::table('users')->orderBy($userInput)->get();

Start doing this:

// This is how you keep your job
$allowedColumns = ['name', 'email', 'created_at'];
$column = in_array($request->input('column'), $allowedColumns)
    ? $request->input('column')
    : 'created_at';
DB::table('users')->orderBy($column)->get();

If You Find a Security Bug:

  1. Don't panic (I panicked, learn from my mistakes)
  2. Report it privately (GitHub Security Advisories for public repos)
  3. Give them time to fix it (don't tweet about it immediately)
  4. Be nice (maintainers are people too)
  5. Don't expect money (but recognition is cool)

Resources (Boring But Useful) ๐Ÿ“–

Report Laravel security issues:

My commit in Laravel:

Learn more about SQL injection:

The Bottom Line ๐ŸŽฏ

I set out to build a framework. I ended up finding bugs in Laravel instead.

Lessons learned:

  • Procrastination can be productive
  • Imposter syndrome is dumb
  • Always validate user input
  • Reading source code is underrated
  • My framework is never getting finished

Would I do it again? Absolutely. Finding this bug was more educational than any tutorial.

Will I finish my framework? Check back in 5 years. Maybe. No promises.


Connect & Stuff ๐Ÿค

If you enjoyed this story of accidental success:

๐Ÿ”— LinkedIn: linkedin.com/in/anuraghkp ๐Ÿ‘จโ€๐Ÿ’ป GitHub: github.com/kpanuragh ๐Ÿ”’ The Actual Commit: See my name in Laravel!

Questions? DMs are open. Just don't ask when my framework will be ready. ๐Ÿ˜…


Update Timeline โฐ

Jan 16, 2026: Accidentally found bugs while "researching" for my framework Jan 16, 2026, 2:37 AM: Finally got courage to report it (GHSA-9p82-4j4w-5hw8) Jan 19, 2026: Laravel responded (didn't laugh at me!) Jan 20, 2026: My patch merged (Commit 1dcf0b3) Jan 30, 2026: Writing this blog instead of finishing my framework Version fixed: Laravel 12.48.0+


Remember: The best code you'll ever write is code that makes someone else's code better. Even if you found it by accident while procrastinating. ๐Ÿ˜Š

P.S. - If anyone wants to collaborate on that DDD/TDD/Clean Architecture framework... just kidding, I'm never finishing it. But the dream lives on! ๐Ÿ˜‚

P.P.S - Validate your damn inputs.