OSDN Git Service

FIX:変数名の誤記を修正
[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 1721 2012-03-31 10:18:25Z 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                 DB::execute("DELETE FROM " . sql_table('plug_securityenforcer') . " WHERE lastfail < " . (time() - ($this->login_lockout * 60)));
207                 $query = "SELECT fails as result FROM " . sql_table('plug_securityenforcer') . " ";
208                 $query .= 'WHERE login=' . DB::quoteValue($login);
209                 $flogin = DB::getValue($query); 
210                 $query = "SELECT fails as result FROM " . sql_table('plug_securityenforcer') . " ";
211                 $query .= 'WHERE login=' . DB::quoteValue($ip);
212                 $fip = DB::getValue($query); 
213                 
214                 if ( $flogin >= $this->max_failed_login || $fip >= $this->max_failed_login )
215                 {
216                         $data['success']        = 0;
217                         $data['allowlocal']     = 0;
218                         $info = sprintf(_SECURITYENFORCER_LOGIN_DISALLOWED, Entity::hsc($login), Entity::hsc($ip));
219                         ActionLog::add(INFO, $info);
220                 }
221                 return;
222         }
223         
224         public function event_LoginSuccess($data)
225         {
226                 //member(obj),username
227                 if ( $this->enable_security != 'yes' || $this->max_failed_login <= 0 )
228                 {
229                         return;
230                 }
231                 global $_SERVER;
232                 $login = $data['username'];
233                 $ip = $_SERVER['REMOTE_ADDR'];
234                 DB::execute("DELETE FROM " . sql_table('plug_securityenforcer') . " WHERE login=" . DB::quoteValue($login));
235                 DB::execute("DELETE FROM " . sql_table('plug_securityenforcer') . " WHERE login=" . DB::quoteValue($ip));
236                 return;
237         }
238         
239         public function event_LoginFailed($data)
240         {
241                 //username
242                 if ( $this->enable_security != 'yes' || $this->max_failed_login <= 0 )
243                 {
244                         return;
245                 }
246                 global $_SERVER;
247                 $login = $data['username'];
248                 $ip = $_SERVER['REMOTE_ADDR'];
249                 $lres = DB::getValue("SELECT * FROM " . sql_table('plug_securityenforcer') . ' WHERE login=' . DB::quoteValue($login));
250                 if ( $lres )
251                 {
252                         DB::execute("UPDATE " . sql_table('plug_securityenforcer') . " SET fails=fails+1, lastfail=" . time() . ' WHERE login=' . DB::quoteValue($login));
253                 }
254                 else
255                 {
256                         DB::execute("INSERT INTO " . sql_table('plug_securityenforcer') . ' (login,fails,lastfail) VALUES (' . DB::quoteValue($login) . ',1,' . time() . ')');
257                 }
258                 $lres = DB::getValue("SELECT * FROM " . sql_table('plug_securityenforcer') . " WHERE login='" . DB::quoteValue($ip) . "'");
259                 if ( $lres )
260                 {
261                         DB::execute("UPDATE " . sql_table('plug_securityenforcer') . ' SET fails=fails+1, lastfail=' . time() . ' WHERE login=' . DB::quoteValue($ip));
262                 }
263                 else
264                 {
265                         DB::execute("INSERT INTO " . sql_table('plug_securityenforcer') . ' (login,fails,lastfail) VALUES (' . DB::quoteValue($ip) . ',1,' . time() . ')');
266                 }
267                 return;
268         }
269         
270         private function validate_and_messsage($passwd,$minlength = 6,$complexity = 0)
271         {
272                 $minlength = intval($minlength);
273                 $complexity = intval($complexity);
274                 
275                 if ( $minlength < 6 )
276                 {
277                         $minlength = 6;
278                 }
279                 if ( i18n::strlen($passwd) < $minlength )
280                 {
281                         $message = _SECURITYENFORCER_MIN_PWD_LENGTH . $this->pwd_min_length;
282                 }
283                 
284                 if ( $complexity > 4 )
285                 {
286                         $complexity = 4;
287                 }
288                 
289                 $ucchars        = "[A-Z]";
290                 $lcchars        = "[a-z]";
291                 $numchars       = "[0-9]";
292                 $ochars         = "[-~!@#$%^&*()_+=,.<>?:;|]";
293                 $chartypes      = array($ucchars, $lcchars, $numchars, $ochars);
294                 $tot            = array(0,0,0,0);
295                 $i                      = 0;
296                 
297                 foreach ( $chartypes as $value )
298                 {
299                         $tot[$i] = preg_match("#{$value}#", $passwd);
300                         $i = $i + 1;
301                 }
302                 
303                 if ( array_sum($tot) < $complexity )
304                 {
305                         $message .= _SECURITYENFORCER_PWD_COMPLEXITY . $this->pwd_complexity;
306                 }
307                 return $message;
308         }
309 }