Skip to content

Conversation

@ahmad-cit22
Copy link
Contributor

@ahmad-cit22 ahmad-cit22 commented Oct 23, 2025

Optional $securityCheck parameter for the previousPath() method

This PR brings a security enhancement to the previousPath() method in UrlGenerator with better security check and origin validation, which resolves the issue #57456.
This is an improved version of the PR #57487 - as @taylorotwell stated that this change can be a breaking change for some devs, so now I developed it as an opt-in feature with a new parameter $securityCheck and I am submitting this into the master branch as a a security enhancement/feature for the next major release v13. Details are below.

Issue Summary

Screenshot 2025-10-22 at 1 57 03 PM

The previousPath() method is expected to return only the path of the previous URL (the doc SS attached for clarity). However, the existing implementation in previousPath() method returns the URL, when the request origin is different from the app origin. This happened because it extracted the path from the previous URL without checking that the request originated from the same origin or not and the path extracting logic only worked for the same origin requests. So, this was making applications open to potential security issues including:

  • Open redirect attacks via external domains in the referrer header
  • XSS attacks through dangerous URI schemes (javascript:, data: etc)
  • Protocol confusion attacks using different schemes (http vs https)

And considering these potential security implications of returning the full external URL, I brought this change as a security enhancement to provide developers with more control over the previous URL path.

Solution in Brief

I have added a new parameter $securityCheck to the previousPath() method, which when set to true, will enable the security checks to validate the origin of the previous URL and also check if that URL path is dangerous or not. If the origin is different from the app origin or detects as dangerous, the method will return the fallback value or the root path (/) instead of the previous URL path.
And, if the parameter is set to false (which I kept as the default value), the method will work exactly as it did before, returning the previous URL path like before without any security checks, thus bringing no change in the behavior.

So, this implementation provides a fully backward-compatible, and opt-in security enhancement for the previousPath() method:

  1. Backward Compatibility: The previousPath() method maintains the existing/old behavior by default - no breaking changes.
  2. Opt-in Security: Added an optional $securityCheck parameter (previousPath($fallback = false, $securityCheck = false))
  3. Enhanced Security Checks (when enabled):
    • Origin Validation: Same-origin checking with host matching (case-insensitive), scheme consistency, and port validation
    • Dangerous URL Detection: Protection against malicious URI schemes (javascript:, data:, file:), when origin is different from the app origin.
    • Updated URL Parsing: Uses built-in parse_url() for reliability

New Changes Benefits

  • Zero Breaking Changes: Existing code continues to work exactly as before
  • Opt-in Security: If developers want, they can choose when to enable security checks via the $securityCheck parameter
  • Comprehensive Protection: When enabled, prevents open redirect vulnerabilities, XSS attacks, and protocol confusion attacks
  • Future-Proof: Provides a migration path for future Laravel versions where security could become the default
  • Better DX: Developers can rely on the method to get a more secure previous path, when they enable the security checks.

Test Coverage

Added comprehensive tests covering both backward compatibility and security modes:

Backward Compatibility Tests:

  • Existing behavior preserved for all current use cases
  • External domains, different schemes, and dangerous URIs work as before in the default mode
  • No chance of breaking changes for existing applications using the method

Security Mode Tests:

  • Same-origin URL validation and external domain blocking
  • Dangerous URI scheme detection (javascript:, data:, file:)
  • Port-based and protocol confusion attack prevention
  • Empty/ null referrer handling and case-insensitive host validation
  • Complex query strings, URL encoding, and special characters
  • Fallback behavior verification and path extraction accuracy

Implementation Details

As stated above, when the optional $securityCheck parameter is enabled by devs, then the method applies extra security validations while maintaining the same API contract.

Usage Examples:

// Default behavior (backward compatible)
$path = $url->previousPath(); // securityCheck = false by default and the logic remains the same as before

// with security checks enabled
$path = $url->previousPath(fallback: '/', securityCheck: true);

Note: This change is designed for the master branch (next major release) to provide developers time to adopt the security enhancements. The opt-in approach ensures no immediate breaking changes while offering a clear migration path for enhanced security and better DX. Hope this helps the devs in their future projects.

Let me know if you need any further clarification or tests from my side or if u have any suggestions to improve my approach. Thank you.

@ahmad-cit22
Copy link
Contributor Author

the styleCI indicating a config issue "The 'nullable_type_declarations' fixer cannot be enabled again because it has already been enabled" - ig this is related to the .styleci.yml. please let me know if I have anything to do from my side regarding the failing checks.

@taylorotwell
Copy link
Member

Parameter isn't intuitive.

@garygreen
Copy link
Contributor

The UrlGenerator class is used to generate urls relative to the configured app.url - so it is a bit odd that you can pass a fully qualified url to various functions and it'll passthru without doing anything, e.g. url()->to('http://evil.com') - the issue is if people don't sanitize user input, and especially if they are unware that referer is used in some cases for generating (e.g. previous*(), it could expose to vulnerable attacks.

Rather than a new parameter, maybe this would be a better option?

// Assuming `app.url` is configured to: http://safe.com
url()->disableStrictOrigin();
url()->enableStrictOrigin();

url()->to('http://evil.com'); // 🔥
url()->previous(); // 🔥 if referer is http://evil.com
url()->previousPath(); // 🔥 if referer is http://evil.com

It could throw a 🔥 Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException

@ahmad-cit22 ahmad-cit22 deleted the enhancement/add-security-enhancement-to-previousPath-method branch October 23, 2025 17:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants