Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/ap/plack-middleware-redirectssl
force all requests to use in-/secure connections
https://github.com/ap/plack-middleware-redirectssl
https perl plack psgi
Last synced: about 2 months ago
JSON representation
force all requests to use in-/secure connections
- Host: GitHub
- URL: https://github.com/ap/plack-middleware-redirectssl
- Owner: ap
- Created: 2012-12-26T01:30:18.000Z (almost 12 years ago)
- Default Branch: master
- Last Pushed: 2022-09-04T13:23:19.000Z (over 2 years ago)
- Last Synced: 2024-10-11T21:15:46.914Z (2 months ago)
- Topics: https, perl, plack, psgi
- Language: Perl
- Homepage: https://metacpan.org/release/Plack-Middleware-RedirectSSL
- Size: 31.3 KB
- Stars: 2
- Watchers: 3
- Forks: 2
- Open Issues: 0
-
Metadata Files:
- Readme: README.pod
- Changelog: Changes
Awesome Lists containing this project
README
use 5.006; use strict; use warnings;
package Plack::Middleware::RedirectSSL;
our $VERSION = '1.302';
BEGIN { require Plack::Middleware; our @ISA = 'Plack::Middleware' }
use Plack::Util ();
use Plack::Util::Accessor qw( ssl hsts_header );
use Plack::Request ();# seconds minutes hours days weeks
sub DEFAULT_STS_MAXAGE () { 60 * 60 * 24 * 7 * 26 }
sub MIN_STS_PRELOAD_MAXAGE () { 60 * 60 * 24 * 365 }sub call {
my ( $self, $env ) = ( shift, @_ );my $do_ssl = $self->ssl ? 1 : 0;
my $is_ssl = ( 'https' eq $env->{'psgi.url_scheme'} ) ? 1 : 0;if ( $is_ssl xor $do_ssl ) {
my $m = $env->{'REQUEST_METHOD'};
return [ 400, [qw( Content-Type text/plain )], [ 'Bad Request' ] ]
if 'GET' ne $m and 'HEAD' ne $m;
my $uri = Plack::Request->new( $env )->uri;
$uri->scheme( $do_ssl ? 'https' : 'http' );
return [ 301, [ Location => $uri ], [] ];
}my $res = $self->app->( $env );
return $res unless $is_ssl and my $hsts = $self->hsts_header;
Plack::Util::response_cb( $res, sub {
Plack::Util::header_set( $_[0][1], 'Strict-Transport-Security', $hsts );
} );
}sub hsts_policy {
my ( $self, $policy ) = ( shift, @_ );
return $self->{'hsts_policy'} unless @_;
$self->hsts_header( render_sts_policy( $policy ) );
$self->{'hsts'} = $policy ? $policy->{'max_age'} || '00' : 0; # legacy compat
$self->{'hsts_policy'} = $policy;
}sub hsts {
my ( $self, $value ) = ( shift, @_ );
return $self->{'hsts'} unless @_;
$self->hsts_policy( ( $value or not defined $value )
? { ( map %$_, $self->{'hsts_policy'} || () ), max_age => $value }
: undef
);
$self->{'hsts'} = $value;
}sub new {
my $self = shift->SUPER::new( @_ );
$self->ssl(1) if not defined $self->ssl;
if ( exists $self->{'hsts_policy'} ) { $self->hsts_policy( $self->{'hsts_policy'} ) }
elsif ( exists $self->{'hsts'} ) { $self->hsts ( $self->{'hsts'} ) }
elsif ( not $self->hsts_header ) { $self->hsts_policy( {} ) }
$self;
}########################################################################
sub _callsite () { my $i; while ( my ( $p, $f, $l ) = caller ++$i ) { return " at $f line $l.\n" if __PACKAGE__ ne $p } '' }
sub render_sts_policy {
my ( $opt ) = @_;die 'HSTS policy must be a single undef value or hash ref', _callsite
if 1 != @_ or defined $opt and 'HASH' ne ref $opt;return undef if not defined $opt;
my @directive = qw( max_age include_subdomains preload );
{
my %known = map +( $_, 1 ), @directive;
my $unknown = join ', ', map "'$_'", sort grep !$known{ $_ }, keys %$opt;
die "HSTS policy contains unknown directive(s) $unknown", _callsite if $unknown;
}my ( $max_age, $include_subdomains, $preload ) = @$opt{ @directive };
$max_age = defined $max_age
? do { no warnings 'numeric'; int $max_age }
: $preload ? MIN_STS_PRELOAD_MAXAGE : DEFAULT_STS_MAXAGE;die 'HSTS max_age 0 conflicts with setting other directives', _callsite
if 0 == $max_age and ( $include_subdomains or $preload );if ( $preload ) {
$include_subdomains = 1 unless defined $include_subdomains;
die 'HSTS preload conflicts with disabled include_subdomains', _callsite unless $include_subdomains;
die "HSTS preload requires longer max_age (got $max_age; minimum ".MIN_STS_PRELOAD_MAXAGE.')', _callsite
if MIN_STS_PRELOAD_MAXAGE > $max_age;
}# expose computed values back to the caller
@$opt{ @directive } = ( $max_age, !!$include_subdomains, !!$preload );join '; ', "max-age=$max_age", ('includeSubDomains') x !!$include_subdomains, ('preload') x !!$preload;
}1;
__END__
=pod
=encoding UTF-8
=head1 NAME
Plack::Middleware::RedirectSSL - force all requests to use in-/secure connections
=head1 SYNOPSIS
# in app.psgi
use Plack::Builder;
builder {
enable 'RedirectSSL';
$app;
};=head1 DESCRIPTION
This middleware intercepts requests using either the C or C scheme
and redirects them to the same URI under respective other scheme.=head1 CONFIGURATION OPTIONS
=over 4
=item C
Specifies the direction of redirects.
If true, requests using C will be redirected to C.
If false, requests using C will be redirected to plain C.Defaults to true if not specified during construction.
=item C
Specifies an arbitrary string value for the C header.
If false, no such header will be sent.=item C
Specifies a value to pass to C>
and updates the C option with the returned value.enable 'RedirectSSL', hsts_policy => { include_subdomains => 1 };
Defaults to an HSTS policy with default values,
which is a C of 26Eweeks and no other directives.=item C
Use of this option is L.
Specifies a C value for the C option,
preserving all other existing C directives, if any.
If undef, uses a C of 26Eweeks.
If otherwise false, sets C to C.
(If you really want a C value of 0, use C<'00'>, C<'0E0'> or C<'0 but true'>.)=back
=head1 FUNCTIONS
=head2 C
Takes either a hash reference containing an HSTS policy or C,
and returns the corresponding C header value.my $policy = { include_subdomains => 1 };
printf "Strict-Transport-Security: %s\n", render_sts_policy $policy;
# Strict-Transport-Security: max-age=15724800; includeSubDomainsAs a side effect, validates the policy and
updates the hash with the ultimate value of every directive after computing defaults.use Data::Dumper; local $Data::Dumper::Terse = 1;
print +Dumper $policy;
# {
# 'max_age' => 15724800,
# 'include_subdomains' => 1,
# 'preload' => ''
# }The following directives are supported:
=over 4
=item C
Integer value for the C directive.
If missing or undefined, it will normally default to 26Eweeks.
But if the C directive is true, it will default to 365Edays
and may not be set to any smaller value.If 0 (which unpublishes a previous HSTS policy), no other directives may be set.
=item C
Boolean; whether to include the C directive.
If missing or undefined, it will normally default to false.
But if the C directive is true, it will defaults to true
and may not be set to false.=item C
Boolean; whether to include the C directive.
=back
=head1 SEE ALSO
=over 4
=item *
L
If your L application runs behind a reverse proxy that unwraps SSL connections
then you will need to put this middleware in front of RedirectSSL.=item *
L6797, I|http://tools.ietf.org/html/rfc6797>
=item *
L
Specification of the C directive
and submission form for inclusion into the Google Chrome preload list
(also used by most other browsers)=back
=cut