Jump to content

Jacques1

Members
  • Posts

    4,207
  • Joined

  • Last visited

  • Days Won

    209

Everything posted by Jacques1

  1. If the address database is not an option, consider binding the free product to something that is harder to get than an e-mail address and relatively easy to verify – like a (mobile) phone number.
  2. What makes you think that they are safe? JSON encoders have absolutely nothing to do with XSS protection. They create JSON documents, nothing more, nothing less. If you think that JSON itself is somehow inherently safe, you're wrong: <?php $json = '["</script><script>alert(123);</script>"]'; ?> <script> var x = <?= $json ?>; </script> The “</script>” within the JSON string terminates the script element and allows the user to manipulate the HTML markup. Occasionally a JSON encoder does have a kind of “magic quotes” feature to help developers who aren't aware of the problem. PHP currently has it as well. But relying on that is a very, very bad idea – just like the actual “magic quotes”.
  3. There's nothing you can do about the physical address except providing a drop-down menu instead of a text field. This requires a complete address database, of course. If you try to remove spaces, people will use abbreviations, similar-looking characters, random middle names and other variations which still result in a valid address. Why do you even care?
  4. No, they can't just add spaces, because then the confirmation mail will never reach them. The only reason why they can add random spaces right now is because you don't even check if the e-mail address exists.
  5. Never, I repeat, never just dump some PHP value into a JavaScript context. This will almost inevitably end in a cross-site scripting vulnerability. The best way to pass a PHP value to JavaScript is Ajax: Within your JavaScript you make an HTTP request to the PHP script, parse the JSON response with JSON.parse() and then use the data. Modern JavaScript frameworks like jQuery can do this automatically: $.getJSON('path/to/your/script.php', function (response_data) { // response_data is the array from your PHP script });
  6. The only way to stop people from registering multiple times is to personally verify their identity (through the ID, the driver's licence or whatever). Checking the IP or e-mail address is pointless, because anybody can have as many IP addresses and e-mail accounts as they want. There's no way you could change that. The best you can do is make sure that the e-mail address actually exists: Send a confirmation e-mail with a random number to the provided address. If the user doesn't confirm the registration within a certain time frame, you may cancel the registration.
  7. It sounds like you just accept the e-mail address and never actually send a mail to it. Is that true?
  8. I don't think it makes sense to write a bunch of random garbage code (your words) and then try to somehow debug it. This strategy may work for simple tasks, but it hardly works for actual web applications. So before you do anything, it's important that you fully understand how sessions work. Read the manual, search the web, and if there's still something you don't understand, ask.
  9. You've asked the question at least twice on this forum and twice on stackoverflow, yet the code is still completely wrong. That means you're either not listening, or you're listening to the wrong people. But if you're happy with the code, well, that's up to you.
  10. A VARCHAR(n) doesn't allocate space for the unused characters. It does allocate one or two extra bytes to store the length, but that's of course irrelevant. However, the problem of your VARCHAR(255) is that you lose type safety: MySQL will accept any string up to 255 characters, so if there's a bug in your application, you may end up storing tons of invalid hashes and not realize it. This cannot happen with a proper CHAR(60), because in that case, MySQL will actually warn you or even reject the data (in strict mode) if the input string is too long. So I strongly recommend that you always use the correct size. This is also important for us, the programmers, because an explicit length tells us immediately what the application expects. Yes, something like that. Of course it doesn't have to be a PHP file, it might as well be XML, JSON, INI or whatever format you prefer. If you already have a configuration mechanism, simply add the hash parameters.
  11. The password reset must be done with the actual token, yes. Applications typically embed the token into the URL of the reset link. For example: https://www.yoursite.com/password_reset?secret=2d6d4fa7ef9607ea11755960114eac8f. However, this is problematic, because URLs are often logged by the server (including the parameters), and users may not understand that the URL is secret and mustn't be shared. A more secure approach is to make an actual text field for the token and ask the user to copy the token from the e-mail. But of course this is not very convenient and can confuse inexperienced users, so you might want to choose the first approach nonetheless. Yes, only the latest token should be considered valid. And, yes, do inform the user about invalid tokens (e. g. truncated URL parameters), nonexistent tokens and expired tokens. Those are no secrets.
  12. Navees_, how about you actually read our replies instead of posting the same question into 10 other forums? I already told you that log-in checks are difficult to implement, and I pointed you to some sample code. But for some reason you've decided to ignore this and instead make all the mistakes I warned you of. That's not very useful. How do you know that? Judging from the code quality, I hope this is just for some personal home page.
  13. You should specifiy the hash algorithm and cost (like you already did). Do not rely on the default settings. The whole point of modern password hashing is that you tune the settings for your specific hardware and requirements. You seem to have done that already, and that's great. However, you should store the parameters in a configuration file and automatically update the hashes when the settings change. This way you can easily keep the parameters up-to-date: <?php // put the hash parameters into a configuration file so that you can easily change them define('APP_PASSWORD_HASH_ALGORITHM', PASSWORD_BCRYPT); define('APP_PASSWORD_HASH_COST', 9); // hashing a password (e. g. during registration) $password_hash = password_hash($password, APP_PASSWORD_HASH_ALGORITHM, array('cost' => APP_PASSWORD_HASH_COST)); // verifying a password; if the parameters are out-of-date, the hash is automatically updated if (password_verify($password, $stored_hash)) { # do the log-in procedure // need a hash update? if (password_needs_rehash($stored_hash, APP_PASSWORD_HASH_ALGORITHM, array('cost' => APP_PASSWORD_HASH_COST))) { $new_password_hash = password_hash($password, APP_PASSWORD_HASH_ALGORITHM, array('cost' => APP_PASSWORD_HASH_COST)); # do the UPDATE query } } else { # password incorrect } Note that bcrypt hashes are always 60 ASCII characters long, so a CHAR(60) is sufficient. Also note that bcrypt can only process up to 56 bytes of input, so you need to validate the user-provided password and warn them if it's too long: <?php // put this into a configuration file define('APP_MIN_PASSWORD_LENGTH', 6); define('APP_MAX_PASSWORD_LENGTH', 56); // bcrypt can only process up to 56 bytes of input if (mb_strlen($password, '8bit') < APP_MIN_PASSWORD_LENGTH) { # too short } elseif (mb_strlen($password, '8bit') > APP_MAX_PASSWORD_LENGTH) { # too long } else { # length OK } Without this check, the password may be silently truncated (if it exceeds 72 bytes), and that obviously should never happen.
  14. There's an easy way to find out: Try it. Yes, you can have multiple catch blocks, and the first match is executed. Yes, you can use the variables anywhere in the function or method, because try statements don't create a new scope. No, you still shouldn't catch the generic Exception. In this case, it doesn't even make sense, because there might be a fatal error which is not an exception. If you need a cleanup procedure, use something like register_shutdown_function() which will be called when the script ends (regardless of the reason) and allows you to check if there was a fatal error of any kind. However, there are usually more elegant solutions. For example, wrap the sequence of INSERT queries in a transaction. If one of them fails, the entire transaction is rolled back automatically, and there won't be any garbage data left. Should you actually encounter a situation where you need to catch all exceptions (I can't think of any), then rethrow the exception when you're done: <?php try { } catch (Exception $error) // for some special reason, we need to catch every exception here { // do the error handling // rethrow the exception throw $error; }
  15. The Exception class covers every possible exception, it could be anything from SecurityBreachException to CPUHasCaughtFireException. If you simply catch any exception (aka Pokémon Exception Handling) and keep the application running, this can have serious consequences. Exceptions exist for a reason, so you shouldn't just stop them at will. If you want to handle a specific problem, then only catch this specific subclass of Exception.
  16. Yes, exceptions travel through the call stack. If you don't catch an exception in the function/method, it's passed on to the caller. If you don't catch it there, it's again passed to the caller etc. On a side note: Never catch the top-level Exception class, only specific subclasses.
  17. PHP doesn't have a \u escape sequence. There's no such thing. What is does have is a \x{...} sequence for multibyte characters (as I already explained above).
  18. This is a regular expression. It defines a pattern which the input must match: \A is the beginning of the string. [...] is a character from a certain character set; for example, [a-z] is a lowercase character from the latin alphabet (you may read it as “a to z”). \x{...} is the UTF-8 representation of a certain Unicode code point; for example, \x{00C0} is the UTF-8 sequence of the “latin capital letter A with grave”. + means that the pattern should be repeated once or more. \z is the end of the string. u turns the regular expression into “UTF-8 mode”. So the pattern simply says: A sequence of at least one UTF-8 encoded character from the Unicode ranges U+00C0 to U+01FF and U+0600 to U+06FF.
  19. Again, you can do this in PHP, and I've offered three possible approaches. Now it's up to you to pick one. If the encoding will always be UTF-8, then this is actually very easy. There's even a special escape sequence which converts code points to UTF-8 sequences, so you don't have to do that yourself: <?php // only accept characters from the Arabic block and some characters from the Latin blocks $character_validation_pattern = '/\\A[\\x{00C0}-\\x{01FF}\\x{0600}-\\x{06FF}]+\\z/u'; // should match var_dump( preg_match($character_validation_pattern, 'ĕث') ); // should not match var_dump( preg_match($character_validation_pattern, 'ab') );
  20. Um, that's exactly what I'm trying to help you with. What do you think the whole discussion was about? If you want to validate the data with PHP (which you appearently do), then you have to use the features of PHP. It's great that your desktop coders have some Unicode range check they can use. We don't. PHP can do byte ranges, sure, but then you have to use the UTF-8 representations, not the code points. So that's the solution you've chosen? By the way, what's the whole point of the last two ranges when they're already covered by the second one?
  21. When you do a single query with no external dependencies, you don't need a lock. For example, you can easily insert dozens of new rows at the same time. MySQL will take care of this. So, no, you don't need low-level write locks like you needed them for plaintext files. But be aware that conflicts may also occur at a higher level. Often times the data you want to insert or use for an update actually depends on a previous query (even if it's the same table). For example, you may first load the data into PHP, process it there and then do an update. In this case, you do have to worry about conflicts, because MySQL doesn't see the full picture. It just sees two separate queries, not the complex read-process-update procedure behind them. A few common pitfalls you need to watch out for: Uniqueness checks within PHP rarely work out (see my user registration example). Counters within PHP are problematic (e. g. log-in limits). If users can edit larger pieces of data (like a blog post), you'll quickly run into conflicts, even if it's just one user! Imagine the following scenario: The user opens the data in multiple browser tabs and edits it. If the user saves one tab and then the other, the first update will be lost entirely, because the second update is based on the previous state of the data. Storing session data in the database is problematic, especially when you access it with Ajax. Even professional PHP frameworks struggle with this, and the homegrown session handlers you'll find on the Internet are almost always broken.
  22. There is no race condition if you do the version check within the UPDATE query. You can and should do that. The canonical implementation of optimistic locking is this: When the page is loaded, SELECT version FROM entries WHERE id = <some_id>, save in $current_version When the content should be saved, UPDATE entries SET data = <new_data>, version = version + 1 WHERE id = <some_id> AND version = <current_version> If the UPDATE hasn't affected any rows, then the data was changed in between (either the version was incremented, or the row was deleted)
  23. Which of the solutions?
  24. @requinix: This makes no sense (besides the fact that it's pure speculation). It seems you want to implement optimistic locking, but then you use SELECT ... FOR UPDATE, which is pessimistic locking. You can't have both. The timestamp is also a rather poor version identifier. Who says there will never be two queries within one second? @enveetee: As kicken already said, this depends entirely on the concrete scenario. There is no one-size-fits-all solution, just different strategies for different requirements. You do not have to worry about low-level data corruption. An INSERT or UPDATE query itself is atomic, so you won't end up with something like “half-of-the-data” or “two-datasets-mixed-into-each-other”. However, you do need to worry about multiple queries which depend on each other. For example, a typical mistake people make is this: They want to enforce unique usernames, so they first check if the name already exists, and if it doesn't, they insert a new row. But what if a simultaneous process has inserted the same name after the check? Then the name is used twice. Solving this doesn't require locking, though. You add a UNIQUE constraint to the name column, try to insert the name, and if this fails due to a constraint violation, you know that the name is already taken. Can you give us a concrete example of what you're trying to do and why you think this might require locking?
  25. I still find it odd to have such severe limitation in today's times. It's some legacy application, I guess? Anyway, this is not quite that easy. The problem is that there's a difference between the abstract Unicode codepoint (which is unique) and the actual byte representation (which depends on the character encoding). Your hex strings above are not how those characters are represented in UTF-8, so if you literally searched the input for the byte patterns, you wouldn't find anything. However, there are several solutions which do work: If those ranges have a specific meaning (I'm not familiar with Arabic), you may be able to express them with a Unicode character class within a regular expression. This is by far the most elegant approach, because you don't need to hard-code any byte sequences. You could look up the concrete UTF-8 representation of each character and use byte ranges within a regular expression: [\xUUUUUU-\xVVVVVV\xYYYYYY-\xZZZZZZ] You might (ab)use json_decode() to enter the Unicode code points directly and get back the UTF-8 representation. JSON supports Unicode escape sequences like \u00FF.
×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.