Jump to content
thirty bees forum

Jeffrey de Bruijn

Members
  • Posts

    24
  • Joined

  • Last visited

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

Jeffrey de Bruijn's Achievements

  1. Hi, I have migrated to a new server, with the migration I took the chance to upgrade OS and PHP versions. I am now on PHP 8.1.14, I have "fixed" the installation with core updater. It seems like the PayPal module is not working any more though, with the only option I use (Website Payments Plus) am getting a forbidden iframe with the following error: Failed to load resource: the server responded with a status of 403 () Refused to set the document's base URI to 'https://www.paypalobjects.com/web/res/956/47fb6d7...' because it violates the following Content Security Policy directive: "base-uri 'self' https://*.paypal.com". Is this related to the version or was the plugin broken already and I didn't notice? Since PayPal deprecated Website Payments Standard and has a new SDK out, is there a viable alternative plugin to use?
  2. I am attempting something different, but I am stuck. In order to keep the benefit of public key authentication via SSH, I am using SSH port forwarding this way: ssh -L 9999:fqdn.server.domain:443 user@fqdn.server.domain And, by resolving the domain name to 127.0.0.1 by adding this line to the /etc/hosts file 127.0.0.1 server.domain It is possible to reach the thirtybees website through the SSH port forwarding. All it takes is to get the following url https://server.domain:9999 Unfortunately it seems thirtybees redirects the request with code 302. I am certain there is no code 302 in my virtual host configuration so I am certain it is thirtybees doing the redirect. Is there any way to disable that redirect? wget https://server.domain:9999/ --2023-04-01 21:29:03-- https://server.domain:9999/ Resolving server.domain (server.domain)... 127.0.0.1 Connecting to server.domain (server.domain)|127.0.0.1|:9999... connected. HTTP request sent, awaiting response... 302 Found Location: https://server.domain/ [following] --2023-04-01 21:29:04-- https://server.domain/ Connecting to server.domain (server.domain)|127.0.0.1|:443... failed: Connection refused.
  3. I wasn't aware the recaptcha module would add a captcha on the admin login page. However, I still do not believe recaptcha is a suitable solution. Locking the folder with a password is another thing I never considered because the password should be shared across all admin users, and the chances of it leaking are higher. It still seems better than nothing, though a rate-limit on attempts and/or 2FA is the gold-standard way to handle this problem nowadays, is there really no such implementation for tb?
  4. Hi, I am concerned about the security of the backoffice login page: I do not believe there is any sort of application-level rate limiting on the login page. Anybody could reach that page and attempt to log in, and thirty bees would not stop an attacker from attempting hundreds of logins per minute. There is no 2-factor authentication available either. This is why I would limit access to the backoffice route to a small number of trusted IP addresses, using apache's require IP directive. Recently however, I have moved to another country and the ISP here do not provide static IP addresses. The IP address I am given changes rather frequently and I am unable to use the require IP directive to protect the backoffice login page. Am I mistaken? Are there application-level mitigations for bruteforce attacks on the backoffice login page? How do I make sure attackers cannot attempt logins like that?
  5. I'm stupid and there was a missing trailing / in the italian url. <a href="/it/contenuto/termini-e-condizioni">link on italian language</a> Problem solved.
  6. Hi, I have a cms page on a multilanguage site (english and italian). In the cms page content I insert a link using html like so <a href="/en/content/terms-and-conditions">link on english (default) language</a> And this link resolves correctly to `https://example.com/en/content/terms-and-conditions` If I do the same thing on the secondary language <a href="it/contenuto/termini-e-condizioni">link on italian language</a> The address wrongly resolves to `https://example.com/en/content/current-page/it/contenuto/termini-e-condizioni` What am I doing wrong?
  7. I tried and from my limited testing it seems to be working as intended (thank you) but I really don't understand the logic: How does this work? `$product.id_product_attribute` is true as long as the product has one attribute? What does the `id_product_` refer to? And why does the "method" `.combinations` not work?
  8. Hi, I am modifying the Niara theme and in the `<span class="price product-price">` of `product-list-item.tpl` I want to add a label to products that have combinations. I have seen that in `product.tpl` there is this line: `{if isset($combinations) && $combinations}`, I have tried add this: `{if isset($product.combinations) && $product.combinations}From{/if}` but it does not seem to work, is there some similar method? Note that I am purposely avoiding the "Advanced EU Compliance" module as it seems to cause some problems I do not want to debug. I am aware that module has an option like this.
  9. HTMLpurifier strips picture tags too. The following <picture> <source type="image/avif" srcset="/themes/themename/img/test.avif" class="picture-responsive" alt="Responsive image"> <img src="/themes/themename/img/test.jpeg" class="img-responsive" alt="Responsive image"> </picture> Becomes <img src="/themes/themename/img/test.jpeg" class="img-responsive" alt="Responsive image"> I think this is a bit excessive. I read on the project's website that there should be a whitelist, how can I manage that whitelist in thirty bees?
  10. I indeed do not follow. I only follow that yes, having to clear the CDN cache manually is not as immediate. What do you mean with users having to load a minified file? Users generally do not even touch CSS nor JS and their developers do it for them. The current situation with the default theme is even more complex as users are asked to compile the entire SASS directory to CSS. If a user is expected to be able to do that I imagine that they are also able to minify their CSS.
  11. Because I do not have a CDN to try and I want to understand how it works beforehand. A CDN is not strictly to serve stylesheets and js but for other static content like images and videos too. Why would thirty bees need to force js and css caching if users want to serve images and videos via a CDN? My CSS is fine, thank you very much. Dropping it may be a bad idea, but the repository has a load of issues open that are not being addressed. CSS and JS are constantly changing and if a library that manipulates this kind of code does not keep up, it is obviously going to have issues; look at how much work it is being done on actively maintained projects such as csso and terser just for minifying. That is a good point, thanks for making me notice that. Who exactly develops themes if not developers? I'm sorry but even users who do not develop their own themes use themes somebody else develops for them. I do not understand what your point is with this statement. Wrong assumption, not doing that.
  12. That does not sound simple at all 🤣 I do not want to change and implement a new library, I am not good enough to do that and have people depend on my php skills. In my opinion, just dropping the library might be better. No need to rely on third party libraries that may get abandoned and break, plus developers have their own preferences too. For example I work in LESS and CoffeeScript and I already compile these to CSS and JavaScript, minifying is built in my tooling, I do not need another tool to minify my code. That said, I am still confused: does enabling media servers without CCC work? If so, what is handed to the CDN?
  13. Hello, I need clarifications regarding the media servers functionality and CCC Media servers are said to be used only with CCC enabled but it is very confusing to me: which of the many toggles of CCC are required for using media servers? Smart cache for CSS and JS is handled by mrclay/minify. This script supposedly minifies and combines CSS and JS files into a single CSS and single JS, the problem is that this is a very outdated script which does not support modern JS and also breaks my CSS. I want to minify and combine CSS and JS on my own (with csso and terser, actively maintained scripts) and provide these files to my CDN (media servers). Why is this not possible?
  14. Yes this is also an acceptable solution. In my experience, people do not trust that contact forms actually send any messages, some form of confirmation is better than none. As a system administrator I think that reducing the volume of emails sent is generally a good idea. If hosting a shop on a shared hosting account, most hosts limit the emails sent on a hourly/daily basis to a very low amount, I have seen as low as 50 per hour. Large shops may run into these limitations and the above override can help them.
  15. I would like to raise a concern about the contact form. The default behavior of the contact form: customer reaches the contact form, submits a message and the form sends the customer a copy of the message at the provided email address. The problem: this behavior cannot be changed in the backoffice and it can easily be exploited. A malicious individual, human or robot, can provide the email address of another person and enter the message to send to the provided address, effectively turning the mail server to an open relay. This causes the domain of the shop to be blacklisted as spam/malicious domain and hurts email deliverability/reputation. Proposed solution: provide the shop owner the ability to disable "sending a copy of the message" to the customer so that the copy of the email is only sent to the domain of the shop owner. Hotfix solution: provide an override anyone can apply to their shop Unacceptable solution: utilizing a captcha, specifically google's recaptcha, is not a viable solution because it is not a service available in all countries and in Europe it may be required to be loaded as an opt-in non-strictly-necessary cookie (bots are not going to accept optional cookies). Edit: hotfix Here is the code of my override if anybody needs it. Save it as ContactController.php in override/controllers/front and then delete cache/class_index.php. With this override, the contact form will no longer send a copy of the email to the email provided by the visitor. <?php class ContactController extends ContactControllerCore { public function postProcess() { if (Tools::isSubmit('submitMessage')) { $extension = ['.txt', '.rtf', '.doc', '.docx', '.pdf', '.zip', '.png', '.jpeg', '.gif', '.jpg']; $fileAttachment = Tools::fileAttachment('fileUpload'); $message = Tools::getValue('message'); // Html entities is not usefull, iscleanHtml check there is no bad html tags. if (!($from = Tools::convertEmailToIdn(trim(Tools::getValue('from')))) || !Validate::isEmail($from)) { $this->errors[] = Tools::displayError('Invalid email address.'); } elseif (!$message) { $this->errors[] = Tools::displayError('The message cannot be blank.'); } elseif (!Validate::isCleanHtml($message)) { $this->errors[] = Tools::displayError('Invalid message'); } elseif (!($idContact = (int) Tools::getValue('id_contact')) || !(Validate::isLoadedObject($contact = new Contact($idContact, $this->context->language->id)))) { $this->errors[] = Tools::displayError('Please select a subject from the list provided. '); } elseif (!empty($fileAttachment['name']) && $fileAttachment['error'] != 0) { $this->errors[] = Tools::displayError('An error occurred during the file-upload process.'); } elseif (!empty($fileAttachment['name']) && !in_array(mb_strtolower(substr($fileAttachment['name'], -4)), $extension) && !in_array(mb_strtolower(substr($fileAttachment['name'], -5)), $extension)) { $this->errors[] = Tools::displayError('Bad file extension'); } else { $customer = $this->context->customer; if (!$customer->id) { $customer->getByEmail($from); } $idOrder = (int) $this->getOrder(); if (!(( ($idCustomerThread = (int) Tools::getValue('id_customer_thread')) && (int) Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue( (new DbQuery()) ->select('ct.`id_customer_thread`') ->from('customer_thread', 'ct') ->where('ct.`id_customer_thread` = '.(int) $idCustomerThread) ->where('ct.`id_shop` = '.(int) $this->context->shop->id) ->where('ct.`token` = \''.pSQL(Tools::getValue('token')).'\'') ) ) || ( $idCustomerThread = CustomerThread::getIdCustomerThreadByEmailAndIdOrder($from, $idOrder) )) ) { $fields = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS( (new DbQuery()) ->select('ct.`id_customer_thread`, ct.`id_contact`, ct.`id_customer`, ct.`id_order`, ct.`id_product`, ct.`email`') ->from('customer_thread', 'ct') ->where('ct.`email` = \''.pSQL($from).'\'') ->where('ct.`id_shop` = '.(int) $this->context->shop->id) ->where('('.($customer->id ? 'id_customer = '.(int) $customer->id.' OR ' : '').' id_order = '.(int) $idOrder.')') ); $score = 0; foreach ($fields as $key => $row) { $tmp = 0; if ((int) $row['id_customer'] && $row['id_customer'] != $customer->id && $row['email'] != $from) { continue; } if ($row['id_order'] != 0 && $idOrder != $row['id_order']) { continue; } if ($row['email'] == $from) { $tmp += 4; } if ($row['id_contact'] == $idContact) { $tmp++; } if (Tools::getValue('id_product') != 0 && $row['id_product'] == Tools::getValue('id_product')) { $tmp += 2; } if ($tmp >= 5 && $tmp >= $score) { $score = $tmp; $idCustomerThread = $row['id_customer_thread']; } } } $oldMessage = Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue( (new DbQuery()) ->select('cm.`message`') ->from('customer_message', 'cm') ->leftJoin('customer_thread', 'cc', 'cm.`id_customer_thread` = cc.`id_customer_thread`') ->where('cc.`id_customer_thread` = '.(int) $idCustomerThread) ->where('cc.`id_shop` = '.(int) $this->context->shop->id) ->orderBy('cm.`date_add` DESC') ); if ($oldMessage == $message) { $this->context->smarty->assign('alreadySent', 1); $contact->email = ''; $contact->customer_service = 0; } if ($contact->customer_service) { if ((int) $idCustomerThread) { $ct = new CustomerThread($idCustomerThread); $ct->status = 'open'; $ct->id_lang = (int) $this->context->language->id; $ct->id_contact = (int) $idContact; $ct->id_order = (int) $idOrder; if ($idProduct = (int) Tools::getValue('id_product')) { $ct->id_product = $idProduct; } $ct->update(); } else { $ct = new CustomerThread(); if (isset($customer->id)) { $ct->id_customer = (int) $customer->id; } $ct->id_shop = (int) $this->context->shop->id; $ct->id_order = (int) $idOrder; if ($idProduct = (int) Tools::getValue('id_product')) { $ct->id_product = $idProduct; } $ct->id_contact = (int) $idContact; $ct->id_lang = (int) $this->context->language->id; $ct->email = $from; $ct->status = 'open'; $ct->token = Tools::passwdGen(12); $ct->add(); } if ($ct->id) { $cm = new CustomerMessage(); $cm->id_customer_thread = $ct->id; $cm->message = $message; if (isset($fileAttachment['rename']) && !empty($fileAttachment['rename']) && rename($fileAttachment['tmp_name'], _PS_UPLOAD_DIR_.basename($fileAttachment['rename']))) { $cm->file_name = $fileAttachment['rename']; @chmod(_PS_UPLOAD_DIR_.basename($fileAttachment['rename']), 0664); } $cm->ip_address = (int) ip2long(Tools::getRemoteAddr()); $length = ObjectModel::getDefinition('CustomerMessage', 'user_agent')['size']; $cm->user_agent = substr($_SERVER['HTTP_USER_AGENT'], 0, $length); if (!$cm->add()) { $this->errors[] = Tools::displayError('An error occurred while sending the message.'); } } else { $this->errors[] = Tools::displayError('An error occurred while sending the message.'); } } if (!count($this->errors)) { $varList = [ '{order_name}' => '-', '{attached_file}' => '-', '{message}' => Tools::nl2br(stripslashes($message)), '{email}' => $from, '{product_name}' => '', ]; if (isset($fileAttachment['name'])) { $varList['{attached_file}'] = $fileAttachment['name']; } $idProduct = (int) Tools::getValue('id_product'); if (isset($ct) && Validate::isLoadedObject($ct) && $ct->id_order) { $order = new Order((int) $ct->id_order); $varList['{order_name}'] = $order->getUniqReference(); $varList['{id_order}'] = (int) $order->id; } if ($idProduct) { $product = new Product((int) $idProduct); if (Validate::isLoadedObject($product) && isset($product->name[$this->context->language->id])) { $varList['{product_name}'] = $product->name[$this->context->language->id]; } } // Here disable sending copy of email to customer https://forum.thirtybees.com/topic/5433-contact-form-exploit-concern if (!empty($contact->email)) { if (!Mail::Send($this->context->language->id, 'contact', Mail::l('Message from contact form') . ' [no_sync]', $varList, $contact->email, $contact->name, null, null, $fileAttachment, null, _PS_MAIL_DIR_, false, null, null, $from)) { $this->errors[] = Tools::displayError('An error occurred while sending the message.'); } } } if (count($this->errors) > 1) { array_unique($this->errors); } elseif (!count($this->errors)) { $this->context->smarty->assign('confirmation', 1); } } } } }
×
×
  • Create New...