.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.40) .\" .\" 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 "WWW::CSRF 3pm" .TH WWW::CSRF 3pm "2021-01-03" "perl v5.32.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" WWW::CSRF \- Generate and check tokens to protect against CSRF attacks .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 1 \& use WWW::CSRF qw(generate_csrf_token check_csrf_token CSRF_OK); .Ve .PP Generate a token to add as a hidden in all \s-1HTML\s0 forms: .PP .Vb 1 \& my $csrf_token = generate_csrf_token($username, "s3kr1t"); .Ve .PP Then, in any action with side effects, retrieve that form field and check it with: .PP .Vb 2 \& my $status = check_csrf_token($username, "s3kr1t", $csrf_token); \& die "Wrong CSRF token" unless ($status == CSRF_OK); .Ve .SH "COPYRIGHT" .IX Header "COPYRIGHT" Copyright 2013 Steinar H. Gunderson. .PP This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. .SH "DESCRIPTION" .IX Header "DESCRIPTION" This module generates tokens to help protect against a website attack known as Cross-Site Request Forgery (\s-1CSRF,\s0 also known as \s-1XSRF\s0). \s-1CSRF\s0 is an attack where an attacker fools a browser into make a request to a web server for which that browser will automatically include some form of credentials (cookies, cached \&\s-1HTTP\s0 Basic authentication, etc.), thus abusing the web server's trust in the user for malicious use. .PP The most common \s-1CSRF\s0 mitigation is sending a special, hard-to-guess token with every request, and then require that any request that is not idempotent (i.e., has side effects) must be accompanied with such a token. This mitigation depends critically on the fact that while an attacker can easily make the victim's browser \&\fImake\fR a request, the browser security model (same-origin policy, or \s-1SOP\s0 for short) prevents third-party sites from reading the \&\fIresults\fR of that request. .PP \&\s-1CSRF\s0 tokens should have at least the following properties: .IP "\(bu" 4 They should be hard-to-guess, so they should be signed with some key known only to the server. .IP "\(bu" 4 They should be dependent on the authenticated identity, so that one user cannot use its own tokens to impersonate another user. .IP "\(bu" 4 They should not be the same for every request, or an attack known as \s-1BREACH\s0 can use \s-1HTTP\s0 compression to gradually deduce more and more of the token. .IP "\(bu" 4 They should contain an (authenticated) timestamp, so that if an attacker manages to learn one token, he or she cannot impersonate a user indefinitely. .PP \&\s-1WWW::CSRF\s0 simplifies the (simple, but tedious) work of creating and verifying such tokens. .PP Note that resources that are protected against \s-1CSRF\s0 should also be protected against a different attack known as clickjacking. There are many defenses against clickjacking (which ideally should be combined), but a good start is sending a \f(CW\*(C`X\-Frame\-Options\*(C'\fR \s-1HTTP\s0 header set to \f(CW\*(C`DENY\*(C'\fR or \f(CW\*(C`SAMEORIGIN\*(C'\fR. See the Wikipedia article on clickjacking for more information. .PP This module provides the following functions: .ie n .IP "generate_csrf_token($id, $secret, \e%options)" 4 .el .IP "generate_csrf_token($id, \f(CW$secret\fR, \e%options)" 4 .IX Item "generate_csrf_token($id, $secret, %options)" This routine generates a \s-1CSRF\s0 token to send out to already authenticated users. (Unauthenticated users generally need no \s-1CSRF\s0 protection, as there are no credentials to impersonate.) .Sp \&\f(CW$id\fR is the identity you wish to authenticate; usually, this would be a user name of some sort. .Sp \&\f(CW$secret\fR is the secret key authenticating the token. This should be protected in the same matter you would protect other server-side secrets, e.g. database passwords\*(--if this leaks out, an attacker can generate \s-1CSRF\s0 tokens at will. .Sp The keys in \f(CW%options\fR are relatively esoteric and need generally not be set, but currently supported are: .RS 4 .IP "\(bu" 4 \&\f(CW\*(C`Time\*(C'\fR, for overriding the time value added to the token. If this is not set, the value of \f(CW\*(C`time()\*(C'\fR is used. .IP "\(bu" 4 \&\f(CW\*(C`Random\*(C'\fR, for controlling the random masking value used to protect against the \s-1BREACH\s0 attack. If set, it must be exactly 20 random bytes; if not, these bytes are generated with a call to Bytes::Random::Secure. .RE .RS 4 .Sp The returned \s-1CSRF\s0 token is in a text-only form suitable for inserting into a \s-1HTML\s0 form without further escaping (assuming you did not send in strange things to the \f(CW\*(C`Time\*(C'\fR option). .RE .ie n .IP "check_csrf_token($id, $secret, $csrf_token, \e%options)" 4 .el .IP "check_csrf_token($id, \f(CW$secret\fR, \f(CW$csrf_token\fR, \e%options)" 4 .IX Item "check_csrf_token($id, $secret, $csrf_token, %options)" This routine checks the integrity and age of the a token generated by \&\f(CW\*(C`generate_csrf_token\*(C'\fR. The values of \f(CW$id\fR and \f(CW$secret\fR correspond to the same parameters given to \f(CW\*(C`generate_csrf_token\*(C'\fR, and \f(CW$csrf_token\fR is the token to verify. Also, you can set one or more of the following options in \f(CW%options:\fR .RS 4 .IP "\(bu" 4 \&\f(CW\*(C`Time\*(C'\fR, for overriding the time value used to check the age of the token. If this is not set, the value of \f(CW\*(C`time()\*(C'\fR is used. .IP "\(bu" 4 \&\f(CW\*(C`MaxAge\*(C'\fR, for setting a maximum age for the \s-1CSRF\s0 token in seconds. If this is negative, \fIno age checking is performed\fR, which is not recommended. The default value is a week, or 604800 seconds. .RE .RS 4 .Sp This routine returns one of the following constants: .IP "\(bu" 4 \&\f(CW\*(C`CSRF_OK\*(C'\fR: The token is verified correct. .IP "\(bu" 4 \&\f(CW\*(C`CSRF_EXPIRED\*(C'\fR: The token has an expired timestamp, but is otherwise valid. .IP "\(bu" 4 \&\f(CW\*(C`CSRF_INVALID_SIGNATURE\*(C'\fR: The token is not properly authenticated; either it was generated using the wrong secret, for the wrong user, or it has been tampered with in-transit. .IP "\(bu" 4 \&\f(CW\*(C`CSRF_MALFORMED_TOKEN\*(C'\fR: The token is not in the correct format. .RE .RS 4 .Sp In general, you should only allow the requested action if \f(CW\*(C`check_csrf_token\*(C'\fR returns \f(CW\*(C`CSRF_OK\*(C'\fR. .Sp Note that you are allowed to call \f(CW\*(C`check_csrf_token\*(C'\fR multiple times with e.g. different secrets. This is useful in the case of key rollover, where you change the secret for new tokens, but want to continue accepting old tokens for some time to avoid disrupting operations. .RE .SH "SEE ALSO" .IX Header "SEE ALSO" Wikipedia has an article with more information on \s-1CSRF:\s0 .PP .Vb 1 \& L .Ve