Security Advisories (12)
CPANSA-Mojolicious-2015-01 (2015-02-02)

Directory traversal on Windows

CPANSA-Mojolicious-2014-01 (2014-10-07)

Context sensitivity of method param could lead to parameter injection attacks.

CVE-2011-1841 (2011-03-10)

Mojolicious is vulnerable to cross-site scripting, caused by improper validation of user-supplied input by link_to helper. A remote attacker could exploit this vulnerability using a specially-crafted URL to execute script in a victim's Web browser within the security context of the hosting Web site, once the URL is clicked. An attacker could use this vulnerability to steal the victim's cookie-based authentication credentials.

CVE-2011-1589 (2011-04-05)

Directory traversal vulnerability in Path.pm in Mojolicious before 1.16 allows remote attackers to read arbitrary files via a %2f..%2f (encoded slash dot dot slash) in a URI.

CVE-2011-1841 (2011-05-03)

Cross-site scripting (XSS) vulnerability in the link_to helper in Mojolicious before 1.12 allows remote attackers to inject arbitrary web script or HTML via unspecified vectors.

CPANSA-Mojolicious-2022-03 (2022-12-10)

Mojo::DOM did not correctly parse <script> tags.

CPANSA-Mojolicious-2021-02 (2021-06-01)

Small sessions could be used as part of a brute-force attack to decode the session secret.

CVE-2021-47208 (2021-03-16)

A bug in format detection can potentially be exploited for a DoS attack.

CPANSA-Mojolicious-2018-03 (2018-05-19)

Mojo::UserAgent was not checking peer SSL certificates by default.

CPANSA-Mojolicious-2018-02 (2018-05-11)

GET requests with embedded backslashes can be used to access local files on Windows hosts

CVE-2018-25100 (2018-02-13)

Mojo::UserAgent::CookieJar leaks old cookies because of the missing host_only flag on empty domain.

CVE-2024-58134 (2025-05-03)

Mojolicious versions from 0.999922 for Perl uses a hard coded string, or the application's class name, as an HMAC session cookie secret by default. These predictable default secrets can be exploited by an attacker to forge session cookies.  An attacker who knows or guesses the secret could compute valid HMAC signatures for the session cookie, allowing them to tamper with or hijack another user’s session.

NAME

Mojolicious::Guides::Growing - Growing

OVERVIEW

Starting a Mojolicious::Lite prototype from scratch and growing it into a well structured Mojolicious application.

CONCEPTS

Essentials every Mojolicious developer should know.

Model View Controller

MVC is a software architectual pattern for graphical user iterface programming, originating in Smalltalk-80 that separates application logic, presentation and input.

           .------------.    .-------.    .------.
Request -> | Controller | -> | Model | -> | View | -> Response
           '------------'    '-------'    '------'

A slightly modified version of the pattern moving some application logic into the controller is the foundation of pretty much every web framework these days.

            .----------------.     .-------.
Request  -> |                | <-> | Model |
            |                |     '-------'
            |   Controller   |
            |                |     .-------.
Response <- |                | <-> | View  |
            '----------------'     '-------'

The controller receives a request from a user, passes incoming data to the model and retrieves outgoing data from it, which then gets turned into an actual response by the view. It's important to consider that this pattern is just a guideline that most of the time results in cleaner more maintainable code, but not a rule that should be followed at all costs.

PROTOTYPE

One of the main differences between Mojolicious and other web frameworks is that it also includes Mojolicious::Lite, a micro web framework optimized for rapid prototyping.

Foundation

A Mojolicious::Lite protoype often consists of just a single executable Perl script like myapp.pl.

% mkdir myapp
% cd myapp
% touch myapp.pl
% chmod 744 myapp.pl

This will be the foundation for our login manager example application.

use Mojolicious::Lite;

get '/' => sub {
    my $self = shift;
    $self->render(text => 'Hello world!');
};

app->start;

The built in web server makes working on your application a lot of fun thanks to automatic reloading.

% ./myapp.pl daemon --reload
Server available at http://*:3000.

Just save your changes and they will be automatically in effect the next time you refresh your browser.

Model

In Mojolicious we consider web applications simple frontends for existing application logic, that means Mojolicious is entirely model layer agnostic and you just use whatever Perl modules you like most.

% mkdir lib
% touch lib/MyUser.pm
% chmod 644 lib/MyUser.pm

Our login manager will simply use a normal Perl module abstracting away all logic related to matching user names and passwords.

package MyUsers;

use strict;
use warnings;

my $USERS = {
    sri => 'secr3t',
    vti => 'lulz',
    yko => 'zeecaptain'
};

sub new { bless {}, shift }

