-
Posts
4,207 -
Joined
-
Last visited
-
Days Won
209
Community Answers
-
Jacques1's post in PHP and Token Authentication was marked as the answer
Both thephpleague/oauth2-server and bshaffer/oauth2-server-php are very active, have been around for a while and are in fact mentioned on the “official” site (if that helps).
But even if the project dies in a couple of years – so what? Switching to a different library is still much, much less work than writing your own from scratch and maintaining it (just look at the number of contributors and commits). You could even implement an abstraction layer (e. g. a couple of wrapper functions), so that the application doesn't rely on any library-specific features.
-
Jacques1's post in images upload safely !? was marked as the answer
At least the view part definitely doesn't work. You set the Content-Type header to the image type, but then you try to output HTML. You also try to use readfile() to insert the image content into an img element, but readfile() doesn't return the image, and even if it did, you can't just insert bytes into an image source (you'd have to use data URLs with an encoded payload, but this has other problems like efficiency).
Without actual working code, we cannot really assess the risks. However, there are many conceptual security problems:
The content type declaration, one of the most critical aspects, is accepted directly from the user input ($_FILES[...]['type']) without any validation at all. This means the user can, for example, upload a file with malicious JavaScript code and actually declare it as text/html or text/svg. If you deliver the file with this type, you trigger code execution. Even when you add validation to the upload script, there's still no validation in the view script. So if a user chooses a harmless type during upload and then manages to manipulate it later (e. g. through an SQL injection), they can again make you trigger code execution. You have a uniqueness check during insertion. If you only allow unique filenames, it will be easy to perform a denial-of-service attack by uploading files with common names. The random filenames generated by tempnam() are fairly weak and may again be abused for a denial-of-service attack. You also have no proper error checking. PHP errors during upload are completely ignored, and even when you do detect an error (e. g. a wrong type), you just set an error variable and let the script keep running. Deleting the original image isn't a good idea either, because resizing is a destructive operation. You cannot go back if you realize that something went wrong or you simply want a better quality.
I would start from scratch and use a much stricter and more robust approach:
Create a hard-coded whitelist of allowed extensions and MIME types. This will be used in multiple scripts, so put it into a central place (e. g. the configuration script). When you receive an upload, you first check for errors. If the upload has failed, you abort processing. Then you check the file extension and/or the MIME type. If this fails, you again abort processing. You must not continue, because this may be an attack. You use a strong random number generator (like the one I gave you) to generate the filename and store the original(!) file outside of the document root. Then you can additionally store resized images. The easiest and most efficient way of delivering the images is through the “sendfile” mechanism (e. g. mod_xsendfile for Apache). This delegates the task to the webserver rather than PHP.
In any case, make sure that all files are served with a safe MIME type.
-
Jacques1's post in Using float values in JSON was marked as the answer
The client essentially subverts the type flexibility of InfluxDB by appending an explicit “i” whenever it encounters a PHP integer.
If there's no better client, cast the arguments of the Point constructor:
$point = new Point($measurement, (float) $value); Or change the source code.
-
Jacques1's post in I have an array that I need to get into a db was marked as the answer
You don't need no regular expressions. Just look at the output of the command in the console and then split the string at the right characters. Surely you can recognize a newline and a tab.
In my case, that's
foreach (explode("\n", $arpOutput) as $host) { list($ip, $mac) = explode("\t", $host); echo "IP address: $ip, MAC address: $mac<br>"; } -
Jacques1's post in Regarding .htaccess was marked as the answer
To make it short: Virtual paths like /items/p/ and relative physical paths like search.php don't go well together. The browser will end up trying to access nonsense URLs like /items/p/search.php, because it assumes it's actually within the directory /items/p/.
Use absolute paths instead. For example, if your PHP script is directly below the document root, that's
/search.php In the long run, you should also consider switching to virtual (“pretty”) URLs altogether. Then you can completely decouple the physical script paths from the URLs.
-
Jacques1's post in HELP needed on this simple code no output no error was marked as the answer
Several things:
Don't show your internal database errors to your visitors. It's very irritating for legitimate users, and it helps attackers gain information about your system. Instead, enable exceptions, so that error messages are automatically sent to the right device according to your PHP configuration (an error log in production, the developer's screen during development). Your database structure is broken. Whenever you find yourself numbering table names or columns, there's something wrong. SQL is not Excel. It's a database system with specific rules how to store data. Don't use SELECT *. Always select specific columns. Don't copy every item of the $row array into an extra variable. Just access the array items whenever you need them: $row['index_of_the_item']. You'll want a more meaningful name than $row, though. Stylistic HTML attributes like align, width, color etc. are dead since 1997. We have CSS now. If this is the entire script, you're also failing to output a complete and valid HTML document. Create a function for your database connection so that you can reuse this code in all your scripts:
database.php
<?php /** * Establishes a MySQL database connection using mysqli * * @param $host string the hostname or IP address of the database server (e. g. "localhost") * @param $user string the name of the database user * @param $password string the password of the database user * @param $database string the database name * @param $charset string the character encoding of the connection (e. g. "utf8) * * @return mysqli the connection * * @throws mysqli_exception if the connection failed */ function connect_to_database($host, $user, $password, $database, $charset) { // make mysqli throw an exception whenever it encounters a problem $mysqli_driver = new mysqli_driver(); $mysqli_driver->report_mode = MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT; $database_connection = mysqli_connect($host, $user, $password, $database); // specify the character encoding of the database connection $database_connection->set_charset($charset); return $database_connection; } Then create a configuration script to hold the connection parameters:
configuration.php
<?php const DATABASE_HOST = 'localhost'; const DATABASE_USER = '...'; const DATABASE_PASSWORD = '...'; const DATABASE_NAME = '...'; const DATABASE_ENCODING = 'utf8'; Fix your database structure. For example:
TABLES quizzes - quiz_id questions - question_id - question answers - question_id - answer - is_correct quiz_questions - quiz_id - question_id -
Jacques1's post in is it efficient ? was marked as the answer
No need to apologize. I'm simply pointing out that efficiency isn't the issue here.
Choose the most readable solution. If it makes sense to only fetch the title, that's perfectly valid. It doesn't matter if you need a few extra queries, because there won't be any performance difference at all.
-
Jacques1's post in Array missing some values was marked as the answer
I've already told you what to do: Put the array initialization before the loops, so that they only happen once and aren't repeated in every iteration. You want to keep the same array throughout all iterations.
-
Jacques1's post in IE8 and js lib issue, Object expected error was marked as the answer
The jQuery 2 and 3 branches don't support very old browsers like IE8. You have to either give up IE8 support yourself or stick with jQuery 1.
-
Jacques1's post in newbie question regarding fopen was marked as the answer
Your goals and ideas don't make any sense from a technical standpoint.
If you want to be sure that the eBay stock is updated during the purchase, there will be a delay for the user, even a potentially infinite delay. Network operations can be slow. Network operations can fail (yes, this happens). The only way would be to wait for the response indefinitely, and if there's any error, try again indefinitely. This is obviously unrealistic. And you may still end up selling out-of-stock items: What if somebody purchases the last item on eBay right after your user has clicked on the “order” button and before you make your API calls?
Note that this is also a legal question, so if you're a newbie, you definitely shouldn't invent your own processes. Either find a widely accepted standard solution. Or discuss this with an e-commerce expert. The shops I worked on had a non-binding order confirmation on the website and a separate binding confirmation sent by e-mail. But I'm not a lawyer, so I cannot tell you if this works in every country and in every context.
-
Jacques1's post in Arithmetic and NULL was marked as the answer
Just don't. Don't rely on languages to guess what you mean when you juggle with nonsense values, and don't try to produce results from nonsense input.
I know this is hard to believe for a PHP programmer, but many languages actually trigger an error when the program does weird things like trying to calculate sums of void values. You should do that too, because it's the most straightforward reaction. If you absolutely must produce a result, then return immediately after validation. No magic.
-
Jacques1's post in Use of Switch was marked as the answer
$Clue_No isn't the same as $Clue_no. PHP variables are case-sensitive.
If you get a proper IDE, it will actually point out trivial mistakes like this and save you a lot of time.
Also note that your default case will trigger an error when you try to access $result, because there won't be such variable. You should fix your logic. Personally, I would avoid magic numbers and switch statements at all costs, because it quickly turns the code into an unreadable mess.
-
Jacques1's post in Lengthy Processing & Informing Users was marked as the answer
The response doesn't have to be one fixed block of data like in classical web applications. It can also be a stream, which allows the server to send updates to the client without having to wait for the next request.
Compared to polling, this is simpler, quicker (there are no unnecessary delays) and more efficient in terms of traffic.
Client-side JavaScript code
var evtSource = new EventSource("process.php"); evtSource.addEventListener("progress", function (event) { var eventData = JSON.parse(event.data); console.log("Processing: " + eventData.percentage + "%"); }); Server-side PHP code
<?php header("Content-Type: text/event-stream"); function send_event($event, $data) { if (!preg_match('/\\A\\w+\\z/', $event)) { throw new InvalidArgumentException('Invalid event name: '.$event); } echo "event: $event\n", "data:".json_encode($data)."\n\n"; flush(); } for ($i = 0; $i <= 100; $i++) { send_event('progress', ['percentage' => $i]); sleep(1); } -
Jacques1's post in Variables are not showing in email was marked as the answer
Let's look at this line:
data: {"amount": $('#x_my_amount').val()}, This is what gets sent to the server. I'm sure you can figure out the rest.
-
Jacques1's post in Mysql dies with invalid user name or pass was marked as the answer
Erase the code from your disk, forget what you think you know about PHP, start from scratch
The Codeacademy PHP course (learning the basics of PHP) Exploits of a Mom (why security matters) (The only proper) PDO tutorial (how to communicate with a MySQL database in the 21st century) The PHP manual (lots of useful information and warnings) -
Jacques1's post in Alternative to e pattern modifier in PHP5.5 was marked as the answer
Just remove the modifer.
The replacement operation itself looks strange, but that's another story.
-
Jacques1's post in How to make php.ini changes stick? was marked as the answer
Yes, that's PHP-FPM.
Do you have an init script for PHP-FPM? Then you simply need to use that instead of the nginx one after you've updated the PHP configuration:
/etc/init.d/php-fpm restart (the script could be named differently)
There are also plenty of init scripts on the Internet.
-
Jacques1's post in Username/Password check - does it matter which way? was marked as the answer
How is the second query even possible? Are you storing your passwords as plaintext?
-
Jacques1's post in email decryption was marked as the answer
A SHA-2 hash is not a SHA-2 HMAC. As I already said above, you simply create an extra column for the HMAC of the e-mail address and make it UNIQUE. Whenever you want to add a new address, you calculate the HMAC with PHP and try to insert the address together with the HMAC. If the address already exists, the HMAC will be identitical to an existing value and violate the UNIQUE constraint. Otherwise the query will succeed, and you know that the address is indeed unique.
It's really very simple once you understand the idea.
<?php class HMAC { /** * @var the underlying algorithm for the HMAC */ const ALGO = 'sha256'; /** * @var the length of the HMAC key in bytes */ const KEY_LENGTH = 32; /** * @var string the HMAC key */ protected $key; /** * HMAC constructor. * * @param string $key the HMAC key */ public function __construct($key) { if (!is_string($key)) { throw new InvalidArgumentException('Invalid key type. Expected a string.'); } if (strlen($key) != static::KEY_LENGTH) { throw new InvalidArgumentException('Invalid key length. Expected '.static::KEY_LENGTH.' bytes.'); } $this->key = $key; } /** * Calculates the HMAC of a message * * @param string $message the message * * @return string the resulting HMAC */ public function calculate($message) { if (!is_string($message)) { throw new InvalidArgumentException('Invalid message type. Expected a string.'); } return hash_hmac(static::ALGO, $message, $this->key); } /** * Verifies the integrity of a message with a known HMAC. * * @param string $message the message to be verified * @param string $hmac the known HMAC * * @return bool whether the HMAC of the message matches the known HMAC */ public function verify($message, $hmac) { if (!is_string($message)) { throw new InvalidArgumentException('Invalid message type. Expected a string.'); } if (!is_string($hmac)) { throw new InvalidArgumentException('Invalid HMAC type. Expected a string.'); } return hash_equals($this->calculate($message), $hmac); } } <?php $key = hex2bin('8311f033e7235286f5d9b17f2e83366989c3f12b414dc15348dbed2aaa102b2d'); // note binary key $hMAC = new HMAC($key); $email = '[email protected]'; $emailHMAC = $hMAC->calculate($email); // insert $email with $emailHMAC -
Jacques1's post in why my explode and str_split code is not working? was marked as the answer
The code you've copied and pasted checks for PHP 4.3.0 which was released 14(!) years ago and doesn't even work for mysqli which was released 12 years ago. So wherever you got this from, it's literally ancient.
I understand that you're still learning, and that's exactly what this forum is for. What I'm trying to get across is that programming is more than “somehow making things work”. Sure, you can assemble your programs from 14-year-old code snippets you found somewhere on the interwebs. And you might even get a result in the sense that you see rendered HTML on the screen. But that's not really programming.
Modern PHP is actually a decent language which allows you to write secure and compact code, but so many people learning PHP (not just you) seem to be stuck in early 2000. That's why I'm trying to promote the PHP of 2016.
-
Jacques1's post in preventing form confirmation email on page reload was marked as the answer
And the $order_complete variable comes from where? You realize that variables only exist while the script is running, right? When the page is reloaded, the variables from the previous run are long dead.
The overall approach seems very odd, so it might be a good idea to start from scratch:
Braintree appearently uses POST requests to confirm orders. So you need a script which reacts specifically to POST requests (see $_SERVER['REQUEST_METHOD']), not plain GET requests. When you receive a (positive) confirmation, you send an e-mail. But don't use the strange approach of including some other script which then sends the e-mail. This is extremely confusing, and I bet that requesting the script directly in the browser causes trouble. If you don't want to have the e-mail code directly in the main script (which makes sense), define a function or method and then call it in the main script. After the confirmation has been processed, you redirect the client to a status page which merely displays the success/failure message. Since the status page doesn't do anything, the client can reload it as often as they want. See the PRG Pattern for a more detailed explanation. -
Jacques1's post in Troubles with header for authentication, CORS, and Options was marked as the answer
When you set custom HTTP headers, the client has to preflight to determine whether the actual request will be valid. This is the OPTIONS request you're seeing in the log. When you respond with a 401 code, the client assumes that the test has failed and will not send the actual request.
To fix the problem, exclude OPTIONS requests from the header check so that the client will get a 200 response.
-
Jacques1's post in CSV hinderance was marked as the answer
CSP rules are defined with an actual HTTP header, not a meta element. You either make your webserver add the header, or you use the header() function in PHP.
-
Jacques1's post in libsodium encrypt was marked as the answer
Don't use the e-mail address as an identifier. Use an additional public username.
Misusing e-mail addresses as usernames is generally a bad approach, because it's almost impossible to hide them even from the public interface. For example, if I want to check if [email protected] is registered, I can simply measure the time (as the slow bcrypt check only takes place when the address exists).
-
Jacques1's post in PHP OUTFILE with datestamp was marked as the answer
$database_connection->exec("
SELECT
'FIELD_1'
-- ...
UNION ALL
SELECT
*
FROM
mydatabasetable
INTO
OUTFILE 'E:/Data/exports/DailyExport/myoutputfile".date('Ymd').".csv'
FIELDS
TERMINATED BY ',' ENCLOSED BY ''''
LINES
TERMINATED BY '\r\n'
");