-
Posts
4,207 -
Joined
-
Last visited
-
Days Won
209
Everything posted by Jacques1
-
Anush, you keep talking about different results depending on whether "there's a session on my site". I have no idea what that means. The example code I gave you doesn't involve any sessions at all, so you've obviously changed something. What did you change? What do you mean by "session on my site"?
-
logging in to a website automatically and retrieving the data
Jacques1 replied to stubarny's topic in PHP Coding Help
It's again a problem with the logic: You're still trying to output the response of the log-in request, but there's nothing to see there. Like I already said, the response probably doesn't say anything. It just tries to redirect you. After the log-in request, you again make a POST request with your credentials, this time to the main page. This doesn't make any sense to me. Shouldn't this be a simple GET request with no parameters at all? Before you write any line of code, get clear about what you want to do. Write it down or draw a diagram, if necessary. Then do the implementation. Don't start with a bunch of random code and try to fix it by trial-and-error. -
logging in to a website automatically and retrieving the data
Jacques1 replied to stubarny's topic in PHP Coding Help
Not sure if you understand the general logic of a log-in. The whole point of sending your credentials to the log-in script is to get back a session cookie pointing to an authenticated session. The body of the response is irrelevant. It's usually indeed empty, because the script tries to redirect you to the main page or whatever. So what you want is retrieve the session cookie, store it and then make another request with the cookie to the actual target page. To store cookies, you need the CURLOPT_COOKIEJAR parameter. This is an arbitrary file path. To load cookies from a file and include them in the request, you need CURLOPT_COOKIEFILE. -
Since terungwa just wants the highest score, there's no need for sorting the array. A simple loop is enough: <?php $student_scores = array( array( 'student' => 'John', 'score' => '20' ), array( 'student' => 'Mary', 'score' => '35' ), array( 'student' => 'Samuel', 'score' => '5' ), ); $max_score = null; foreach ($student_scores as $student_score) { if (is_null($max_score) || $student_score['score'] > $max_score) { $max_score = $student_score['score']; } } If the summary comes after the table, you can even do this in the main loop. If you want “the” student with the highest score, you first need to define an additional unique criterion in case multiple students have the same score.
-
Yes, many people struggle with those functions. I find this a bit surprising, because for the most part, the name already tells you the purpose. There are several pitfalls, though. You should forget about the following functions: stripslashes() is bullshit and damages your data. Do not use this. In the early days of PHP, there used to be a bug/feature called “Magic Quotes” which automatically applied a kind of primitive SQL-escaping to external input. The idea was to help newbies get their queries right, because many people didn't know anything about escaping and just dropped their URL parameters etc. straight into the query string. Of course this “feature” also meant that you had to remove all the backslash characters whenever you wanted to use the string for something other than a query. This is what stripslashes() does. However, the whole idea of blanket escaping is obviously naïve and technically flawed, so it was frowned upon for many years, then officially deprecated and finally removed. “Magic Quotes” should have become extinct by now. Unfortunately, people still use stripslashes() as a kind of ritual or simply because they've copied and pasted some really, really old code. This is downright harmful, because if there are no backslashes from “Magic Quotes”, then stripslashes() will delete actual backslashes from the string content. htmlentities() is nonsense, do not use it. This function replaces all characters for which there's a named HTML entity. So, for example, the umlaut “ö” becomes “ö”. This may have been useful back in the 90s when Unicode was still new and not well-supported. But in the year 2014, we have “modern” encodings like UTF-8 and no longer need those workarounds. It's a waste of resources. While we're at it: Don't use strip_tags() either. It's an attempt at writing an HTML filter, but it's so primitive that you're likely to mangle the entire input. This function solves your HTML problems in the same way that decapitation cures a headache. There are better alternatives to the following functions: mysql_real_escape_string() is only necessary if you still use the old mysql_* functions. Those are, again, hopelessly outdated and will be removed in one of the next PHP releases. Didn't you see the big red warning signs in the PHP manual? Nowadays, we use modern database interfaces like PDO. Since PDO supports parameterized queries, manual SQL-escaping is mostly a thing of the past. It's also extremely cumbersome and error-prone and should be avoided at all cost. You do need the following functions: htmlspecialchars() escapes characters like “<” and “>” which have a special meaning in HTML. You absolutely must use this function whenever you want to insert a value into your HTML document. Otherwise, your users can inject arbitrary HTML markup into your page, including malicious script elements (see cross-site scripting). Unfortunately, htmlspecialchars() is very difficult to use, and even experienced developers constantly get it wrong. At the very least, you have to define the correct character encoding and set the ENT_QUOTES flag. For example: htmlspecialchars($raw_input, ENT_QUOTES, 'UTF-8'). urlencode() is required whenever you want to insert a value into an URL. Many characters like “/” or “?” have a special meaning within a URL, so if you want to use them as literal characters, you need to encode them. For example, you can't just write "https://yoursite.com/account.php?user=" . $username . You need "https://yoursite.com/account.php?user=" . urlencode($username) .
-
The symptoms you describe (getting the filename as a POST parameter rather than the actual file) occur when you screw up the enctype. Make a minimal example with nothing but a form and a script which inspects the incoming data: <?php echo 'Content of $_FILES:<br>'; var_dump($_FILES); echo 'Content of $_POST:<br>'; var_dump($_POST); ?> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Upload</title> </head> <body> <form method="post" enctype="multipart/form-data"> <input type="file" name="test_file"> <input type="submit"> </form> </body> </html> So what happens when you submit this form?
-
A few things: You still need to quote the second string literal in $_FILES['csv'][size] and $_FILES['csv'][tmp_name]. The unquoted word size or tmp_name is interpreted as a constant. That's obviously not what you mean, and of course those constants don't exist anywhere in your script. PHP will emit a warning and then assume you meant a string. This does lead to the correct result eventually, but it's really not the proper way of doing it. String literals always need quotes (the only exception is an index within a double-quoted string if you don't use the curly brace syntax; but that's an edge case). You should initialize $errors with false, not an empty string. You keep overwriting $error_report['error'] with different messages. If you want an array of multiple messages, you need to append them like before: $error_report[] = ... The $success variable at the end is a bit “surprising” and won't be defined at all if the condition isn't met. Better initialize it before the if statement or even before the loop. If you initialize it before the loop, you can actually set it to true right in the loop and don't need any extra checks. Apart from that, the code looks good.
-
“Sanitzing” the input with addslashes() makes no sense and will only damage your data, because you prepend a literal backslash to every quote, backslash and a few other characters. A prepared statement already prevents the input parameters from causing any harm, so there's no need for any extra stuff. Also note that addslashes() is not suitable for escaping, anyway, because it doesn't take the character encoding into account. It simply assumes that the input it an ASCII string, which obviously isn't always true. In some cases, you may end up with no protection at all. So get rid of the addslashes() calls – and forget this function. Then you should create the prepared statement outside of the loop. That's really one of the main features of prepared statements: You create them once and then execute them as often as you want. Another problematic practice is that you use the magical string “YES” as an error indicator. This can easily lead to total chaos if the code becomes more complex and you accidentally use different variations (“YES”, “yes”, “Yes”, “TRUE” etc.). A simple yes/no is what booleans are for. If you have more than two states, use constants.
-
optional form field validation with patterns
Jacques1 replied to michaellunsford's topic in HTML Help
Not sure what the .{0} is supposed to do. The pattern for “nothing” is simply ... no pattern. And why the brackets around \d? The \d itself is a character class. The pattern of the OP already solves the problem. What makes you two think it doesn't? It accepts either an empty string or exactly 5 digits. Note, however, that you normally need to use anchors to match the beginning and end of the string. If you just write, say, \d{2}, then this matches any string which includes two digits following each other. If you want a string which only consists of two digits, you need ^\d{2}$. In your case, however, it's not needed since the pattern corresponds with the maximum input length. -
Allow only letters, numbers, dash, underscore, or space
Jacques1 replied to LLLLLLL's topic in Regex Help
This thread is from last year and has been solved already. What's the point of digging it out? This isn't really the perfect solution either: You've forgotten the uppercase characters. The underscore doesn't have any meaning, so no need to escape it. A dash at the end of a character class doesn't have any meaning either, so again no need to escape it. If you do use escaping, make it a double backslash, because the regex will be inside a PHP string. What's the point of the parentheses? The \s class contains tabs and newlines as well, which the OP did not want. Why not use the \w class as suggested above? -
The error happens if you call the script without uploading a file, because you incorrectly assume that $_FILES['uploaded']['name'] is always defined. Maybe you actually meant to check for isset($_FILES['uploaded'])?
-
How arrays accessed without defining an array?
Jacques1 replied to eldan88's topic in PHP Coding Help
PHP automatically turns a variable into an array when you treat it as an array. You can easily try it out: <?php for ($i = 0; $i < 10; $i++) $some_var[] = 'foo'; var_dump($some_var); I wouldn't recommend this, though. As you can see, it's very confusing, and the type of the variable remains unknown until the array syntax is used. It's much clearer to explicitly initialize the variable with an empty array. -
I don't have a concrete tutorial at hand, but if you search for something like “secure file upload”, you'll get a good overview. Unfortunately, file uploads are tricky. It seems trivial to save some files on your server, but doing this correctly is pretty hard. Even experienced developers often get this wrong. You're facing two major risks: People may upload server-side scripts and use them to attack you. Or they might upload client-side scripts and attack your users. To prevent attackers from uploading and executing server-side scripts, you need to take the following steps: Do not let the user choose the filename. Make a whitelist of acceptable extensions (e. g., “.jpg”, “.jpeg”, “.gif” and “.png”) and validate the uploaded file against this list. If it's valid, then you assemble the filename from the user ID and the extension. Otherwise, you reject the file. Make sure your webserver will never execute scripts within the upload folder. As a more robust alternative, save the uploaded files outside of the document root and serve them through an extra script. Preventing attacks against your users is even harder. What's important to understand is that even a perfectly valid image can contain malicious JavaScript code. For example, the JPEG format supports arbitrary text comments. If an attacker creates such an image, every validator and even manual inspection will tell you that it's fine. Yet still it's a script waiting to be executed by the victim. Take the following steps: If there's any chance you can get a separate domain, use that to serve the uploaded images. It's one of the few reliable counter-measures, because the same-origin policy will isolate the attack on this domain and not let JavaScript access your actual site. Make sure the files are served with the correct MIME type (like image/jpeg). This is what makes the difference between a harmless image and malicious JavaScript code. Use Content Security Policy to tell modern browsers that no scripts should be executed. Note that some particularly shitty browsers like IE before version 8 will still execute embedded scripts, even if you explicitly tell them to interpret the file as an image. The only barrier in this case would be the same-origin policy, so definitely try to get an extra domain.
-
Yes. The same happens if you get your e-mail address wrong in the registration form. But what's a minor inconvenience in a few cases compared to exposing the e-mail addresses of everybody? If the addresses become a target for spam, then it's irrelevant what kind of website leaked them. For that matter, no, I don't think any website may expose e-mail addresses without the explicit consent of its users. Even the smallest and most irrelevant site is responsible for keeping its data safe. So? eBay also thinks it's a good idea to limit the password to only 20 bytes and block pasting, which means I can't use a passphrase and can't use my password manager without a workaround. Many websites, including those of large companies, do incredibly stupid things. That doesn't mean we should do the same.
-
You're hashing the password client-side? This is useless*. It means that the hash is the password. Anybody who knows the SHA-512 hash can log in, regardless of whether or not they know the original password. All you're doing is replace the user-chosen password with a different password. So in any case, you need to send the password to the server via HTTPS and then hash it with bcrypt. There are some alternative authentication schemes, but they're too complex for an average website. * For the record: Client-side hashing in addition to bcrypt hashing on the server does have a small benefit for the user, because it conceals their original password choice. If the same password is used on different websites, then an attacker who obtains the SHA-512 hash at least won't get free access to those other sites. But they do get access to your site. And as already explained, SHA-512 doesn't provide much protection, anyway. The difference is that you allow anybody to check who's in your database. I only let people check their own address. If your site is online, I could go to it right now and start trying out different e-mail addresses. Each time, your application would tell me immediately whether or not this address is registered. That's obviously a violation of privacy. On the other hand, I don't tell visitors anything about an e-mail address. All I do is send a message to the address so that the owner knows if they're registered. This does not create any security risk at all. If the address is not registered, then I only tell the user exactly that: “Sorry, this e-mail address is not registered in our system. Maybe you've used a different address?” Of course this does mean that my application might occasionally send unwanted e-mails to people who have nothing to do with my site. But every registration procedure has the exact same problem: Anybody can enter an arbitrary e-mail address, and then this person will get a message about their registration. There's not much we can do about that.
-
That means your application exposes the e-mail addresses of your users. You're allowing anybody to check which e-mail addresses are registered at your website. Hashing passwords with SHA-512 is not acceptable, regardless of whether you salt them or not. Current PCs can calculate hundreds of millions of SHA-512 hashes per second, making it easy for an attacker to find out the passwords with simple brute force. This is particularly bad if the users are likely to choose weak passwords, which is probably the case for older people. You need an actual password hash algorithm like bcrypt. Salting the tokens and hashing them with a relatively slow algorithm like SHA-512 is nonsensical and only wastes resources. Maybe you should read the other thread until the end, because we've discussed those very mistakes.
-
We've already discussed this just a few threads before yours: hashing a token for password reset. I'm not gonna repeat everything, so just a few problems of your scheme: What if the e-mail address does not exist? Do you simply do nothing? Then a user who accidentally entered the wrong address will wait forever for the password reset mail. Do you show an error message? Then you leak private data. The only solution is to send an e-mail in any case. If the address isn't registered, you explain that in the e-mail. The password reset token is equivalent to a password, so you may only store a hash of it in the database. Do not store the plaintext token. “encrypt password”? I hope you meant that you hash all passwords with bcrypt? Are you sure your rest tokens are random? That's usually the biggest problem. The output of functions like rand(), mt_rand() or uniqid() is not random and can be predicted with a bit of effort.
-
I find it interesting that there's still this strong belief in application security when every single day proves the opposite. How many more database leaks, SQL injections, cross-site scripting attacks etc. do we still need? Do we have to wait until every web developer on this planet personally got “hacked”? I don't mean this personally, but it's a bit tiring to see the same battles being fought since more than 10 years. No, hashing passwords with MD5 or SHA is not acceptable, and this cute 5-character salt almost looks like they're making fun of the whole problem. In fact, they're not even using proper MD5 hashes. They're using this: $hash = md5( md5( $salt ) . md5( $password ) ); Well, yeah. I guess that was acceptable back in the 90s when people simply didn't know any better and came up with all kinds of silly ideas. But for current software used in production: no. This stuff is bullshit, and there's no excuse for it. It doesn't matter how much brute-force protection mumbo jumbo the site uses. In fact, I often think that those fancy extra features do more harm than good, because they create a false sense of security: If a password doesn't even survive a harmless online attack, then it won't survive anything. The very first database leak will reveal it. I think we should take a more honest and realistic approach: The only effective protection against brute-force attacks is a strong password hashed with a strong algorithm. Things like account locks, IP blacklists and whatnot are nice to have and give users a warm and fuzzy feeling, but in no way can you rely upon them. It's a good idea to assume that an attacker has direct access to the database. For that matter, I don't really like the term “last line of defense”. It's the only defense. Everything else is just a thin layer of extra protection.
-
I was actually taking about token hashes, not password hashes. If you use a standard hash algorithm like SHA-2 for passwords, that's a very, very bad idea. An old gamer GPU like the HD 6990 can calculate around 1.5 billion(!) SHA-256 hashes per second. And the boom of cryptocurrencies has made the situation even worse (for us). An attacker willing to spend 400 € on an ASIC can easily get 200 billion hashes per second. Given this massive computing power, it's simply irrelevant whether the SHA-2 hashes are salted or not. The attacker just needs a bit more time or money. You're also forgetting one thing in your scheme: While the user ID and the e-mail address may be unique in your database, they're certainly not globally unique. That means an attacker going after an unrelated website somewhere in the world may be able to attack your hashes for free. So this is much less secure than an actual random salt. Long story short: This is completely unsuitable for password protection. You need an actual password hash algorithm like bcrypt. The main feature of bcrypt is that the hashes are extremely expensive to compute, making brute-force attacks much harder. bcrypt also generates the salt automatically and stores it in the output string, so you don't have to take care of that yourself.
-
Hi, you don't need a salt. The sole purpose of a salt is to protect weak input like user-provided passwords against certain attacks by adding randomness. Since your token is already completely random (if you've generated it correctly), salting it is pointless. The token basically has the salt built in. I'm not sure what davidannis means by using data like the e-mail address as a “salt”. That's not a salt, and I don't see the point of this. There's also no reason for using SHA-512. While this may be slightly better than, say, MD5 when dealing with user passwords, it has no benefit for random tokens and only wastes resources. MD5 is just fine and much more efficient. Or SHA-256. It also seems you're hashing the encoded token, e. g. 32 hexadecimal characters rather than the original 16 bytes. That's not necessarily a problem, but it's somewhat more sensible to hash the raw bytes and not the human-friendly representation of them. Last but not least, there's no need to pass the user ID along with the token. The token itself is already a unique identifier. Personally, I use something like this for password reset tokens: The random number generator: function pseudo_random_bytes($length) { if (is_int($length) && $length > 0) { $random_bytes = null; // Try to find a randomness source. if (extension_loaded('mcrypt')) { $random_bytes = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM); } elseif (extension_loaded('openssl')) { $random_bytes = openssl_random_pseudo_bytes($length); } else { /* * As the last resort, try to read from /dev/urandom. Since this is * an OS-specific device, make sure to suppress errors. */ $random_bytes = @file_get_contents('/dev/urandom', false, null, -1, $length); } if ($random_bytes) { return $random_bytes; } else { trigger_error('No randomness source available. Need Mcrypt extension or OpenSSL extension or /dev/urandom', E_USER_ERROR); } } else { trigger_error('Number of random bytes must be a positive integer.', E_USER_ERROR); } } Generating a token: <?php $raw_token = pseudo_random_bytes(16); // this is sent to the user $encoded_token = bin2hex($raw_token); // this is stored in the database $token_hash = hash('sha256', $raw_token); Checking a token: define('TOKEN_LENGTH', 32); $valid_token = false; if (isset($_GET['token']) && ctype_xdigit($_GET['token']) && strlen($_GET['token']) == TOKEN_LENGTH) { $raw_input_token = hex2bin($_GET['token']); // this is what you search for in the database $input_token_hash = hash('sha256', $raw_input_token); }
-
The PHP manual explains it in the chapter about strings. A more exotic use case for this syntax is also variable variables.
-
The script allows anybody to use your server as a spam relay. By injecting a BCC header, an attacker can send the e-mail to arbitrary accounts. You may have been lucky so far, but bots regularly scan websites for vulnerabilities like this. And once they find you, your server will quickly be blacklisted, which means you won't be able to send any mails -- your hoster also won't be happy about it. Do not use random scripts you found somewhere on the Internet. You wouldn't download and run arbitrary .exe files on your PC, right? Then why do you download and run arbitrary PHP scripts? That stuff is at least 10 years old, and there's absolutely no reason to believe that it's credible. As we've just found out, it's not. If you want to send e-mails, use an established library like PHPMailer. This will take care of the technical details and make sure you don't end up flooding innocent people with spam. Do not use the mail() function unless you have deep knowledge about the underlying mechanisms and a very good reason why you need low-level access to the raw SMTP message.
-
The code is not secure, and it was never really intended to be. It's a rather badly named PDO tutorial for beginners, not a login script for actual use in production. Another member of the forum made a more serious attempt of implementing this: http://forums.devshed.com/php-faqs-stickies-167/advanced-login-system-password-forgot-955871.html But it's generally a bad idea to just copy and paste code in the hopes that it will do what you expect, especially when it's security-related code. Of course you can use this for inspiration, but the bottom line is, learn PHP and write your own code.
-
Did you see that three people have already replied? So what exactly do you not understand about our suggestions? How the URL looks like is irrelevant for the general logic. cURL doesn't care if the path is “/my_login_script.php” or “/my/login/script”.
-
Two Queries and a variable from one to the other
Jacques1 replied to Aero77's topic in PHP Coding Help
Then why do you use MySQLi? That's yet another database interface. Sorry, but the whole architecture just doesn't make sense. You're working with one database, right? Then why do you try to access it in three different ways at the same time? Before we jump into the calculations details, it's really more important to get the basics right.