sub check {
    my ($self, $user, $pass) = @_;

    # Success
    return 1 if $USERS->{$user} && $USERS->{$user} eq $pass;

    # Fail
    return;
}

1;

The defaults method of the application can be used to make our model available to all actions through the stash.

use Mojolicious::Lite;

use lib 'lib';
use MyUsers;

app->defaults(users => MyUsers->new);

# GET /?user=sri&pass=secr3t
any '/' => sub {
    my $self = shift;

    # Query parameters
    my $user = $self->param('user');
    my $pass = $self->param('pass');

    # Check password
    return $self->render(text => "Welcome $user!")
      if $self->stash('users')->check($user, $pass);

    # Failed
    $self->render(text => 'Wrong username or password!');
};

app->start;

The param method of our Mojolicious::Controller instance is used to access query parameters, POST parameters and route placeholders, all at once.

Testing

Test driven development is a very powerful tool for designing good APIs, because you are building a use case before the actual implementation.

% mkdir t
% touch t/login.t
% chmod 644 t/login.t

Test::Mojo is a scriptable user agent designed specifically for testing with many fun state of the art features such as CSS3 selectors.

use Test::More tests => 16;
use Test::Mojo;

# Include application
use FindBin;
$ENV{MOJO_HOME} = "$FindBin::Bin/../";
require "$ENV{MOJO_HOME}/myapp.pl";

my $t = Test::Mojo->new(max_redirects => 1);

# GET / (login form)
$t->get_ok('/')->status_is(200)
  ->element_exists('form input[name="user"]')
  ->element_exists('form input[name="pass"]')
  ->element_exists('form input[type="submit"]');

# POST / (submit login form)
$t->post_form_ok('/' => {user => 'sri', pass => 'secr3t'})
  ->status_is(200)->text_like('html body' => qr/Welcome sri/);

# GET /protected (member only page)
$t->get_ok('/protected')->status_is(200)->text_like('a' => qr/Logout/);

# GET /logout (back to the login form)
$t->get_ok('/logout')->status_is(200)
  ->element_exists('form input[name="user"]')
  ->element_exists('form input[name="pass"]')
  ->element_exists('form input[type="submit"]');

From now on you can always check your progress by running these unit tests against your application.

% ./myapp.pl test
...
% perl t/login.pl
...

To make the test less noisy and limit log output to just error messages you could add a line like this.

$t->app->log->level('error');

You can also make quick GET requests from the command line.

% ./myapp.pl get /
Wrong username or password!

% ./myapp.pl get -v /?user=sri&pass=secr3t
HTTP/1.1 200 OK
Connection: Keep-Alive
Date: Sun, 18 Jul 2010 13:09:58 GMT
Server: Mojolicious (Perl)
Content-Length: 12
Content-Type: text/plain

Welcome sri!

Final Prototype

A final prototype for our login manager could look like this.

use Mojolicious::Lite;

use lib 'lib';
use MyUsers;

app->defaults(users => MyUsers->new);

any '/' => sub {
    my $self = shift;

    # Query or POST parameters
    my $user = $self->param('user') || '';
    my $pass = $self->param('pass') || '';

    # Check password
    return $self->render
      unless $self->stash('users')->check($user, $pass);

    # Store user name in session
    $self->session(user => $user);

    # Store a friendly message for the next page in flash
    $self->flash(message => 'Thanks for logging in!');

    # Redirect to protected page
    $self->redirect_to('protected');
} => 'index';

get '/protected' => sub {
    my $self = shift;

    # Redirect to main page if user is not logged in
    return $self->redirect_to('index') unless $self->session('user');
} => 'protected';

get '/logout' => sub {
    my $self = shift;

    # Expire and in turn clear session automatically
    $self->session(expires => 1);

    # Redirect to main page
    $self->redirect_to('index');
} => 'logout';

app->start;
__DATA__

@@ layouts/default.html.ep
<!doctype html><html>
    <head><title>Login Manager</title></head>
    <body><%= content %></body>
</html>

@@ index.html.ep
% layout 'default';
<%= form_for index => {%>
    <% if (param 'user') { %>
        <b>Wrong name or password, please try again.</b><br />
    <% } %>
    Name:<br />
    <%= input user => (type => 'text') %><br />
    Password:<br />
    <%= input pass => (type => 'text') %><br />
    <input type="submit" value="Login" />
<%}%>

@@ protected.html.ep
% layout 'default';
<% if (my $message = flash 'message' ) { %>
    <b><%= $message %></b><br />
<% } %>
Welcome <%= session 'user' %>!<br />
<%= link_to logout => {%>
    Logout
<%}%>