OSDN Git Service

CHANGE: ENTITYクラスの整備。globalfunctions.phpの整理。
[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 class NP_SecurityEnforcer extends NucleusPlugin
18 {
19         public function getName()               { return 'SecurityEnforcer'; }
20         public function getAuthor()             { return 'Frank Truscott + Cacher + Mocchi'; }
21         public function getURL()                        { return 'http://revcetera.com/ftruscot';       }
22         public function getVersion()    { return '1.02'; }
23         public function getDescription()        { return _SECURITYENFORCER_DESCRIPTION; }
24         public function getMinNucleusVersion()  { return 400; }
25         public function getTableList() { return array(sql_table('plug_securityenforcer')); }
26         public function getEventList() { return array('QuickMenu','PrePasswordSet','CustomLogin','LoginSuccess','LoginFailed','PostRegister'); }
27         public function hasAdminArea() { return 1; }
28         
29         public function supportsFeature($what)
30         {
31                 if ( $what == 'SqlTablePrefix' )
32                 {
33                         return 1;
34                 }
35                 return 0;
36         }
37         
38         public function install()
39         {
40                 global $CONF;
41                 
42                 // Need to make some options
43                 $this->createOption('quickmenu', '_SECURITYENFORCER_OPT_QUICKMENU', 'yesno', 'yes');
44                 $this->createOption('del_uninstall_data', '_SECURITYENFORCER_OPT_DEL_UNINSTALL_DATA', 'yesno','no');
45                 $this->createOption('enable_security', '_SECURITYENFORCER_OPT_ENABLE', 'yesno','yes');
46                 $this->createOption('pwd_min_length', '_SECURITYENFORCER_OPT_PWD_MIN_LENGTH', 'text','8');
47                 $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');
48                 $this->createOption('max_failed_login', '_SECURITYENFORCER_OPT_MAX_FAILED_LOGIN', 'text', '5');
49                 $this->createOption('login_lockout', '_SECURITYENFORCER_OPT_LOGIN_LOCKOUT', 'text', '15');
50                 
51                 // create needed tables
52                 sql_query("CREATE TABLE IF NOT EXISTS ". sql_table('plug_securityenforcer').
53                                         " (`login` varchar(255),
54                                            `fails` int(11) NOT NULL default '0',
55                                            `lastfail` bigint NOT NULL default '0',
56                                            KEY `login` (`login`)) ENGINE=MyISAM");
57                 return;
58         }
59         
60         public function unInstall()
61         {
62                 // if requested, delete the data table
63                 if ( $this->getOption('del_uninstall_data') == 'yes' )
64                 {
65                         sql_query('DROP TABLE '.sql_table('plug_securityenforcer'));
66                 }
67                 return;
68         }
69         
70         public function init()
71         {
72                 // include language file for this plugin
73                 if ( file_exists($this->getDirectory() . i18n::get_current_locale() . '.' . i18n::get_current_charset() . '.php') )
74                 {
75                         include_once($this->getDirectory() . i18n::get_current_locale() . '.' . i18n::get_current_charset() . '.php');
76                 }
77                 else
78                 {
79                         include_once($this->getDirectory().'en_Latn_US.UTF-8.php');
80                 }
81                 
82                 $this->enable_security  = $this->getOption('enable_security');
83                 $this->pwd_min_length   = (integer) $this->getOption('pwd_min_length');
84                 $this->pwd_complexity   = (integer) $this->getOption('pwd_complexity');
85                 $this->max_failed_login = (integer) $this->getOption('max_failed_login');
86                 $this->login_lockout            = (integer) $this->getOption('login_lockout');
87                 return;
88         }
89         
90         public function event_QuickMenu($data)
91         {
92                 // only show when option enabled
93                 global $member;
94                 if ( $this->getOption('quickmenu') != 'yes' || !$member->isAdmin() )
95                 {
96                         return;
97                 }
98                 //global $member;
99                 if ( !($member->isLoggedIn()) )
100                 {
101                         return;
102                 }
103                 array_push($data['options'],
104                         array('title' => 'Security Enforcer',
105                         'url' => $this->getAdminURL(),
106                         'tooltip' => _SECURITYENFORCER_ADMIN_TOOLTIP));
107                 return;
108         }
109         
110         public function event_PrePasswordSet($data)
111         {
112                 //password, errormessage, valid
113                 if ( $this->enable_security == 'no' )
114                 {
115                         return;
116                 }
117                 $password = $data['password'];
118                 // conditional below not needed in 3.60 or higher. Used to keep from setting off error when password not being changed
119                 if ( postVar('action') == 'changemembersettings' )
120                 {
121                         $emptyAllowed = true;
122                 }
123                 else
124                 {
125                         $emptyAllowed = false;
126                 }
127                 if ( (!$emptyAllowed)||$password )
128                 {
129                         $message = $this->validate_and_messsage($password,$this->pwd_min_length, $this->pwd_complexity);
130                         if ( $message )
131                         {
132                                 $data['errormessage'] = _SECURITYENFORCER_INSUFFICIENT_COMPLEXITY . $message. "<br /><br />\n";
133                                 $data['valid'] = false;
134                         }
135                 }
136                 return;
137         }
138         
139         public function event_PostRegister($data)
140         {
141                 if ( $this->enable_security != 'yes' )
142                 {
143                         return;
144                 }
145                 $password = postVar('password');
146                 if( postVar('action') == 'memberadd' )
147                 {
148                         $message = $this->validate_and_messsage($password,$this->pwd_min_length, $this->pwd_complexity);
149                         if ( $message )
150                         {
151                                 $errormessage = _SECURITYENFORCER_ACCOUNT_CREATED. $message. "<br /><br />\n";
152                                 global $admin;
153                                 $admin->error($errormessage);
154                         }
155                 }
156                 return;
157         }
158         
159         public function event_CustomLogin($data)
160         {
161                 if ( $this->enable_security != 'yes' || $this->max_failed_login <= 0 )
162                 {
163                         return;
164                 }
165                 
166                 //login,password,success,allowlocal
167                 global $_SERVER;
168                 $login = $data['login'];
169                 $ip = $_SERVER['REMOTE_ADDR'];
170                 sql_query("DELETE FROM ".sql_table('plug_securityenforcer')." WHERE lastfail < ".(time() - ($this->login_lockout * 60)));
171                 $query = "SELECT fails as result FROM ".sql_table('plug_securityenforcer')." ";
172                 $query .= "WHERE login='".sql_real_escape_string($login)."'";
173                 $flogin = quickQuery($query); 
174                 $query = "SELECT fails as result FROM ".sql_table('plug_securityenforcer')." ";
175                 $query .= "WHERE login='".sql_real_escape_string($ip)."'";
176                 $fip = quickQuery($query); 
177                 
178                 if ( $flogin >= $this->max_failed_login || $fip >= $this->max_failed_login )
179                 {
180                         $data['success'] = 0;
181                         $data['allowlocal'] = 0;
182                         $info = sprintf(_SECURITYENFORCER_LOGIN_DISALLOWED, ENTITY::hsc($login), ENTITY::hsc($ip));
183                         ACTIONLOG::add(INFO, $info);
184                 }
185                 return;
186         }
187         
188         public function event_LoginSuccess($data)
189         {
190                 //member(obj),username
191                 if ( $this->enable_security != 'yes' || $this->max_failed_login <= 0 )
192                 {
193                         return;
194                 }
195                 global $_SERVER;
196                 $login = $data['username'];
197                 $ip = $_SERVER['REMOTE_ADDR'];
198                 sql_query("DELETE FROM ".sql_table('plug_securityenforcer')." WHERE login='".sql_real_escape_string($login)."'");
199                 sql_query("DELETE FROM ".sql_table('plug_securityenforcer')." WHERE login='".sql_real_escape_string($ip)."'");
200                 return;
201         }
202         
203         public function event_LoginFailed($data)
204         {
205                 //username
206                 if ( $this->enable_security != 'yes' || $this->max_failed_login <= 0 )
207                 {
208                         return;
209                 }
210                 global $_SERVER;
211                 $login = $data['username'];
212                 $ip = $_SERVER['REMOTE_ADDR'];
213                 $lres = sql_query("SELECT * FROM ".sql_table('plug_securityenforcer')." WHERE login='".sql_real_escape_string($login)."'");
214                 if ( sql_num_rows($lres) )
215                 {
216                         sql_query("UPDATE ".sql_table('plug_securityenforcer')." SET fails=fails+1, lastfail=".time()." WHERE login='".sql_real_escape_string($login)."'");
217                 }
218                 else
219                 {
220                         sql_query("INSERT INTO ".sql_table('plug_securityenforcer')." (login,fails,lastfail) VALUES ('".sql_real_escape_string($login)."',1,".time().")");
221                 }
222                 $lres = sql_query("SELECT * FROM ".sql_table('plug_securityenforcer')." WHERE login='".sql_real_escape_string($ip)."'");
223                 if ( sql_num_rows($lres) )
224                 {
225                         sql_query("UPDATE ".sql_table('plug_securityenforcer')." SET fails=fails+1, lastfail=".time()." WHERE login='".sql_real_escape_string($ip)."'");
226                 }
227                 else
228                 {
229                         sql_query("INSERT INTO ".sql_table('plug_securityenforcer')." (login,fails,lastfail) VALUES ('".sql_real_escape_string($ip)."',1,".time().")");
230                 }
231                 return;
232         }
233         
234         private function validate_and_messsage($passwd,$minlength = 6,$complexity = 0)
235         {
236                 $minlength = intval($minlength);
237                 $complexity = intval($complexity);
238                 
239                 if ( $minlength < 6 )
240                 {
241                         $minlength = 6;
242                 }
243                 if ( i18n::strlen($passwd) < $minlength )
244                 {
245                         $message = _SECURITYENFORCER_MIN_PWD_LENGTH . $this->pwd_min_length;
246                 }
247                 
248                 if ( $complexity > 4 )
249                 {
250                         $complexity = 4;
251                 }
252                 
253                 $ucchars = "[A-Z]";
254                 $lcchars = "[a-z]";
255                 $numchars = "[0-9]";
256                 $ochars = "[-~!@#$%^&*()_+=,.<>?:;|]";
257                 $chartypes = array($ucchars, $lcchars, $numchars, $ochars);
258                 $tot = array(0,0,0,0);
259                 $i = 0;
260                 
261                 foreach ( $chartypes as $value )
262                 {
263                         $tot[$i] = preg_match("#{$value}#", $passwd);
264                         $i = $i + 1;
265                 }
266                 
267                 if ( array_sum($tot) < $complexity )
268                 {
269                         $message .= _SECURITYENFORCER_PWD_COMPLEXITY . $this->pwd_complexity;
270                 }
271                 return $message;
272         }
273 }