Skip to content

Refuse to set signed or encrypted cookies with an insecure default secret #2252

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

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

Grinnz
Copy link
Contributor

@Grinnz Grinnz commented May 20, 2025

Summary

Rough attempt at taking the approach of dying if encrypted or signed cookies are requested to be set but the application secret is still set to the default of the application moniker.

This is fairly straightforward to do in the signed_cookie and encrypted_cookie methods, but this also requires a check in the session method, because setting the session cookie occurs too late to error out sensibly.

I'm not satisfied with this check in the session method, because: there still may be instances where this session method is called while a response is already being rendered; and session data can be set by assigning to $c->session->{foo} and there's no way to know that will happen at this juncture.

Additionally, this check does not help cases where the secret has been set to a value that is also insecure but is not the current application's moniker, such as with Mojolicious::Plugin::Mount.

I am not sure if these issues can be solved, but wanted to put this idea into practice for others to review.

This also affects use of features using the session implicitly, such as the flash and csrf_token default helpers.

The MOJO_ALLOW_INSECURE_SECRET env variable is provided as a bypass option for applications that can't be made secure for whatever reason.

Motivation

The risk of insecure secrets being used in applications using signed or encrypted cookies is not sufficiently mitigated by the annoying warning in the logs. But we don't want to cause applications to fail if they are not using these features.

References

Implements a part of #2200 by refusing to set signed or encrypted cookies by default unless the secret has been changed from the default.

@kraih
Copy link
Member

kraih commented May 23, 2025

It seems a little overengineered, but i also don't know a better solution.

my $secret = $app->secrets->[0];
my $moniker = $app->moniker;

Carp::croak 'Your secret passphrase must be changed to set session data (see FAQ for more)'
Copy link

Choose a reason for hiding this comment

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

I don't think these secrets are "passphrase"s (and the FAQ doesn't call them that either)

my $moniker = $app->moniker;

Carp::croak 'Your secret passphrase must be changed to set signed cookies (see FAQ for more)'
if !$ENV{MOJO_ALLOW_INSECURE_SECRET} and (!length $secret or $secret eq $moniker);
Copy link

Choose a reason for hiding this comment

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

Would it make sense to move this check into $app->session?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is no session method for Mojolicious; that would be calling the session helper, which just forwards to the session method on the controller which is handled above (albeit with some issues as described in the initial comment).

Copy link

Choose a reason for hiding this comment

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

yes, sorry, github threw away my comment, and i crankly re-typed it wrong.

I mean should it be in $app->secrets

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That attribute may be accessed by applications that don't actually do anything with it, such as Mojolicious::Plugin::Mount. But perhaps we can work around that more easily than the problems with this approach.

@kraih kraih requested a review from Copilot June 16, 2025 13:24
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR introduces stricter checks to prevent the setting of signed or encrypted cookies when using insecure application secrets by comparing the secret with the application moniker. It also updates several test applications to use dynamically generated secrets.

  • Updates tests in multiple files to use generate_secret for secure default secrets.
  • Adds runtime checks in the Controller methods (encrypted_cookie, session, signed_cookie) to enforce secret validation.
  • Enhances documentation to clearly indicate the security requirement and the bypass option.

Reviewed Changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
t/mojolicious/validation_lite_app.t Updated to use generate_secret for secure defaults
t/mojolicious/session_lite_app.t Added subtests for insecure secret handling in session contexts
t/mojolicious/rebased_lite_app.t Updated to use generate_secret for secure defaults
t/mojolicious/longpolling_lite_app.t Updated to use generate_secret for secure defaults
t/mojolicious/embedded_lite_app.t Updated to use generate_secret for secure defaults
t/mojolicious/app.t Updated to use generate_secret for secure defaults
lib/Mojolicious/Controller.pm Added security checks and documentation updates in cookie methods
Comments suppressed due to low confidence (1)

lib/Mojolicious/Controller.pm:63

  • [nitpick] Consider refactoring the duplicate insecure-secret checks (found in encrypted_cookie, session, and signed_cookie methods) into a common helper function to improve maintainability and reduce redundancy.
Carp::croak 'Your secret passphrase must be changed to set encrypted cookies (see FAQ for more)' if !$ENV{MOJO_ALLOW_INSECURE_SECRET} and (!length $secret or $secret eq $moniker);

my $session = $stash->{'mojo.session'} //= {};

# Require changed secret if session data will be set
my $set_operation = !!(@_ > 1 || ref $_[0]);
Copy link
Preview

Copilot AI Jun 16, 2025

Choose a reason for hiding this comment

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

[nitpick] The use of double negation (!!) can reduce readability; consider using an explicit boolean expression to check for a set operation.

Suggested change
my $set_operation = !!(@_ > 1 || ref $_[0]);
my $set_operation = (@_ > 1 || ref $_[0]) ? 1 : 0;

Copilot uses AI. Check for mistakes.

subtest 'Insecure secret' => sub {
subtest 'Insecure secret (signed cookie)' => sub {
$t->reset_session;
$t->app->secrets([app->moniker]);
Copy link
Preview

Copilot AI Jun 16, 2025

Choose a reason for hiding this comment

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

[nitpick] For clarity and consistency with other parts of the code, consider using $t->app->moniker instead of app->moniker when setting insecure secrets in tests.

Suggested change
$t->app->secrets([app->moniker]);
$t->app->secrets([$t->app->moniker]);

Copilot uses AI. Check for mistakes.

Copy link
Member

Choose a reason for hiding this comment

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

This one is actually good!

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