SMF Development > Bug Reports

Repair Settings can't handle " in the DB password

(1/4) > >>

Arantor:
So I've been working on a little site that I wanted to launch, anyway, the DB password has a " in it.

repair_settings.php chops the password off, making it impossible to actually use, and even if you manually override it each time with the new password, the saved password in Settings.php escapes the " - but since it's inside ' the escape character is included as a literal, so that doesn't work either.

emanuele:
An htmlspecialchars should fix that one I think hope.

--- Code: (find) ---<input type="text" name="', $info[0], 'settings[', $setting, ']" id="', $setting, '" value="', isset($settings[$setting]) ? $settings[$setting] : '', '" size="', $settings_section == 'path_url_settings' || $settings_section == 'theme_path_url_settings' ? '60" style="width: 80%;' : '30', '" class="input_text" />';
--- End code ---

--- Code: (replace with) ---<input type="text" name="', $info[0], 'settings[', $setting, ']" id="', $setting, '" value="', isset($settings[$setting]) ? htmlspecialchars($settings[$setting]) : '', '" size="', $settings_section == 'path_url_settings' || $settings_section == 'theme_path_url_settings' ? '60" style="width: 80%;' : '30', '" class="input_text" />';
--- End code ---

emanuele:
And while we are looking at passwords and repair settings, there is also a problem with semi colons that break the mysql password.
I was thinking to change the regexp:

--- Code: ---^[$]([a-zA-Z_]+)\s*=\s*(["\'])?(.*?)(?:\\2)?;
--- End code ---
to

--- Code: ---^[$]([a-zA-Z_]+)\s*=\s*(["\'])?(.*?)(?:\\2)?([^'].*;)
--- End code ---
but since regexp are scary...

Arantor:
Well, now that I have my setup all working I'm a bit reluctant to test it with anything ;)

But easy to reproduce, just set up a MySQL account with a " in the middle of the password and see what happens.

Not sure about that regex, is complicated.

MrPhil:

--- Quote from: emanuele on June 26, 2012, 11:37:46 AM ---but since regexp are scary...

--- End quote ---


--- Quote ---^[$]([a-zA-Z_]+)
--- End quote ---
Starting at the beginning of the line (do we just assume that everything starts at column 1?), look for a literal dollar sign (could also be \$) followed by a variable name (one or more letters and underscores (aren't digits allowed after the first alpha?). Stick the name (less the $) into variable $1.

Note, since in the future we might want variable names with digits, change this to

--- Code: ---^\$([a-zA-Z_][a-zA-Z0-9_]*)
--- End code ---
As we're using Perl style regexps, this can be simplified to

--- Code: ---^\$([a-zA-Z_]\w*)
--- End code ---
To allow whitespace before a variable name,

--- Code: ---^\s*\$([a-zA-Z_]\w*)
--- End code ---
or you could trim() the element first. Since we're using Perl style, we could also just say a-z and stick an "i" modifier (case insensitive) at the end of the pattern, provided we don't have any places where it might be a case sensitive match.


--- Quote ---\s*=\s*
--- End quote ---
any whitespace, equals sign, any whitespace


--- Quote ---(["\'])?
--- End quote ---
optional " or ' to start the value string. Put into $2 variable (if no match, $2 should be an empty string). Numeric values don't need quotes, so they will be missing here.


--- Quote ---(.*?)
--- End quote ---
0 or more characters, non-greedy match (don't match the closing delimiter here, which comes next, or the semicolon). If the field contains a character matching the string delimiter (if used), it will stop here. Note that this allows $varname=; which is invalid PHP, but presumably that will be caught elsewhere.


--- Quote ---(?:\\2)?
--- End quote ---
match of $2 (the opening delimiter, if it was used). It is "capture less" (not put into $3). The second ? makes it optional for the closing, which I'm not sure is correct. I don't think the second ? should be there, as it permits $var = 'value;. Someone ought to look at that carefully.


--- Quote ---;
--- End quote ---
a literal semicolon is next to match

Note that nothing beyond the ; is matched. As there could be whitespace and even comments following, it would not be good to put a $ here (force match to end of string). I will have to look over repair_settings.php in more detail to see if we need to save leading whitespace and trailing comments for the rewrite of the file.


--- Code: ---^[$]([a-zA-Z_]+)\s*=\s*(["\'])?(.*?)(?:\\2)?([^'].*;)
--- End code ---
This does not handle any leading whitespace before the variable name, nor does it allow variable names containing digits. The closing quote on a string is optional, which is not good. Then you match any one non-' character followed by any number of characters followed by a ;. I suspect that's not what you're aiming for.

We have two problems we're trying to solve:

* A delimiter ' or " inside a string but not escaped. How can it be in a PHP string in the first place? If you happen to have a password with a " in it, either use ' to delimit the string, or escape a " in the middle: \". Is the problem that the escaped \" is not being treated as an ordinary character in this regexp, but as two characters \ and "? Please give an example.
* Semicolons inside the string are doing something bad? Please elaborate. They should not be matched until after the closing delimiter is matched. A semicolon could only be in a string, which means there must be ' or " delimiters around it.I will have to do some experimenting later to see if a Settings.php line with "blah\"blah" is read into the array element as "blah"blah" (escape disappears and the password is chopped off as "blah"). If that's what's happening, the cure could be difficult. I'll have to think about it. I'll report back later unless someone finds a good solution in the next few hours. We may end up with additional code to split up the line and process the pieces using regexp, but not be able to properly handle the whole thing in one regexp.

The following is not tested, but should match

* lines with leading whitespace (the leading whitespace is not saved)
* PHP variables with digits in them (in $1)
* properly constructed strings (' ' or " ") (in $3). A delimiter within the string may still be a problem, even if it's escaped in Settings.php.
* any trailing goop after the semicolon, if you wish to retain comments, etc. (in $4)
--- Code: ---^\s*\$([a-zA-Z_]\w*)\s*=\s*(["\']?(.*?)(?:\\2);(.*)$
--- End code ---

Note that repair_settings.php suffers from the same race condition that SMF's attempt to rewrite the Settings.php on database error does! It empties out the file first, which is a stupid thing to do (needed for some ancient server software). Please consider getting rid of this step in both places!!!!!

Navigation

[0] Message Index

[#] Next page

Go to full version