OSDN Git Service

FIX:$manager->notify()の第二引数に変数を渡すように修正。
[nucleus-jp/nucleus-next.git] / nucleus / plugins / NP_SecurityEnforcer.php
1 <?php
2 /*
3 License:
4 This software is published under the same license as NucleusCMS, namely
5 the GNU General Public License. See http://www.gnu.org/licenses/gpl.html for
6 details about the conditions of this license.
7
8 In general, this program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 2 of the License, or (at your option) any
11 later version.
12
13 This program is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
15 PARTICULAR PURPOSE. See the GNU General Public License for more details.
16
17 * @version $Id: NP_SecurityEnforcer.php 1874 2012-06-17 07:27:38Z sakamocchi $
18 */
19 class NP_SecurityEnforcer extends NucleusPlugin
20 {
21         public function getName()
22         {
23                 return 'SecurityEnforcer';
24         }
25
26         public function getAuthor()
27         {
28                 return 'Frank Truscott + Cacher + Mocchi';
29         }
30
31         public function getURL()
32         {
33                 return 'http://revcetera.com/ftruscot';
34         }
35
36         public function getVersion()
37         {
38                 return '1.02';
39         }
40
41         public function getDescription()
42         {
43                 return _SECURITYENFORCER_DESCRIPTION;
44         }
45
46         public function getMinNucleusVersion()
47         {
48                 return 400;
49         }
50
51         public function getTableList()
52         {
53                 return array(sql_table('plug_securityenforcer'));
54         }
55
56         public function getEventList()
57         {
58                 return array('QuickMenu','PrePasswordSet','CustomLogin','LoginSuccess','LoginFailed','PostRegister');
59         }
60
61         public function hasAdminArea() {
62                 return 1;
63         }
64         
65         public function supportsFeature($what)
66         {
67                 if ( $what == 'SqlTablePrefix' )
68                 {
69                         return 1;
70                 }
71                 return 0;
72         }
73         
74         public function install()
75         {
76                 global $CONF;
77                 
78                 // Need to make some options
79                 $this->createOption('quickmenu',                        '_SECURITYENFORCER_OPT_QUICKMENU',                      'yesno', 'yes');
80                 $this->createOption('del_uninstall_data',       '_SECURITYENFORCER_OPT_DEL_UNINSTALL_DATA',     'yesno','no');
81                 $this->createOption('enable_security',          '_SECURITYENFORCER_OPT_ENABLE',                         'yesno','yes');
82                 $this->createOption('pwd_min_length',           '_SECURITYENFORCER_OPT_PWD_MIN_LENGTH',         'text','8');
83                 $this->createOption('pwd_complexity',           '_SECURITYENFORCER_OPT_PWD_COMPLEXITY',         'select','0', '_SECURITYENFORCER_OPT_SELECT_OFF_COMP|0|_SECURITYENFORCER_OPT_SELECT_ONE_COMP|1|_SECURITYENFORCER_OPT_SELECT_TWO_COMP|2|_SECURITYENFORCER_OPT_SELECT_THREE_COMP|3|_SECURITYENFORCER_OPT_SELECT_FOUR_COMP|4;datatype=numerical');
84                 $this->createOption('max_failed_login',         '_SECURITYENFORCER_OPT_MAX_FAILED_LOGIN',       'text', '5');
85                 $this->createOption('login_lockout',            '_SECURITYENFORCER_OPT_LOGIN_LOCKOUT',          'text', '15');
86                 
87                 // create needed tables
88                 DB::execute('CREATE TABLE IF NOT EXISTS '. sql_table('plug_securityenforcer').
89                                         " (login varchar(255),
90                                            fails int(11) NOT NULL default '0',
91                                            lastfail bigint NOT NULL default '0',
92                                            KEY login (login)) ENGINE=MyISAM");
93                 return;
94         }
95         
96         public function unInstall()
97         {
98                 // if requested, delete the data table
99                 if ( $this->getOption('del_uninstall_data') == 'yes' )
100                 {
101                         DB::execute('DROP TABLE '.sql_table('plug_securityenforcer'));
102                 }
103                 return;
104         }
105         
106         public function init()
107         {
108                 // include translation file for this plugin
109                 if ( file_exists($this->getDirectory() . i18n::get_current_locale() . '.' . i18n::get_current_charset() . '.php') )
110                 {
111                         include_once($this->getDirectory() . i18n::get_current_locale() . '.' . i18n::get_current_charset() . '.php');
112                 }
113                 else
114                 {
115                         include_once($this->getDirectory().'en_Latn_US.UTF-8.php');
116                 }
117                 
118                 $this->enable_security  = $this->getOption('enable_security');
119                 $this->pwd_min_length   = (integer) $this->getOption('pwd_min_length');
120                 $this->pwd_complexity   = (integer) $this->getOption('pwd_complexity');
121                 $this->max_failed_login = (integer) $this->getOption('max_failed_login');
122                 $this->login_lockout    = (integer) $this->getOption('login_lockout');
123                 return;
124         }
125         
126         public function event_QuickMenu(&$data)
127         {
128                 // only show when option enabled
129                 global $member;
130                 if ( $this->getOption('quickmenu') != 'yes' || !$member->isAdmin() )
131                 {
132                         return;
133                 }
134                 //global $member;
135                 if ( !($member->isLoggedIn()) )
136                 {
137                         return;
138                 }
139                 array_push($data['options'],
140                         array('title' => 'Security Enforcer',
141                         'url' => $this->getAdminURL(),
142                         'tooltip' => _SECURITYENFORCER_ADMIN_TOOLTIP));
143                 return;
144         }
145         
146         public function event_PrePasswordSet(&$data)
147         {
148                 //password, errormessage, valid
149                 if ( $this->enable_security == 'no' )
150                 {
151                         return;
152                 }
153                 $password = $data['password'];
154                 // conditional below not needed in 3.60 or higher. Used to keep from setting off error when password not being changed
155                 if ( postVar('action') == 'changemembersettings' )
156                 {
157                         $emptyAllowed = true;
158                 }
159                 else
160                 {
161                         $emptyAllowed = false;
162                 }
163                 if ( (!$emptyAllowed)||$password )
164                 {
165                         $message = $this->validate_and_messsage($password,$this->pwd_min_length, $this->pwd_complexity);
166                         if ( $message )
167                         {
168                                 $data['errormessage'] = _SECURITYENFORCER_INSUFFICIENT_COMPLEXITY . $message. "<br /><br />\n";
169                                 $data['valid'] = false;
170                         }
171                 }
172                 return;
173         }
174         
175         public function event_PostRegister(&$data)
176         {
177                 if ( $this->enable_security != 'yes' )
178                 {
179                         return;
180                 }
181                 $password = postVar('password');
182                 if( postVar('action') == 'memberadd' )
183                 {
184                         $message = $this->validate_and_messsage($password,$this->pwd_min_length, $this->pwd_complexity);
185                         if ( $message )
186                         {
187                                 $errormessage = _SECURITYENFORCER_ACCOUNT_CREATED. $message. "<br /><br />\n";
188                                 global $admin;
189                                 $admin->error($errormessage);
190                         }
191                 }
192                 return;
193         }
194         
195         public function event_CustomLogin(&$data)
196         {
197                 if ( $this->enable_security != 'yes' || $this->max_failed_login <= 0 )
198                 {
199                         return;
200                 }
201                 
202                 //login,password,success,allowlocal
203                 global $_SERVER;
204                 $login = $data['login'];
205                 $ip = $_SERVER['REMOTE_ADDR'];
206                 
207                 $query = "DELETE FROM %s WHERE lastfail < %d;";
208                 $query = sprintf($query, sql_table('plug_securityenforcer'), (integer) (time() - ($this->login_lockout * 60)));
209                 DB::execute($query);
210                 
211                 $query = "SELECT fails as result FROM %s WHERE login=%s;";
212                 $query = sprintf($query, sql_table('plug_securityenforcer'), DB::quoteValue($login));
213                 $flogin = DB::getValue($query); 
214                 
215                 $query = "SELECT fails as result FROM %s WHERE login=%s;";
216                 $query = sprintf($query, sql_table('plug_securityenforcer'), DB::quoteValue($ip));
217                 $fip = DB::getValue($query); 
218                 
219                 if ( $flogin >= $this->max_failed_login || $fip >= $this->max_failed_login )
220                 {
221                         $data['success']        = 0;
222                         $data['allowlocal']     = 0;
223                         $info = sprintf(_SECURITYENFORCER_LOGIN_DISALLOWED, Entity::hsc($login), Entity::hsc($ip));
224                         ActionLog::add(INFO, $info);
225                 }
226                 return;
227         }
228         
229         public function event_LoginSuccess(&$data)
230         {
231                 //member(obj),username
232                 if ( $this->enable_security != 'yes' || $this->max_failed_login <= 0 )
233                 {
234                         return;
235                 }
236                 global $_SERVER;
237                 $login = $data['username'];
238                 $ip = $_SERVER['REMOTE_ADDR'];
239                 DB::execute('DELETE FROM ' . sql_table('plug_securityenforcer') . ' WHERE login=' . DB::quoteValue($login));
240                 DB::execute('DELETE FROM ' . sql_table('plug_securityenforcer') . ' WHERE login=' . DB::quoteValue($ip));
241                 return;
242         }
243         
244         public function event_LoginFailed(&$data)
245         {
246                 //username
247                 if ( $this->enable_security != 'yes' || $this->max_failed_login <= 0 )
248                 {
249                         return;
250                 }
251                 global $_SERVER;
252                 $login = $data['username'];
253                 $ip = $_SERVER['REMOTE_ADDR'];
254                 $lres = DB::getResult('SELECT * FROM ' . sql_table('plug_securityenforcer') . ' WHERE login=' . DB::quoteValue($login));
255                 if ( $lres->rowCount() > 0 )
256                 {
257                         DB::execute('UPDATE ' . sql_table('plug_securityenforcer') . ' SET fails=fails+1, lastfail=' . time() . ' WHERE login=' . DB::quoteValue($login));
258                 }
259                 else
260                 {
261                         DB::execute('INSERT INTO ' . sql_table('plug_securityenforcer') . ' (login,fails,lastfail) VALUES (' . DB::quoteValue($login) . ',1,' . time() . ')');
262                 }
263                 $lres = DB::getResult('SELECT * FROM ' . sql_table('plug_securityenforcer') . ' WHERE login=' . DB::quoteValue($ip));
264                 if ( $lres->rowCount() > 0 )
265                 {
266                         DB::execute('UPDATE ' . sql_table('plug_securityenforcer') . ' SET fails=fails+1, lastfail=' . time() . ' WHERE login=' . DB::quoteValue($ip));
267                 }
268                 else
269                 {
270                         DB::execute('INSERT INTO ' . sql_table('plug_securityenforcer') . ' (login,fails,lastfail) VALUES (' . DB::quoteValue($ip) . ',1,' . time() . ')');
271                 }
272                 return;
273         }
274         
275         private function validate_and_messsage($passwd,$minlength = 6,$complexity = 0)
276         {
277                 $minlength = intval($minlength);
278                 $complexity = intval($complexity);
279                 $message = '';
280                 
281                 if ( $minlength < 6 )
282                 {
283                         $minlength = 6;
284                 }
285                 if ( i18n::strlen($passwd) < $minlength )
286                 {
287                         $message = _SECURITYENFORCER_MIN_PWD_LENGTH . $this->pwd_min_length;
288                 }
289                 
290                 if ( $complexity > 4 )
291                 {
292                         $complexity = 4;
293                 }
294                 
295                 $ucchars        = '[A-Z]';
296                 $lcchars        = '[a-z]';
297                 $numchars       = '[0-9]';
298                 $ochars         = '[#-~!@\\$%^&*()_+=,.<>?:;|]';
299                 $chartypes      = array($ucchars, $lcchars, $numchars, $ochars);
300                 $tot            = array(0,0,0,0);
301                 $i                      = 0;
302                 
303                 foreach ( $chartypes as $value )
304                 {
305                         $tot[$i] = preg_match("/" . $value . "/", $passwd);
306                         $i = $i + 1;
307                 }
308                 
309                 if ( array_sum($tot) < $complexity )
310                 {
311                         $message .= _SECURITYENFORCER_PWD_COMPLEXITY . $this->pwd_complexity;
312                 }
313                 return $message;
314         }
315 }