dolibarr 24.0.0-beta
securitycore.lib.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2024 Laurent Destailleur <eldy@users.sourceforge.net>
3 * Copyright (C) 2026 MDW <mdeweerd@users.noreply.github.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 * or see https://www.gnu.org/
18 */
19
28define('MAIN_SECURITY_REVERSIBLE_ALGO', 'AES-256-CTR');
29
30
39function isHTTPS()
40{
41 $isSecure = false;
42 if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
43 $isSecure = true;
44 } elseif (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' || !empty($_SERVER['HTTP_X_FORWARDED_SSL']) && $_SERVER['HTTP_X_FORWARDED_SSL'] == 'on') {
45 $isSecure = true;
46 }
47 return $isSecure;
48}
49
64function dolEncrypt($chain, $key = '', $ciphering = '', $forceseed = '', $obfuscationmode = 'dolcrypt')
65{
66 global $conf;
67 global $dolibarr_disable_dolcrypt_for_debug;
68
69 if ($chain === '' || is_null($chain)) {
70 return '';
71 }
72
73 $reg = array();
74 if (preg_match('/^(dolobfuscationv1[^:]+|dolcrypt):([^:]+):(.+)$/', $chain, $reg)) {
75 // The $chain is already an encrypted string
76 return $chain;
77 }
78
79 if (empty($key)) { // This may happen only with $obfuscationmode = 'dolcrypt'
80 if (!empty($conf->file->dolcrypt_key)) { // This code was to prepare a renaming of option but has been abandoned. Note: this param was never been set for the moment.
81 $key = $conf->file->dolcrypt_key;
82 } else {
83 // We fall back on the instance_unique_id (coming from $dolibarr_main_instance_unique_id, for backward compatibility).
84 $key = $conf->file->instance_unique_id;
85 }
86 }
87 if (empty($ciphering)) {
88 $ciphering = constant('MAIN_SECURITY_REVERSIBLE_ALGO');
89 }
90
91 $newchain = $chain;
92
93 if (function_exists('openssl_encrypt') && empty($dolibarr_disable_dolcrypt_for_debug)) {
94 if (empty($key)) {
95 return $chain;
96 }
97
98 $ivlen = 16;
99 if (function_exists('openssl_cipher_iv_length')) {
100 $ivlen = openssl_cipher_iv_length($ciphering);
101 }
102 if ($ivlen === false || $ivlen < 1 || $ivlen > 32) {
103 $ivlen = 16;
104 }
105 if (empty($forceseed)) {
106 $ivseed = dolGetRandomBytes($ivlen);
107 } else { // This case has been abandoned
108 $ivseed = dol_substr(md5($forceseed), 0, $ivlen, 'ascii', 1);
109 }
110
111 // If $key is a string with several keys, we keep only the first one (the other are alternative to use to decode)
112 $key = preg_replace('/,.*$/', '', $key); // Remove content after the ",".
113
114 $newchain = openssl_encrypt($chain, $ciphering, $key, 0, $ivseed);
115
116 return $obfuscationmode.':'.$ciphering.':'.$ivseed.':'.$newchain;
117 } else {
118 return $chain;
119 }
120}
121
133function dolDecrypt($chain, $key = '', $patterntotest = '')
134{
135 global $conf;
136
137 if ($chain === '' || is_null($chain)) {
138 return '';
139 }
140
141 $savkey = $key;
142
143 if (empty($key)) {
144 if (!empty($conf->file->dolcrypt_key)) {
145 // If dolcrypt_key is defined, we used it in priority. Note: this param has never been set for the moment.
146 $key = $conf->file->dolcrypt_key;
147 } else {
148 // We fall back on the instance_unique_id (coming from $dolibarr_main_instance_unique_id, for backward compatibility).
149 $key = !empty($conf->file->instance_unique_id) ? $conf->file->instance_unique_id : "";
150 }
151 }
152
153 $reg = array();
154
155 // Old method (no more used, kept for compatibility)
156 if (preg_match('/^crypted:(.+)$/', $chain, $reg)) {
157 return dol_decode($reg[1]);
158 }
159
160 // New method
161 if (preg_match('/^dol[^:]+:([^:]+):(.+)$/', $chain, $reg)) {
162 // Do not enable this log, except during debug
163 //dol_syslog("We try to decrypt the chain: ".$chain, LOG_DEBUG);
164
165 $ciphering = $reg[1];
166 if (function_exists('openssl_decrypt')) {
167 if (empty($key)) {
168 dol_syslog("Error dolDecrypt decrypt key is empty", LOG_WARNING);
169 return $chain;
170 }
171 $tmpexplode = explode(':', $reg[2]);
172 if (!empty($tmpexplode[1])) {
173 $data = $tmpexplode[1];
174 $iv = $tmpexplode[0];
175 } else {
176 $data = (string) $tmpexplode[0];
177 $iv = '';
178 }
179
180 $keys = explode(',', $key);
181
182 $newchain = '';
183
184 // Loop on each possible keys (usually one, but can be more in future if we have a list of keys)
185 foreach ($keys as $tmpkey) {
186 $newchain = openssl_decrypt($data, $ciphering, $tmpkey, 0, $iv);
187 if (!empty($patterntotest) && preg_match('/^'.preg_quote($patterntotest, '/').'/', $newchain)) {
188 break; // decoding is ok, we stop the loop.
189 }
190 if (ascii_check($newchain)) {
191 break; // decoding seems ok, we stop the loop (1rst key is main key, the other one are alternative we can use if we have a pattern to test the decoding).
192 }
193 }
194
195 // Test validity of decryption
196 if (!ascii_check($newchain)) {
197 if (empty($savkey)) {
198 dol_syslog("Error dolDecrypt failed: The key dolibarr_main_dolcrypt or dolibarr_main_instance_unique_id, found in conf.php file, is the the one used to encrypt this encrypted string", LOG_ERR);
199 } else {
200 dol_syslog("Error dolDecrypt failed: The string decoded with the key return a non valid value (not ascii)", LOG_ERR);
201 }
202 return $chain;
203 }
204 } else {
205 dol_syslog("Error dolDecrypt openssl_decrypt is not available", LOG_ERR);
206 return $chain;
207 }
208
209 return $newchain;
210 } else {
211 return $chain;
212 }
213}
214
235function dol_hash($chain, $type = '0', $nosalt = 0, $mode = 0)
236{
237 // No need to add salt for password_hash
238 if (($type == '0' || $type == 'auto') && getDolGlobalString('MAIN_SECURITY_HASH_ALGO') == 'password_hash' && function_exists('password_hash')) {
239 // if string contains a null character that can't be encoded. Return an error instead of fatal error.
240 if (strpos($chain, "\0") !== false) {
241 if ($mode == 1) {
242 return array('pass_encrypted' => 'Invalid string to encrypt. Contains a null character', 'pass_encoding' => '');
243 } else {
244 return 'Invalid string to encrypt. Contains a null character.';
245 }
246 }
247
248 // Build a password hash with default algorithm
249 if ($mode == 1) {
250 return array('pass_encrypted' => password_hash($chain, PASSWORD_DEFAULT), 'pass_encoding' => 'password_hash');
251 } else {
252 return password_hash($chain, PASSWORD_DEFAULT);
253 }
254 }
255
256 // Salt value
257 if (getDolGlobalString('MAIN_SECURITY_SALT') && $type != '4' && $type !== 'openldap' && empty($nosalt)) {
258 $chain = getDolGlobalString('MAIN_SECURITY_SALT') . $chain;
259 }
260
261 if ($type == '1' || $type == 'sha1') {
262 if ($mode == 1) {
263 return array('pass_encrypted' => sha1($chain), 'pass_encoding' => 'sha1');
264 } else {
265 return sha1($chain);
266 }
267 } elseif ($type == '2' || $type == 'sha1md5') {
268 if ($mode == 1) {
269 return array('pass_encrypted' => sha1(md5($chain)), 'pass_encoding' => 'sha1md5');
270 } else {
271 return sha1(md5($chain));
272 }
273 } elseif ($type == '3' || $type == 'md5') { // For hashing with no need of security
274 if ($mode == 1) {
275 return array('pass_encrypted' => md5($chain), 'pass_encoding' => 'md5');
276 } else {
277 return md5($chain);
278 }
279 } elseif ($type == '4' || $type == 'openldap') {
280 if ($mode == 1) {
281 return array('pass_encrypted' => dolGetLdapPasswordHash($chain, getDolGlobalString('LDAP_PASSWORD_HASH_TYPE', 'md5')), 'pass_encoding' => 'ldappasswordhash'.getDolGlobalString('LDAP_PASSWORD_HASH_TYPE', 'md5'));
282 } else {
283 return dolGetLdapPasswordHash($chain, getDolGlobalString('LDAP_PASSWORD_HASH_TYPE', 'md5'));
284 }
285 } elseif ($type == '5' || $type == 'sha256') {
286 if ($mode == 1) {
287 return array('pass_encrypted' => hash('sha256', $chain), 'pass_encoding' => 'sha256');
288 } else {
289 return hash('sha256', $chain);
290 }
291 } elseif ($type == '6' || $type == 'password_hash') {
292 if ($mode == 1) {
293 return array('pass_encrypted' => password_hash($chain, PASSWORD_DEFAULT), 'pass_encoding' => 'password_hash');
294 } else {
295 return password_hash($chain, PASSWORD_DEFAULT);
296 }
297 } elseif (getDolGlobalString('MAIN_SECURITY_HASH_ALGO') == 'sha1') {
298 if ($mode == 1) {
299 return array('pass_encrypted' => sha1($chain), 'pass_encoding' => 'sha1');
300 } else {
301 return sha1($chain);
302 }
303 } elseif (getDolGlobalString('MAIN_SECURITY_HASH_ALGO') == 'sha1md5') {
304 if ($mode == 1) {
305 return array('pass_encrypted' => sha1(md5($chain)), 'pass_encoding' => 'sha1md5');
306 } else {
307 return sha1(md5($chain));
308 }
309 }
310
311 // No particular encoding defined, use default
312 if ($mode == 1) {
313 return array('pass_encrypted' => md5($chain), 'pass_encoding' => 'md5');
314 } else {
315 return md5($chain);
316 }
317}
318
331function dol_verifyHash($chain, $hash, $type = '0')
332{
333 if ($type == '0' && getDolGlobalString('MAIN_SECURITY_HASH_ALGO') == 'password_hash' && function_exists('password_verify')) {
334 // Try to autodetect which algo we used
335 if (! empty($hash[0]) && $hash[0] == '$') {
336 return password_verify($chain, $hash);
337 } elseif (dol_strlen($hash) == 32) {
338 return dol_verifyHash($chain, $hash, '3'); // md5
339 } elseif (dol_strlen($hash) == 40) {
340 return dol_verifyHash($chain, $hash, '2'); // sha1md5
341 }
342
343 return false;
344 }
345
346 return dol_hash($chain, $type) == $hash;
347}
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
dol_substr($string, $start, $length=null, $stringencoding='', $trunconbytes=0)
Make a substring.
ascii_check($str)
Check if a string is in ASCII.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
print $langs trans("Show") . '< td style="' . $timeColor . '" align="center"> s</td > badge status0 badge status4 badge status3 Error badge status8< td align="center">< span class="badge ' . $badge . '"></span ></td >< td align="center">< a href="#" class="button button-small" onclick="openLogModal(this)" data-req="' . dol_escape_htmltag($reqSafe) . '" data-res="' . dol_escape_htmltag($resSafe) . '" data-err="' . dol_escape_htmltag($errSafe) . '">< span class="fa fa-search-plus"></span ></a ></td ></tr >< tr >< td colspan="' . $colspan . '" class="opacitymedium"></td ></tr ></table ></div ></form > logModal none logModal none s a JSON string
buildzip.php
dolGetRandomBytes($length)
Return a string of random bytes (hexa string) with length = $length for cryptographic purposes.
dol_decode($chain, $key='1')
Decode a base 64 encoded + specific delta change.
dolGetLdapPasswordHash($password, $type='md5')
Returns a specific ldap hash of a password.
dol_hash($chain, $type='0', $nosalt=0, $mode=0)
Returns a hash (non reversible encryption) of a string.
dolDecrypt($chain, $key='', $patterntotest='')
Decode a string with a symmetric encryption.
isHTTPS()
Return if we are using a HTTPS connection Check HTTPS (no way to be modified by user but may be empty...
dol_verifyHash($chain, $hash, $type='0')
Compute a hash and compare it to the given one For backward compatibility reasons,...
dolEncrypt($chain, $key='', $ciphering='', $forceseed='', $obfuscationmode='dolcrypt')
Encode a string with a symmetric encryption.