.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.43) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "LWPx::ParanoidAgent 3pm" .TH LWPx::ParanoidAgent 3pm "2022-11-27" "perl v5.36.0" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" LWPx::ParanoidAgent \- subclass of LWP::UserAgent that protects you from harm .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& require LWPx::ParanoidAgent; \& \& my $ua = LWPx::ParanoidAgent\->new; \& \& # this is 10 seconds overall, from start to finish. not just between \& # socket reads. and it includes all redirects. so attackers telling \& # you to download from a malicious tarpit webserver can only stall \& # you for $n seconds \& \& $ua\->timeout(10); \& \& # setup extra block lists, in addition to the always\-enforced blocking \& # of private IP addresses, loopbacks, and multicast addresses \& \& $ua\->blocked_hosts( \& "foo.com", \& qr/\e.internal\e.company\e.com$/i, \& sub { my $host = shift; return 1 if is_bad($host); }, \& ); \& \& $ua\->whitelisted_hosts( \& "brad.lj", \& qr/^192\e.168\e.64\e.3?/, \& sub { ... }, \& ); \& \& # get/set the DNS resolver object that\*(Aqs used \& my $resolver = $ua\->resolver; \& $ua\->resolver(Net::DNS::Resolver\->new(...)); \& \& # and then just like a normal LWP::UserAgent, because it is one. \& my $response = $ua\->get(\*(Aqhttp://search.cpan.org/\*(Aq); \& ... \& if ($response\->is_success) { \& print $response\->content; # or whatever \& } \& else { \& die $response\->status_line; \& } .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" The \f(CW\*(C`LWPx::ParanoidAgent\*(C'\fR is a class subclassing \f(CW\*(C`LWP::UserAgent\*(C'\fR, but paranoid against attackers. It's to be used when you're fetching a remote resource on behalf of a possibly malicious user. .PP This class can do whatever \f(CW\*(C`LWP::UserAgent\*(C'\fR can (callbacks, uploads from files, etc), except proxy support is explicitly removed, because in that case you should do your paranoia at your proxy. .PP Also, the schemes are limited to http and https, which are mapped to \&\f(CW\*(C`LWPx::Protocol::http_paranoid\*(C'\fR and \&\f(CW\*(C`LWPx::Protocol::https_paranoid\*(C'\fR, respectively, which are forked versions of the same ones without the \*(L"_paranoid\*(R". Subclassing them didn't look possible, as they were essentially just one huge function. .PP This class protects you from connecting to internal \s-1IP\s0 ranges (unless you whitelist them), hostnames/IPs that you blacklist, remote webserver tarpitting your process (the timeout parameter is changed to be a global timeout over the entire process), and all combinations of redirects and \&\s-1DNS\s0 tricks to otherwise tarpit and/or connect to internal resources. .SH "CONSTRUCTOR" .IX Header "CONSTRUCTOR" .ie n .IP """new""" 4 .el .IP "\f(CWnew\fR" 4 .IX Item "new" my \f(CW$ua\fR = LWPx::ParanoidAgent\->new([ \f(CW%opts\fR ]); .Sp In addition to any constructor options from LWP::UserAgent, you may also set \f(CW\*(C`blocked_hosts\*(C'\fR (to an arrayref), \f(CW\*(C`whitelisted_hosts\*(C'\fR (also an arrayref), and \f(CW\*(C`resolver\*(C'\fR, a Net::DNS::Resolver object. .SH "METHODS" .IX Header "METHODS" .ie n .IP "$csr\->\fBresolver\fR($net_dns_resolver)" 4 .el .IP "\f(CW$csr\fR\->\fBresolver\fR($net_dns_resolver)" 4 .IX Item "$csr->resolver($net_dns_resolver)" .PD 0 .ie n .IP "$csr\->\fBresolver\fR" 4 .el .IP "\f(CW$csr\fR\->\fBresolver\fR" 4 .IX Item "$csr->resolver" .PD Get/set the Net::DNS::Resolver object used to lookup hostnames. .ie n .IP "$csr\->\fBblocked_hosts\fR(@host_list)" 4 .el .IP "\f(CW$csr\fR\->\fBblocked_hosts\fR(@host_list)" 4 .IX Item "$csr->blocked_hosts(@host_list)" .PD 0 .ie n .IP "$csr\->\fBblocked_hosts\fR" 4 .el .IP "\f(CW$csr\fR\->\fBblocked_hosts\fR" 4 .IX Item "$csr->blocked_hosts" .PD Get/set the list of blocked hosts. The items in \f(CW@host_list\fR may be compiled regular expressions (with qr//), code blocks, or scalar literals. In any case, the thing that is match, passed in, or compared (respectively), is all of the given hostname, given \s-1IP\s0 address, and \s-1IP\s0 address in canonical a.b.c.d decimal notation. So if you want to block \*(L"1.2.3.4\*(R" and the user entered it in a mix of network/host form in a mix of decimal/octal/hex, you need only block \&\*(L"1.2.3.4\*(R" and not worry about the details. .ie n .IP "$csr\->\fBwhitelisted_hosts\fR(@host_list)" 4 .el .IP "\f(CW$csr\fR\->\fBwhitelisted_hosts\fR(@host_list)" 4 .IX Item "$csr->whitelisted_hosts(@host_list)" .PD 0 .ie n .IP "$csr\->\fBwhitelisted_hosts\fR" 4 .el .IP "\f(CW$csr\fR\->\fBwhitelisted_hosts\fR" 4 .IX Item "$csr->whitelisted_hosts" .PD Like blocked hosts, but matching the hosts/IPs that bypass blocking checks. The only difference is the \s-1IP\s0 address isn't canonicalized before being whitelisted-matched, mostly because it doesn't make sense for somebody to enter in a good address in a subversive way. .SH "SEE ALSO" .IX Header "SEE ALSO" See LWP::UserAgent to see how to use this class. .PP http://contributing.appspot.com/lwpx\-paranoidagent http://brad.livejournal.com/2409049.html https://github.com/collectiveintel/LWPx\-ParanoidAgent http://search.cpan.org/dist/LWPx\-ParanoidAgent .SH "ISSUES" .IX Header "ISSUES" Report issues: https://github.com/collectiveintel/LWPx\-ParanoidAgent/issues .SH "WARRANTY" .IX Header "WARRANTY" This module is supplied \*(L"as-is\*(R" and comes with no warranty, expressed or implied. It tries to protect you from harm, but maybe it will. Maybe it will destroy your data and your servers. You'd better audit it and send me bug reports. .SH "BUGS" .IX Header "BUGS" Maybe. See the warranty above. .SH "COPYRIGHT" .IX Header "COPYRIGHT" .Vb 2 \& Copyright 2005 Brad Fitzpatrick \& Copyright 2013 Wes Young (wesyoung.me) .Ve .PP Lot of code from the base class, copyright 1995\-2004 Gisle Aas. .PP This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.