diff options
author | Yorhel <git@yorhel.nl> | 2016-06-21 18:36:16 +0200 |
---|---|---|
committer | Yorhel <git@yorhel.nl> | 2016-06-21 18:36:16 +0200 |
commit | 1d23ed0d3894963def629fec61c282a330248b7a (patch) | |
tree | b3a8dc93e013d5dcb089562283f9937715e27fa5 /index.cgi | |
parent | 9df7ea36431064c9a6db04b4a5a1444b9511e1be (diff) |
Add IP-based throttling of invalid paste codes and passcodes
Prevents brute-force attacks to gain access to private pastes.
Diffstat (limited to 'index.cgi')
-rwxr-xr-x | index.cgi | 63 |
1 files changed, 60 insertions, 3 deletions
@@ -11,6 +11,10 @@ use TUWF ':html', 'html_escape'; my @syntax = sort map /([^\/]+)\.vim$/?$1:(), glob("/usr/share/vim/{vim7?,vimfiles}/syntax/*.vim"); +# IP-based throttling on invalid passcodes and paste codes. +$TUWF::OBJ->{throttle_interval} = 10; +$TUWF::OBJ->{throttle_burst} = 10; + TUWF::set( logfile => $ENV{TUWF_LOG}, @@ -54,6 +58,12 @@ sub init { CREATE TABLE IF NOT EXISTS syntaxes AS SELECT syntax, count(*) as cnt FROM pastes GROUP BY syntax )); + $self->dbExec(q( + CREATE TABLE IF NOT EXISTS throttle ( + key TEXT PRIMARY KEY, + timeout INTEGER NOT NULL DEFAULT 0 + ) + )); return 1; } @@ -120,11 +130,17 @@ sub mypastes { my $f = $self->formValidate({ post => 'p', required => 0, template => 'uint', min => 1, max => 100, default => 1}); return $self->msg('Invalid passcode or page number') if !$p || $f->{_err}; + my $th = $self->throttle_get(); + return if $th == 1; + my($pl) = $self->dbPage({page => $f->{p}, results => 100}, q| SELECT code, date, syntax, substr(raw, 1, 150) AS preview, length(raw) AS size FROM pastes WHERE passcode = ? ORDER BY date DESC|, $p ); - return $self->msg('No pastes with that passcode!') if !@$pl; + if(!@$pl) { + $self->throttle_update($th); + return $self->msg('No pastes with that passcode!'); + } my $cnt = ceil($self->dbRow('SELECT count(*) AS cnt FROM pastes WHERE passcode = ?', $p)->{cnt} / 100); $self->htmlHeader('mypastes', 'newpaste'); @@ -257,6 +273,7 @@ sub _get_html { package TUWF::Object; use TUWF ':html', 'html_escape'; +use Socket 'inet_pton', 'inet_ntop', 'AF_INET', 'AF_INET6'; sub htmlHeader { @@ -402,14 +419,54 @@ sub htmlUploadForm { } -# fetches a paste and updates lastvisit column when necessary +# Function directly stolen from VNDB's VNDBUtil.pm +sub norm_ip { + my $ip = shift; + my $v4 = inet_pton AF_INET, $ip; + if($v4) { + $v4 =~ s/(..)(.)./$1 . chr(ord($2) & 254) . "\0"/se; + return inet_ntop AF_INET, $v4; + } + $ip = inet_pton AF_INET6, $ip; + return '::' if !$ip; + $ip =~ s/^(.{6}).+$/$1 . "\0"x10/se; + return inet_ntop AF_INET6, $ip; +} + + +sub throttle_get { + my $self = shift; + + my $tm = time; + my $th = $self->dbRow('SELECT timeout FROM throttle WHERE key = ?', norm_ip($self->reqIP))->{timeout}; + $th = $tm if !$th || $th < $tm; + + return $self->msg('Throttled.') + if $th-$tm > $self->{throttle_burst}*$self->{throttle_interval}; + + return $th; +} + + +sub throttle_update { + my($self, $th) = @_; + $self->dbExec('INSERT OR REPLACE INTO throttle (key, timeout) VALUES (?, ?)', norm_ip($self->reqIP), $th+$self->{throttle_interval}); +} + + +# fetches a paste and handles throttling and updates lastvisit column when necessary sub getpaste { my($self, $code, $col) = @_; + my $th = $self->throttle_get(); + return if $th == 1; my $r = $self->dbRow(q| SELECT !s, lastvisit FROM pastes WHERE code = ? |, $col, $code ); - return $self->msg('No paste with that code.') if !keys %$r; + if(!keys %$r) { + $self->throttle_update($th); + return $self->msg('No paste with that code.'); + } $self->dbExec('UPDATE pastes SET lastvisit = ? WHERE code = ?', time(), $code) if $r->{lastvisit} < time()-24*3600; return $r; } |