Line # Revision Author
1 3 ahitrov@rambler.ru package Utils::Spam::SecretForm;
2
3 use strict;
4 use Digest::MD5;
5 use Contenido::Globals;
6 use Scalar::Util qw(blessed);
7
8 # ��������� ������ ��� ������� � hidden-���� �����
9 # ��� ������������ �������� ������������.
10 # extra - �������������� �������� ��� ��������� ������
11 # (� �������, ���� ������� ���������, ����� ���������� ��� �����)
12 # �� ������ ���������� ������ �������� ��� ���������.
13 sub generate {
14 my %opts = @_;
15
16 my ($start_time, $secret_code) = &get_secret_code(allow_generate_code => 1, memd => $opts{'memd'});
17 return unless $start_time && $secret_code;
18
19 my $random = &get_random_string(5);
20 # start secret code time | generate time | hash
21 return $start_time.'|'.time().'|'.$random.'|'.Digest::MD5::md5_hex($start_time.$secret_code.$random.$opts{'extra'});
22 }
23
24 # ��������� �������� hidden-���� �����
25 # secret - ���, ��� ��������
26 # ttl - ����� ����� ��������� ����
27 # check_count - true �������� ���������� �� ������� ���������� �������������
28 sub validate {
29 my %opts = @_;
30
31 my $user_secret = $opts{'secret'};
32 my $ttl = $opts{'ttl'} || 3600;
33 my $extra = $opts{'extra'};
34 my $check_count = $opts{'check_count'};
35 my $result = { is_valid => 1, is_expired => 0, count => 1 };
36 my $memd = blessed($opts{'memd'}) ? $opts{'memd'} : $keeper->MEMD();
37
38 return $result unless blessed($memd);
39
40 # ���������� �� ������������ ������
41 my ($user_start_time, $user_generate_time, $user_random, $user_hash) = split(/\|/, $user_secret);
42
43 # ������ � ��������� ������
44 my ($start_time, $secret_code) = &get_secret_code(time => $user_start_time, memd => $memd);
45
46 # ���� ��� �� �������� ��������� ����� ���������
47 return $result if !defined($start_time) && !defined($secret_code);
48
49 if (Digest::MD5::md5_hex($start_time.$secret_code.$user_random.$extra) ne $user_hash) {
50 $result->{'is_valid'} = 0;
51 }
52
53 if ($result->{'is_valid'} && (($user_generate_time-$start_time > 3600)
54 || (time()-$user_generate_time > $ttl))
55 ) {
56 $result->{'is_expired'} = 1;
57 }
58
59 # ���� ���������� �������� �� ��������� �������������
60 # ������ � ���������� ������������� ���������� ��������� � ����
61 if ($result->{'is_valid'} && !$result->{'is_expired'} && $check_count) {
62
63 $result->{'count'} = $memd->incr('usersecret|'.$user_secret, 1) if $memd;
64
65 unless ($result->{'count'}) {
66 $memd->add('usersecret|'.$user_secret, 1, $ttl) if $memd;
67 $result->{'count'} = 1;
68 }
69 }
70
71 return $result;
72 }
73
74 # ���������, ������� ���������� true ��� false � ����������� �� ����������, ��������������
75 # � ���������� �������������
76 sub is_valid_secret {
77 my %opts = @_;
78
79 my $validate = &validate(%opts);
80
81 if ($validate->{'is_valid'} && !$validate->{'is_expired'}
82 && (!$opts{'check_count'} || ($opts{'check_count'} && $validate->{'count'} == 1)))
83 {
84 return 1;
85 }
86
87 return undef;
88 }
89
90 # ��������� ��������� � ��������� ��� �������������
91 # ���������� ����. ��������� ���� ����������� � ������� �����.
92 # time - ��� ���������� ������� ������ ���������� �� ��� ������ ���
93 # allow_generate_code - ��������� ������������ ���, ���� �� �� ������
94 sub get_secret_code {
95 my %opts = @_;
96
97 # ������� ������� memcached �������� ������������
98 # �������� �����������������
99 my $memd = blessed($opts{'memd'}) ? $opts{'memd'} : $keeper->MEMD();
100 return unless blessed($memd);
101
102 my $time = abs(int($opts{'time'}));
103 my $now_time = time();
104
105 # ����� � ����������� �� ������ ����
106 unless ($time) {
107 $time = $now_time;
108 $time = $time - ($time % 3600);
109 }
110
111 my $cache_key = 'secret_code|'.$time;
112
113 my $secret_code = $memd->get($cache_key);
114 return ($time, $secret_code) if $secret_code;
115
116 if ($opts{'allow_generate_code'}) {
117 $secret_code = &get_random_string(10);
118 $memd->set($cache_key, $secret_code);
119 }
120
121 return ($time, $secret_code);
122 }
123
124
125 # ��������� ��������� ������
126 # length - ����� ������
127 sub get_random_string {
128 my $length = shift;
129 $length = 10 unless $length && $length =~ /^\d+$/;
130
131 my $random_chars = 'abcdefghijklmnopqrstuvwxyz1234567890';
132 my $random_chars_length = length($random_chars);
133
134 my $string = '';
135 for (1..$length) {
136 $string .= substr($random_chars, int(rand($random_chars_length)), 1);
137 }
138
139 return $string;
140 }
141
142
143 1;
144 __END__
145
146 =head1 NAME
147
148 Utils::Spam::SecretForm - ������� ��������� � �������� ��������� ������ ��� web-����
149
150 =head1 SYNOPSIS
151
152 ���������:
153 <input type="hidden" name="secret" value="<% Utils::Spam::SecretForm::generate() %>">
154
155 ��������:
156 my $validate = Utils::Spam::SecretForm::validate( secret => $ARGS{'secret'}, check_count => 0|1 );
157 if ($validate->{'is_valid'} && !$validate->{'is_expired'}) {
158 allowed method
159 }
160
161
162 =head1 DESCRIPTION
163
164 � ������� javascript ���������� ������ �������� http-������� get � post �������.
165 ����� �������� a.html ��������:
166 <form method=post action=http://www.rambler.ru/post.html name="b">
167 <input type="text" name="text">
168 <input name="submit" type="submit" value="submit">
169 </form>
170 <script>
171 document.b.submit.click();
172 </script>
173
174 ����� �������, �������, �������� �� �������� a.html, �������� ������ �����.
175 ��� ������ � ������ ����� ���� ������� ������������ ������ ������.
176
177 ������� ������: ��� � ��� ������������ ��������� ��������� ������, ������� �������� � ���� �� ����� secret_code|�����_���������.
178 ���� ��� �� ��������, ��������� �������� ������� ��� ������ ���� ��������.
179 ������������ ��� ��������� ����� �������� ����� ��� � hidden ����, ������� ������� �� ���� ������:
180 1) ����� ��������� ��������� ������
181 2) ����� ������ ������ ������������
182 3) ��������� ������
183 4) md5_hex( ����� ������ ������ ������������ . ��������� ������ . ��������� ������)
184 ��� ����������� ����������� hidden-��������� ���������� ��������� ��������� ������ �� ���� secret_code|�����_���������, ��������� md5_hex(������� . ���������� ������) � �������� ������� ������� ��������� ���� � �������� �������.
185 ����� ������� �������� ������ �� ������������������� ��������. ��� ������ �� ���������� ������������� ���� ���������� ������������ �������� check_count, � ����������� �� �������� � ���������� ����������� �������� ���������� ������������� ���� (����� ������� ������������� count = 1).
186 ��� ��������� ������ ���������� ����������� ���������� extra ��������� ��� ��������� hidden-����, �������� ��� ������������ ������������, ��� ����� ���� ����� ��� ������. �� ���������� �� �������� ���������� ���� �������� �� ����� ���������.
187