Jump to content

Jacques1

Members
  • Posts

    4,207
  • Joined

  • Last visited

  • Days Won

    209

Everything posted by Jacques1

  1. You'll have to be a bit more specific. What kind of puncuation marks? Period, comma, semicolon? And where exactly are they allowed? Are you OK with “.......”?
  2. First of all: Why do you have those weird limitations? Is that a requirement or a decision on your part? I understand that you cannot or don't want to install a full-blown database system like MySQL, but there's still SQLite. That's also a single file, but it's much more convenient and reliable than messing with friggin' txt files. And why on earth can't you use sessions? That's one of the core functionalities of PHP. Secondly: What exactly is this for? Is this just a fun application where any user may take over any account, or does the code actually have to be secure? If you need security, things will get complicated, because you obviously need to prevent users from manipulating the cookies. This is possible with cryptography (or more specifically: a message authentication code), but that will take a lot more work and knowledge than simply using PHP sessions.
  3. It's perfectly fine to omit the action attribute. In fact, your suggestion is against the specification, because the attribute may only contain non-empty URLs.
  4. Your code generally doesn't make a lot of sense. What is the query SELECT password FROM users WHERE password = supposed to do? You take the submitted plaintext password and then try to find the exact same string in your database? Aren't your database passwords hashed? I guess what you actually want is get the password hash(!) for the provided username: SELECT password FROM users WHERE username = :username It might be a good idea to rename the column "password" to "password_hash" to avoid this confusion in the future. You have a lot of other weird parts in your code, so I strongly recommend you go through this line by line and carefully test each part with var_dump(). Don't just write down one big block of code and test it afterwards, because this makes debugging much harder.
  5. $sql is a string, so it doesn't have a bind_param() method. If you want a prepared statement, you need to create one with PDO::prepare().
  6. So what does your error log say? If you don't have an error log, enable logging now and do it over.
  7. Negative indices are simply a shorthand notation which is useful when you need to access characters from right to left. Yes, the last of 6 characters is at position 5, but it can also be found at index −1. The next-to-last character is at position 4 and −2 etc.
  8. Just because it's popular doesn't mean it's right. Sure, Stack Overflow is one of the better resources, because the users are relatively knowledgeable, and the voting system tends to favor good replies. But there's still a lot of garbage code and bad advice, so you never know what you get. In my experience, it makes more sense to learn from a few experts than to hope for the wisdom of the crowd. A very good site for security-related topics is the Survive The Deep End online book from Pádraic Brady. Of course you can and should still look for other opinions, but this is a much more solid starting point than some idea somebody posted on Stack Overflow.
  9. You also need to get rid of this weird self-made random number generator. It's both overcomplicated and insecure. Use an actual pseudo-random number generator like openssl_random_pseudo_bytes() or mcrypt_create_iv(): // using the OpenSSL extension $token = bin2hex(openssl_random_pseudo_bytes(16)); echo $token; // using the Mcrypt extension $token = bin2hex(mcrypt_create_iv(16, MCRYPT_DEV_URANDOM)); echo $token;
  10. No, you shouldn't do any SELECT query to check the uniqueness of a value. This is naïve, unnecessary and simply doesn't work when multiple PHP processes run at the same time (which is very common in a web application). If two processes simultaneously check a name which hasn't been taken yet, they both come to the conclusion that they can use it, so they'll both run the INSERT query with the same name. This will either lead to a duplicate name or a database error, depending on whether you have a UNIQUE constraint in your database. In any case, this is a problem. Only the database system can and should enforce unique values. Make sure the name, e-mail etc. columns are either a primary key or have a UNIQUE constraint, and then simply try to insert the new user with no prior checks. If that fails due to a constraint violation, you know the name is already taken. This approach will not only save you a lot of code. It's also reliable, because everything happens in a single atomic operation which cannot interfere with other processes. As an example: The database schema CREATE TABLE users ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, public_name VARCHAR(100) NOT NULL UNIQUE, password_hash CHAR(60) NOT NULL CHECK (LENGTH(password_hash) = 60) ); The database connection <?php const DB_HOST = 'localhost'; const DB_USER = 'root'; const DB_PASSWORD = ''; const DB_NAME = 'test'; const DB_CHARSET = 'UTF8'; // MySQL error codes const MYSQL_ER_DUP_ENTRY = '23000'; /* * Set up the database connection * - the character encoding *must* be defined in the DSN string, not with a SET NAMES query * - be sure to turn off "emulated prepared statements" to prevent SQL injection attacks * - turn on exceptions so that you don't have to manually check for errors */ $dSN = 'mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset='.DB_CHARSET; $databaseConnection = new PDO($dSN, DB_USER, DB_PASSWORD, [ PDO::ATTR_EMULATE_PREPARES => false, // activate real prepared statements PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // activate exceptions PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, // fetch associative arrays by default (this isn't critical) ]); The registration logic <?php // test data $username = 'foo'; $passwordHash = '$2y$12$Yo1zXvPWXLuxMku2rOnrpeEvCJssXQ44fHR2rj.uTBps3SA3sjX8q'; $registrationStmt = $databaseConnection->prepare(' INSERT INTO users (public_name, password_hash) VALUES (:public_name, :password_hash) '); /* * Try to insert the new user. * * If the name is already taken, this will violate the UNIQUE constraint of the public_username column and trigger * an exception. We can catch this exception and tell the user that they need to choose a different name. */ $nameIsAlreadyTaken = false; try { $registrationStmt->execute([ 'public_name' => $username, 'password_hash' => $passwordHash, ]); } catch (PDOException $registrationException) { // Was the exception caused by a constraint violation? Then catch it; otherwise propagate it. if ($registrationException->getCode() == MYSQL_ER_DUP_ENTRY) { $nameIsAlreadyTaken = true; } else { throw $registrationException; } } if ($nameIsAlreadyTaken) { echo 'Sorry, this name is already in use. Please choose a different name'; } If you have multiple unique columns, things get a bit more complicated. You have to use the above try statement, and within the error handling procedure, you need to perform a SELECT query to check which one the values already exists in the database. Note that you should never tell arbitrary users that a particular e-mail address already exists in your database, because this is private information. Instead, send an e-mail to that address saying something like “You or somebody else has tried to register at our website with this e-mail address, but the address already exists in our database. If you forgot your password ... etc.”.
  11. Don't use spaces within field names, and don't mix lowercase and uppercase letters. While this is theoretically possible, you'll run into a lot of trouble. PHP seems to convert those spaces into underscores. The field name isn't supposed to be pretty. It's should be unambiguous, so make it all-lowercase and use underscores to avoid any confusion: first_name
  12. You still haven't told us your problem, but the two “else” after the “if” must be “elseif”, and the select element must be within the form, and you need to use $value as the option value, not $key (the key is the numeric array key: 1, 2, 3, 4).
  13. We won't do your homework for you. I you want help, show us your current code and tell us what exactly your problem is. And please choose proper thread titles. Nonsense like “I need help” doesn't tell us anything, because everybody who creates a thread obviously needs help.
  14. Your execute() call still contains the password. Remove it.
  15. Note that you're using password_hash() incorrectly or at least not optimally. The point of modern hash algorithms like bcrypt is that you adjust their strength to your specific security requirements and hardware. bcrypt has a single cost parameter for that: const PASSWORD_HASH_COST = 14; // put this into a configuration file and adjust it $example_password = 'Xs761Ic5sAwE9ASSCdqdYB'; $hash = password_hash($example_password, PASSWORD_BCRYPT, ['cost' => PASSWORD_HASH_COST]); The higher the cost parameter, the harder it is for an attacker to perform a brute-force attack against the password. But of course a high cost factor also slows down the log-in procedure and stresses your CPU. Finding a good balance between security and usability is very important when you use bcrypt. A common recommendation is that you decide how long the hashing may take. For example, 1 second for a standard account and 3 seconds for an admin-like account should be acceptable. Then you hash a test password with different cost parameters until you've reached the chosen time. That's the right value. Of course you'll have to update the parameter when you move the application to a different server, so it's a good idea to put it into a configuration file. Secondly, bcrypt has two nasty pitfalls: Using passwords longer than 56 bytes violates the specification, and passwords longer than 72 bytes are silently truncated. This can become a real problem if you're using a multibyte character encoding like UTF-8. Any nullbyte within the password will cut off the password at that point. There are two solutions. Either you validate the password and reject it if it has any of the above problems. Or you run the password through a hash algorithm like SHA-256 prior to using bcrypt: <?php const PASSWORD_HASH_COST = 14; // test data $password = 'Xs761Ic5sAwE9ASSCdqdYB'; // hash the password with SHA-256 and encode the raw output with Base64 $raw_intermediate_hash = hash('sha256', $password, true); $encoded_intermediate_hash = base64_encode($raw_intermediate_hash); // now hash the encoded SHA-256 hash with bcrypt $final_hash = password_hash($encoded_intermediate_hash, PASSWORD_BCRYPT, ['cost' => PASSWORD_HASH_COST]); var_dump($final_hash); This automatically solves both problems: The password is “compressed” to a fixed length of 256 bits, and then those bits are encoded with Base64, yielding exactly 44 Base64 characters. So the bcrypt input cannot become too long, and it cannot cointain nullbytes, regardless of the original password.
  16. You cannot check the password using SQL. You need to fetch the password hash from the database and then compare it in PHP using password_verify(). <?php $user_stmt = $handler->prepare(' SELECT password, rank, active FROM users WHERE username = :username '); $user_stmt->execute([ 'username' => $username, ]); $user_data = $user_stmt->fetch(); if ($user_data) { if (password_verify($password, $user_data['password'])) { echo 'The password is correct'; } else { echo 'Incorrect password.'; } }
  17. I understand that you have multiple Jabber servers, but that doesn't mean you should have a separate database for each one of them. Or is this a specific requirement of the Jabber implementation you're using? Anyway, if you absolutely need multiple databases, create an array which maps the different servernames to different MySQL connection parameters: <?php $jabberDatabases = [ 'jabber1.example.com' => [ 'host' => 'localhost', 'user' => 'user1', 'password' => 'pw1', 'database' => 'db1', ], 'jabber2.example.com' => [ 'host' => 'localhost', 'user' => 'user2', 'password' => 'pw2', 'database' => 'db2', ], ]; Given the name of the jabber server, you can select the corresponding parameters for mysql_connect() and mysql_select_db().
  18. Why do you want two databases? Why not use one and separate the users within the database? Simply add a column which assigns the user to one of the two Jabber servers. Maintaining two identically(?) structured databases is generally a very bad idea, because it makes the code more complicated, forces you to do everything twice (changes, backups) and can quickly lead to problems if you update one database but forget to update the other.
  19. It's still unclear to me what you mean by choosing the MySQL server. Do you have multiple database servers? Or are you talking about databases? In any case: Why do you have more than one? By the way, the code has lots of problems. The mysql_* functions are obsolete since more than a decade and will be removed in PHP 7. Nowadays, we use PDO. Randomly throwing all this stripslashes(), htmlspecialchars() etc. at the user input also isn't a good idea.
  20. Try to phrase a coherent problem description and show the relevant code. Then we might be able to help you.
  21. “Didn't work” is a bit vague. <?php $_SERVER['HTTP_REFERER'] = 'https://www.mynewlink.com/folder/abc123'; $url_path = parse_url($_SERVER['HTTP_REFERER'], PHP_URL_PATH); $url_id = basename($url_path); var_dump($url_id);
  22. It's still not really clear what you're trying to achieve. Web crawlers announce themselves via the user agent and can also be easily identified with reverse DNS lookups, so there's no need for an IP blacklist. In fact, Google specifically recommends against that, because their IP addresses may change at any time. Or are you talking about malicious bots which post spam? That's an entirely different story and may be prevented with CAPTCHAs powerful content filters (e. g. Bayesian or Markov filters known from e-mail) as a last resort: blacklists And then of course there are simple bots written by amateurs which don't cause any harm and should really be left alone. So before you start to randomly implement all kinds of features, I strongly recommend you get clear about your goal. Trying to recognize bots is hardly a sensible objective, because there's such a big range of entirely different bots for entirely different purposes.
  23. MySQLi doesn't use emulation at all, PDO must be configured with PDO::ATTR_EMULATE_PREPARES set to false: $databaseConnection = new PDO($dSN, DB_USER, DB_PASSWORD, [ PDO::ATTR_EMULATE_PREPARES => false, // disable emulation so that actual prepared statements are used ... ]); A quick test is to prepare an invalid query. If emulation is used, nothing will happen, because the query template doesn't get processed at that point. If an actual prepared statement is used, then you'll get an error, because the query template is sent to the database system where it cannot be processed: <?php const DB_HOST = 'localhost'; const DB_USER = '...'; const DB_PASSWORD = '...'; const DB_NAME = '...'; const DB_CHARSET = 'UTF8'; $dSN = 'mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset='.DB_CHARSET; $databaseConnection = new PDO($dSN, DB_USER, DB_PASSWORD, [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // activate exceptions ]); // preparing an invalid query *with* emulation: nothing happens $databaseConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, true); $databaseConnection->prepare('THIS IS NO VALID SQL'); // preparing an invalid query *without* emulation: an exception is triggered $databaseConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); $databaseConnection->prepare('THIS IS NO VALID SQL');
  24. What's your actual goal? What kind of information do you need for what purpose? There are plenty of IP databases and services with proper APIs, but which one is appropriate depends on your specific requirements. In any case, don't webscrape when there's no need to. It's fugly, fragile and possibly against the TOS.
  25. What's much more important than a particular style is consistency. If you're working alone, choose your favorite style and stick to it. If you're working in a team, make sure everybody agrees on the same set of rules. A good starting point are PSR-1 and PSR-2, because they're thought-out and have already been adopted by many projects.
×
×
  • 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.