I have a PHP app which takes a user-inputted $imageurl and does the following:
exec('convert "'.$url.'" -thumbnail 80x500 "images/out.jpg"');
Now obviously I have to take some precautions with this to stop users from executing arbitrary code. For example, if the user sets $url to";rm -rf *;" is no good at all.
So for starters I have to filter out " so that no matter what they type in, they can't escape from their input being a parameter to convert. But should I filter out ; as well? I've seen urls with semicolons in them... and while the semicolon is really the danger here, filtering out " would still keep me safe right? But can urls have " in them? And are there any other characters I should watch for?
Maybe instead of filtering characters out I should try to escape them. So should I try to escape every character interpreted specially by the shell? Or just escape " as everything else is sort of "pre-escaped" given that it's inside double-quotes?
Sorry for my rambling confusion, I'm just new at this and want to stay safe!
Thanks,
Mala
Well, if you want to make sure the URL is a URL, use filter_var
filter_var($url, FILTER_VALIDATE_URL);
This will not prevent people from supplying a URL like example.com/foo?;rm -rf though, which is still a valid URL. I'm not sure if this would cause rm to execute, but you could also check the URL with parse_url() and omit the query part.
Generally, it is a good idea to have a look at these as well:
escapeshellarg() — Escape a string to be used as a shell argument
escapeshellcmd() — Escape shell metacharacters
Also see the PHP Manual on securing user input.
You can use the escapeshellarg function.
Use Regular Expressions to ensure that $url only contains valid filename characters, e.g. "(\w\.\-/){1,256}". Plus, I imagine you are renaming the file the user uploads to be a random filename, or at least a whitelisted filename (i.e. using the same regex). sha1.ext or md5.ext are easy formats to use.
Related
So let's say we have a following code:
<?php
$str = addslashes($_GET['str']);
$cmd = 'sometool "'.$str.'"';
system($cmd);
?>
Is it secure? Can I escape from double quotes somehow? The operating system in linux.
Purely theoretical consideration. I don't use it in my code ;)
It's not secure. You can still pass some arguments that will be malicious, i.e. execute other files in system.
$var = '$(sh file.sh)';
$str = addslashes($var);
$cmd = 'sometool "'.$str.'"';
system($cmd);
You should use escapeshellarg method for escaping shell arguments.
$str = escapeshellarg($_GET['str']);
$cmd = 'sometool ' . $str;
system($cmd);
Note that you have to use the argument $str as it is and that it will be a single argument. You must not surround it with quotes ' or double quotes "
Definitively not secure as is,
Like any user's input, you'll have to not only escape quotes, but also verify the conformity of the data being submitted and have to be strict on that.
For e.g.: if the "sometool" command is waiting for a path, you've to ensure that the user input is a valid path. If the path has to be in a restricted location, you've to check that restriction.
Assuming that your "str" has to be a valid string, you can begin with the filter input function.
https://www.php.net/manual/en/function.filter-input.php
Make sure to use the appropriate filters (for quotes, encoding, etc.) : https://www.php.net/manual/en/filter.filters.php.
It will not be possible to by-pass the escaping here.
This somehow ensure the conformity of the data, but still, you've to be sure that the command "sometool" itself will not be affected by any valid string characters or syntax, that's what makes it still unsecure to use.
I know there're plenty of topics regarding escaping characters but I just can't find the solution for my problem.
It's very easy. This is string I have:
$path = "C:\Users\Me\Desktop\14409238.jpg";
Howver, no matter how many escaping techniques I use, I can't manage to display the correct path without destroying it. In all cases the \14 will be replaced with
C:\Users\Me\Desktopd09238.jpg
How do I solve this?
Don't use backslashes in PHP for windows paths. It's smart enough to convert for you:
$path = "c:/users/me/desktop/...";
Using backslashes runs into the exact problem you have - backslashing certain characters turns them into metacharacters, not regular characters.
try to change, the Physical path to access the image, stored on Desktop can be written as,
$path = "C:\Users\Me\Desktop\14409238.jpg";
to
$path = "C:\\Users\\Me\\Desktop\\14409238.jpg";
Avoid the situation entirely, PHP under Windows allows you to submit paths with the backslash
c://Users/Me/Desktop/file.jpg
This also avoids interoperability headaches when a script must run within .nix and Windows.
Suppose that, we're expecting just strings or numbers with the data send by a user. Is it safe enough to check the data with ereg and preg_match functions? Is there a way to fake them? Should we still use mysql_real_escape_string?
This will be short answer...
Use PDO:
Docs: http://php.net/manual/en/book.pdo.php
For example Zend famework is using this engine.
safe enough is relative to your own needs. If you're wanting to avoid mysql_real_escape_string for some reason then I first want to ask why.
My answer is: sure... depending on your conditions
you can preg match against [0-9a-z] and there is nothing to fear. Try passing a multibyte character to be safe. So long as your condition does not allow you to do anything if the match does not fit your requirements then there is no tricky work-around that I know of to slip in malicious characters on such a strict rule.
but the term "string" is very open. does that include punctuation? what kind, etc. If you allow standard injection characters as what you call a "String" then my answer is no longer sure.
But I still recommend mysql_real_escape_string() on all user submitted info, no matter how you try to purify it before hand.
If you use a regex to match against valid input, and it succeeds, then the user input is valid. That being said, if you don't have any malicious characters in valid input (particularly quotes or potentially multibyte characters), then you don't need to call mysql_real_escape_string. The same principle applies to something like:
$user_in_num = intval( $_POST['in_num']); // Don't need mysql_real_escape_string here
So something like the following:
$subject = $_POST['string_input'];
if( !preg_match('/[^a-z0-9]/i', $subject))
{
exit( 'Invalid input');
}
It is fine / safe to use $subject in an SQL query once the preg_match succeeds.
In a contact form, if someone enters the following into the textbox:
<?php echo 'hi'; ?>
I see that the server will not execute it because of an error. What I would like it to do is instead, somehow escape it into plain text and display it correctly. I have seen other sites been able to do this. I originally thought this could be solved by the addslashes() function, but that doesn't seem to work.
Thanks,
Phil
No. Use htmlspecialchars instead. Don't use addslashes.
To be more specific, addslashes bluntly escapes all instances of ', " and \ and NUL. It was meant to prevent SQL injection, but it has no real use in proper security measures.
What you want is preventing the browser to interpret tags as is (and that's entirely different from preventing SQL injections). For instance, if I want to talk about <script> elements, SO shouldn't simply send that string literally, causing to start an actual script (that can lead to Cross-site scripting), but some characters, especially < and >, need to be encoded as HTML entities so they're shown as angle brackets (the same is true for &, that otherwise would be interpreted as the start of an HTML entity).
In your case, output after htmlspecialchars would look like:
<?php echo 'hi'; ?>
Use htmlspecialchars before outputing anything provided by the user. But in this case, also make sure that you do not execute anything the user inputs. Do not use eval, include or require. If you save the user data to a file, use readfile or file_get_contents+htmlspecialchars instead of include/require. If you're using eval, change it into echo and so on.
I've run into a bit of a problem with a Regex I'm using for humans names.
$rexName = '/^[a-z' -]$/i';
Suppose a user with the name Jürgen wishes to register? Or Böb? That's pretty commonplace in Europe. Is there a special notation for this?
EDIT:, just threw the Jürgen name against a regex creator, and it splits the word up at the ü letter...
http://www.txt2re.com/index.php3?s=J%FCrgen+Blalock&submit=Show+Matches
EDIT2: Allright, since checking for such specific things is hard, why not use a regex that simply checks for illegal characters?
$rexSafety = "/^[^<,\"#/{}()*$%?=>:|;#]*$/i";
(now which ones of these can actually be used in any hacking attempt?)
For instance. This allows ' and - signs, yet you need a ; to make it work in SQL, and those will be stopped.Any other characters that are commonly used for HTML injection of SQL attacks that I'm missing?
I would really say : don't try to validate names : one day or another, your code will meet a name that it thinks is "wrong"... And how do you think one would react when an application tells him "your name is not valid" ?
Depending on what you really want to achieve, you might consider using some kind of blacklist / filters, to exclude the "not-names" you thought about : it will maybe let some "bad-names" pass, but, at least, it shouldn't prevent any existing name from accessing your application.
Here are a few examples of rules that come to mind :
no number
no special character, like "~{()}#^$%?;:/*§£ø and probably some others
no more that 3 spaces ?
none of "admin", "support", "moderator", "test", and a few other obvious non-names that people tend to use when they don't want to type in their real name...
(but, if they don't want to give you their name, their still won't, even if you forbid them from typing some random letters, they could just use a real name... Which is not their's)
Yes, this is not perfect ; and yes, it will let some non-names pass... But it's probably way better for your application than saying someone "your name is wrong" (yes, I insist ^^ )
And, to answer a comment you left under one other answer :
I could just forbid the most command
characters for SQL injection and XSS
attacks,
About SQL Injection, you must escape your data before sending those to the database ; and, if you always escape those data (you should !), you don't have to care about what users may input or not : as it is escaped, always, there is no risk for you.
Same about XSS : as you always escape your data when ouputting it (you should !), there is no risk of injection ;-)
EDIT : if you just use that regex like that, it will not work quite well :
The following code :
$rexSafety = "/^[^<,\"#/{}()*$%?=>:|;#]*$/i";
if (preg_match($rexSafety, 'martin')) {
var_dump('bad name');
} else {
var_dump('ok');
}
Will get you at least a warning :
Warning: preg_match() [function.preg-match]: Unknown modifier '{'
You must escape at least some of those special chars ; I'll let you dig into PCRE Patterns for more informations (there is really a lot to know about PCRE / regex ; and I won't be able to explain it all)
If you actually want to check that none of those characters is inside a given piece of data, you might end up with something like that :
$rexSafety = "/[\^<,\"#\/\{\}\(\)\*\$%\?=>:\|;#]+/i";
if (preg_match($rexSafety, 'martin')) {
var_dump('bad name');
} else {
var_dump('ok');
}
(This is a quick and dirty proposition, which has to be refined!)
This one says "OK" (well, I definitly hope my own name is ok!)
And the same example with some specials chars, like this :
$rexSafety = "/[\^<,\"#\/\{\}\(\)\*\$%\?=>:\|;#]+/i";
if (preg_match($rexSafety, 'ma{rtin')) {
var_dump('bad name');
} else {
var_dump('ok');
}
Will say "bad name"
But please note I have not fully tested this, and it probably needs more work ! Do not use this on your site unless you tested it very carefully !
Also note that a single quote can be helpful when trying to do an SQL Injection... But it is probably a character that is legal in some names... So, just excluding some characters might no be enough ;-)
PHP’s PCRE implementation supports Unicode character properties that span a larger set of characters. So you could use a combination of \p{L} (letter characters), \p{P} (punctuation characters) and \p{Zs} (space separator characters):
/^[\p{L}\p{P}\p{Zs}]+$/
But there might be characters that are not covered by these character categories while there might be some included that you don’t want to be allowed.
So I advice you against using regular expressions on a datum with such a vague range of values like a real person’s name.
Edit As you edited your question and now see that you just want to prevent certain code injection attacks: You should better escape those characters rather than rejecting them as a potential attack attempt.
Use mysql_real_escape_string or prepared statements for SQL queries, htmlspecialchars for HTML output and other appropriate functions for other languages.
That's a problem with no easy general solution. The thing is that you really can't predict what characters a name could possibly contain. Probably the best solution is to define an negative character mask to exclude some special characters you really don't want to end up in a name.
You can do this using:
$regexp = "/^[^<put unwanted characters here>]+$/
If you're trying to parse apart a human name in PHP, I recomment Keith Beckman's nameparse.php script.