feat: use secure RNG for short codes

This commit is contained in:
2025-12-29 15:08:35 +01:00
parent e2c4916565
commit eb4c4e4c4c

View File

@@ -3,6 +3,7 @@ package Urupam::URL;
use Mojo::Base -base; use Mojo::Base -base;
use Mojo::Promise; use Mojo::Promise;
use Mojo::Util qw(b64_encode); use Mojo::Util qw(b64_encode);
use Bytes::Random::Secure qw(random_string_from random_bytes);
has db => sub { die 'db attribute required' }; has db => sub { die 'db attribute required' };
@@ -32,11 +33,7 @@ sub _validate_url {
sub _generate_random_code { sub _generate_random_code {
my ($self) = @_; my ($self) = @_;
my $code = ''; return random_string_from( $CHARSET, $CODE_LENGTH );
for ( 1 .. $CODE_LENGTH ) {
$code .= substr( $CHARSET, int( rand( length($CHARSET) ) ), 1 );
}
return $code;
} }
sub _generate_code_from_url { sub _generate_code_from_url {
@@ -55,11 +52,25 @@ sub _generate_code_from_url {
} }
my $max_start = length($encoded) - $CODE_LENGTH; my $max_start = length($encoded) - $CODE_LENGTH;
my $start_pos = int( rand( ( $max_start > 0 ? $max_start : 0 ) + 1 ) ); my $range = ( $max_start > 0 ? $max_start : 0 ) + 1;
my $start_pos = $self->_secure_int($range);
return substr( $encoded, $start_pos, $CODE_LENGTH ); return substr( $encoded, $start_pos, $CODE_LENGTH );
} }
sub _secure_int {
my ( $self, $max ) = @_;
return 0 unless defined $max && $max > 1;
my $limit = int( 0xFFFFFFFF / $max ) * $max;
my $value;
do {
$value = unpack( 'N', random_bytes(4) );
} while ( $value >= $limit );
return $value % $max;
}
sub generate_short_code { sub generate_short_code {
my ( $self, $original_url, $use_pure_random ) = @_; my ( $self, $original_url, $use_pure_random ) = @_;
@@ -172,4 +183,3 @@ sub get_original_url {
} }
1; 1;