Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[11.x] Auto-secure cookies #52422

Merged
merged 2 commits into from
Aug 8, 2024
Merged

[11.x] Auto-secure cookies #52422

merged 2 commits into from
Aug 8, 2024

Conversation

fabricecw
Copy link
Contributor

@fabricecw fabricecw commented Aug 7, 2024

This PR auto-secures cookies as implemented in symfony/symfony#28447. VerifyCsrfToken middleware already auto-secures cookies since it doesn't pass a fallback value false to the Cookie's secure attribute.

By passing null, the secureDefault value from the Cookie takes over.

In the prepare method of Illuminate\Http\Response (Symfony\Component\HttpFoundation\Response) , the cookies inherit the "secure" attribute from the request:

if ($request->isSecure()) {
    foreach ($headers->getCookies() as $cookie) {
        $cookie->setSecureDefault(true);
    }
}

This behavior would be a good balance between making applications "safe by default" and not breaking existing ones. It further provides a solution to the discussion in laravel/laravel#6437

@fabricecw
Copy link
Contributor Author

@valorin referencing to our discussion laravel/laravel#6437, I think the required framework modification is that easy and brings a great improvement in terms of "secure by default". Adding the .env key is no longer required with this solution.

@valorin
Copy link
Contributor

valorin commented Aug 8, 2024

Oh wow, that's all it needs? Nice! 😁

Would it be possible to add a test or two to cover the behaviour? It feels a bit like we're passing responsibility over to Symfony with a magic value, and if their behaviour ever changes, Laravel will just inherit it without notice.

Also, updating the config file to explain null triggers automatic detection could be good too.

@fabricecw
Copy link
Contributor Author

I was surprised as well. 😄

Yes, I went back and forth with tests as it feels a bit redundant to the Symfony tests. But it's probably wise to cover it in the framework as well. Let me add some tests.

@taylorotwell taylorotwell merged commit 6ae8c9a into laravel:11.x Aug 8, 2024
29 checks passed
@valorin
Copy link
Contributor

valorin commented Aug 8, 2024

Nice work @fabricecw, those tests are great. Should let us know if anything breaks. 👍

@@ -224,7 +224,7 @@ protected function addCookieToResponse(Response $response, Session $session)
$this->getCookieExpirationDate(),
$config['path'],
$config['domain'],
$config['secure'] ?? false,
$config['secure'],
Copy link
Contributor

@Jubeki Jubeki Aug 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be better if it has a default value:

$config['secure'] ?? null,

Because if the config key currently does not exist, it would break existing applications. (I think it is present in almost all applications, but the default value was probably there for a reason)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I just thought about it again, the ?? false was there, because of the default null value for the secure key. So should be fine to be left out completely.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Back in the days, Symfony Cookie didn't support null for secure. null was introduced for this "auto-secure" behaviour.

@malohtie
Copy link

malohtie commented Sep 5, 2024

Hi @fabricecw,

I have a question regarding an issue we're facing. Our app uses both HTTP and HTTPS, and some pages are repeatedly prompting users to log in. Could this commit be responsible for the issue?

@fabricecw
Copy link
Contributor Author

fabricecw commented Sep 5, 2024

Hi @fabricecw,

I have a question regarding an issue we're facing. Our app uses both HTTP and HTTPS, and some pages are repeatedly prompting users to log in. Could this commit be responsible for the issue?

If both HTTP and HTTPS requests access the session, yes. The cookie inherits the Secure flag and if a cookie is secured, the browser doesn't pass it in an unencrypted request to the same site.

You have to disable secure cookies as long as you send HTTP and HTTPS requests. Either you do it in the session.php config or in the .env file (set the value to false).

@lanedirt
Copy link

Hi @fabricecw,
I have a question regarding an issue we're facing. Our app uses both HTTP and HTTPS, and some pages are repeatedly prompting users to log in. Could this commit be responsible for the issue?

If both HTTP and HTTPS requests access the session, yes. The cookie inherits the Secure flag and if a cookie is secured, the browser doesn't pass it in an unencrypted request to the same site.

You have to disable secure cookies as long as you send HTTP and HTTPS requests. Either you do it in the session.php config or in the .env file (set the value to false).

Just as an FYI for others: I encountered a similar issue in one of my projects. I was using GuzzleHttp to run custom race condition tests against my app where during the test execution login attempts are made with test user(s).

When my app is configured in production mode, HTTPS is required, and all HTTP requests are automatically redirected. All my test calls for both dev and prod configs were initially made against the same static base URL http://*:80. This setup worked fine for months but suddenly stopped functioning after updating Laravel to 11.21.0. The login part of the test suddenly returned

Client error: `POST http://[app-baseurl]/login` resulted in a `419 unknown status` response:
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width= (truncated...)

My solution was to use a proper https://*:443 base URL for tests when running the app in production mode.

If I understand you correctly the root cause for my issue was the same as what you're describing above: i.e. that certain cookies are now no longer shared between HTTP and HTTPS calls.

@fabricecw
Copy link
Contributor Author

fabricecw commented Sep 29, 2024

@lanedirt

If I understand you correctly the root cause for my issue was the same as what you're describing above: i.e. that certain cookies are now no longer shared between HTTP and HTTPS calls.

The client will not include any Secure cookies in HTTP requests, only in HTTPS requests. In case an application for some reasons have to support mixed requests (HTTP and HTTPS) with the same cookies, the secure parameter in session.php needs to be false.

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.

6 participants