Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PCI Compliance #975

Closed
seansan opened this issue May 14, 2020 · 36 comments
Closed

PCI Compliance #975

seansan opened this issue May 14, 2020 · 36 comments

Comments

@seansan
Copy link
Contributor

seansan commented May 14, 2020

Although PCI Compliancy can be acquired by self assessment

and it is not "really" required in Europe (where I am from) becuase of GDPR regulations, and mainly only for CreditCard processing (if it happens on your site and not the PSPs site)

Howvever it would be good to make a statement on this and publically communicate LTS supports PCI Compliancy ...

https://docs.magento.com/m1/ee/user_guide/store-operations/compliance-pci.html

@rvelhote
Copy link
Contributor

My understanding of this situation regarding PCI-DSS compliance and Magento 1.9 EOL is that as long as you don't handle credit card details yourself by outsourcing to a fully compliant PCI-DSS vendor you don't need the full compliance.

In Stripe's documentation it says for example that by using Stripe Elements you automatically qualify to the most basic level of PCI-DSS compliance SAQ A (which does not contain Requirement 6).

For users that have developed their own integration and are using either Checkout or Stripe.js and Elements to collect card details from customers, you are eligible for the simplest method of PCI validation: SAQ A. Stripe automatically creates a combined SAQ A and Attestation of Compliance (AoC) for you, available for you to download in your account’s compliance settings, and no action is required on your part to submit further proof of your PCI compliance.

This is possible because Checkout and Elements host all form inputs containing card data within an iframe served from Stripe’s domain—not yours—so your customers’ card information never touches your servers.

Regarding Paypal the default Magento integration redirects to Paypal's own website which means that the website never actually sees the payment details just the final payment token which should also make it SAQ A compliant.

I only investigated the two specific payment methods that affect our business so I am not aware of how other payment integrations work.

@Flyingmana
Copy link
Member

adding some more Links

as of https://twitter.com/mbalparda/status/1262742228696395779 :
https://www.pcisecuritystandards.org/documents/FAQs-for-PCI-Software-Security-Framework-v1_0.pdf
https://www.pcisecuritystandards.org/documents/PCI-Secure-SLC-Standard-v1_0.pdf

also we should list Vendors, who post about it in a supportive way

As this is a dynamic topic, we should add a dedicated Page to our Website for PCI, we can additionally publish a Blogpost when we reached enough content, to have it forward to News Websites

@Flyingmana Flyingmana pinned this issue May 19, 2020
@seansan
Copy link
Contributor Author

seansan commented May 19, 2020 via email

@jfksk
Copy link

jfksk commented May 20, 2020

Adding a link: Self-Assessment Questionnaires
That's IMHO a nice, condensed, overview.

Most merchants offering CC payments should be able to easily fulfill the SAQ-A requirements (no audits required). Anyway, a merchant should never store CC data or even get in touch with it (= SAQ-A). Thats in it's own interest.
So it's really a non-issue issue for most merchants and pretty much any e-commerce platform.

Things like recurring CC payments with tokenization are AFAIK not even a PCI compliance subject. That's up to the merchant to provide good enough security.

I wasn't aware of fear mongering on that topic. So it would be nice to clearify that for merchants, and maybe also publish it via admin-notifications...

@simbus82
Copy link
Contributor

https://www.paypal.com/us/smarthelp/article/faq4241

Paypal says:

Q: If PayPal processes my card data, do I still need to comply?
A: Yes, even if you outsource part of your PCI DSS compliance to PayPal, you are still required to install security patches within one month of release, which will no longer be possible after June 30, 2020. In addition to these patches, merchants are responsible for meeting all requirements of their PCI DSS compliance.

Do we have to worry? Could it be that the use of "Paypal Express" is blocked?

@rossbearman
Copy link

rossbearman commented May 21, 2020

It seems unlikely that PayPal will do anything active against existing Magento 1 installations, they just won't officially support it. They already accept PayPal payments through many other EOL applications or those that do not have vendor support. In addition, they don't specifically state something to the effect of "you will no longer be able to receive payments using PayPal", but rather they say "your integration will be out of compliance" and are vague enough to make it sound scary.

As long as a store is still taking active measures to remain secure, such as continuing to apply OpenMage patches and all the security measures they should have already been taking, it would remain in compliance with PCI. In the worst case you may have to contact PayPal and explain to them that the site is still being maintained in line with PCI requirements, but I personally see even that as unlikely.

PayPal and Adobe have entered into an agreement for PayPal to offer loans to companies to upgrade to Magento 2 and it does seem like this messaging is designed to scare people into upgrading.

As a side note, PayPal's own Magento 1 EOL announcement links to the Magento Association's EOL resources, which in turn links to the OpenMage project.

@simbus82
Copy link
Contributor

Thanks @rossbearman, very clear for me now!
It's our same thinking ;-) We need to pass this info to our customers!

@rossbearman
Copy link

If the OpenMage maintainers haven't already, it may be worth reaching out to Mage One about this, as they are also trying to get some clarification from both PayPal and VISA on this matter.

(cc: @Schrank)

@seansan
Copy link
Contributor Author

seansan commented May 21, 2020 via email

@Schrank
Copy link
Contributor

Schrank commented May 21, 2020

Hey @rossbearman thanks for the mention. PCI is unfortunately a highly discussable field and the outcome is: A shop/merchant can be PCI compliant a software can not (that might be simplified until wrong, but I think you get the main point). We talked to a couple of experts, QSAs, etc. and the outcome is, only a QSA can tell you based on your single Magento website, whether you are compliant or not - and of course all of them are humans, this means, one QSA might accept OpenMage or MageOne but another doesn't. One accepts us, but not you and the other way around. So just stating that you are providing patches and therefore the shop is compliant is wrong. But I think one can argue, that you provide patches, therefore the Shop comply with 6.2 of PCI DSS. The big questions is: Is it enough for the QSA? Without having people actively searching for security vulnerabilities.

MageOne (avoiding "we" here to not confuse with us here :-)) discussed in the beginning whether we partner up and share forces with OpenMage, but there are two reasons against:

  1. OpenMage is a moving target with far more code changes than we want
  2. We don't see any way to finance a project like this and especially the bug bounty without a fee.

PCI is an annoying topic. The Specifications of PCI DSS don't know about OSS and is unspecific with the definition of 'vendor', no one is able to tell you what to do on a generic software level to be compliant (read as: if you want to know whether you are compliant, you need an assessment for your store). We and you are giving our best to be part of the solution, hopefully this is enough.

@colinmollenhour
Copy link
Member

I think it'd be great to add a list of payment processors/gateways that have quality M1 extensions which allow for SAQ A compliance (not SAQ A-EP). That is, form fields are fully hosted (redirect or iframe), multi-use tokens (saved card), refunds supported, etc. It's a lot easier to switch payment processors than it is to re-platform!

@Flyingmana
Copy link
Member

@ioweb-gr could you please move this question somewhere else? It does not belong into the topic of this issue, which is already big enough in context.

@mmenozzi
Copy link
Contributor

One customer of ours received this email from Adyen yesterday:

Dear Sir/Madam,

This email is regarding the account(s) you have on the Adyen payment platform (your payment service provider).

We thank you for your business to date, and are contacting you in light of the upcoming Magento 1 End of Life in combination with statements by payment industry parties ( such as Visa) about PCI compliance of Magento 1 merchants.

Making sure that you and your customer’s data is secure is of utmost importance to us. Unfortunately, since we cannot guarantee that this will be the case on Magento 1 moving forward, we require urgent action by you.

Our records indicate that you are currently on Magento 1, which means you need to take one of the following actions before 30 June 2020:

  • Migrate to a different eCommerce platform (Magento 2 or another supplier) in order to remain PCI-DSS compliant and secure. After your migration is completed, please inform us by sending an email to our support team.
  • If you are not using Magento 1 as your eCommerce platform, please inform us of this by sending an email to our support team.

Unless we receive an email confirming one of the above actions by 30 June 2020, we hereby give you formal notice of our decision to terminate the Merchant Agreement with your company in accordance with article 10.1 of the Terms & Conditions applicable to your Merchant Agreement.

The termination will come into effect after due observation of a 2 (two) month termination period after this letter. Therefore, unless we receive your confirmation as set out above by 30 June 2020, your account will be terminated per 30 July 2020. If you wish to continue processing payments after this date while using Magento 1, you will need to do so via a different payment provider.

As per the terms of the Merchant Agreement, you will still be responsible for any chargebacks and fraud related to the transactions we are processing for your company. After the chargeback period has ended, we will settle the final amount into your account. Please note that we reserve all rights in this respect.

Hopefully we have informed you sufficiently, we would like to thank you for doing business with Adyen. In case of inquiries, please do not hesitate to contact us.

Sincerely,

Adyen N.V.

We'll reply that we're switching to OpenMage. If this is enough for them fine, otherwise we'll switch to another payment provider.

@kanevbg
Copy link
Contributor

kanevbg commented Jun 4, 2020

@mmenozzi Do you have reply from Adyen?

@mmenozzi
Copy link
Contributor

mmenozzi commented Jun 5, 2020

Hi, sorry guys for the delay. Yes they replied... Here their response:

Hi Manuele,

I have read through your message and we completely understand your frustration. Unfortunately, due to recent statements by card schemes such as Visa in relation to Magento 1, only security patches released by Magento will allow you to be PCI DSS compliant. Since there will be no security patches released by Magento for Magento 1 after June 30 this means that you will no longer be PCI DSS compliant.

PCI DSS (Payment Card Industry Data Security Standard) are global standards set by the card schemes (such as Visa and Mastercard) and apply to all merchants that processes card payments. We would love to have given you a longer notice period however this was not possible due to the timing of statements released.

Due to the scale of the fines involved with breaches for merchants who are not PCI DSS compliant I'm afraid that we can no longer process for your business after July 30th if you remain on Magento 1.

I am sorry that I can not bring you better news and I hope we can find a way to continue working together in the future.

For more information please visit our pages:
Magento 1 End Of Life
Magento 1 FAQ

Kind regards,
Adyen Support

Note that in my message I also asked what about Magento 2.0, 2.1 and 2.2 which are already in EOL. They didn't reply to this question. And I cannot find any answer to this question elsewhere.

@ollyboy
Copy link

ollyboy commented Jun 18, 2020

We maintain system PCI compliance in the UK via nginx config. Our site undergoes regular penetration tests for this compliance. Maybe we can all agree a best practice Nginx config for LTS.

@colinmollenhour
Copy link
Member

@ollyboy I think a simple, secure, production-ready config would be great to provide, perhaps as an article on the webpage so that the user can customize it as they follow along? Docker-based of course. :)

@diamondavocado
Copy link

@ollyboy Can you share the config you have so far, please? Maybe the community can review it and work towards publishing it as an officially recommended best practice.

@seansan
Copy link
Contributor Author

seansan commented Jul 8, 2020

@ollyboy yes interested in the setup. Also checkout magenx Magento nginx setup which is quite advanced and with security built in. We added some additions on top. Main item I’ve been watching out for is to create some honeypot for admin uris that are known for invulnerabilities and if they are requested then we know it’s a bad request - and send it to ban.

@ollyboy
Copy link

ollyboy commented Jul 9, 2020

Hi all. Here is nginx config. I have text replaced my clients name with [client]. You need to review any of the [...] strings and replace for your own use. This config is passing a UK penetration/PCI scan.

# xpractical nginx config, adapted from www.hypernode.com

user www-data;

# hypernode use 4 here, we have quad core
worker_processes 8;

pid /var/run/nginx.pid;

events {
    worker_connections 1024;
}

http {

    # This uses Nginx’s Map module to set a variable $fastcgi_https. 
    # The variable is set to “off” by default, and “on” for HTTPS connections. We then use the variable to set a FastCGI parameter.
    map $scheme $fastcgi_https {
        default off;
        https on;
    }

    # extra logging
    log_format timed_combined '$remote_addr - $remote_user [$time_local] '
       '"$http_x_forwarded_for" '
       '"$request" $status $body_bytes_sent '
       '"$http_referer" "$http_user_agent" '
       '$request_time $upstream_response_time $pipe $host $request_uri';

    access_log  /var/log/nginx/access.log  timed_combined;
    error_log   /var/log/nginx/error.log   notice;

    # basic settings
    disable_symlinks off; 
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    server_tokens off;
    server_names_hash_bucket_size 64;

    # allows big media uploads
    client_max_body_size 120m;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # SSL global Settings
    #ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
    ssl_protocols TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE , dropping TLSv1 for PCI compliance
    ssl_prefer_server_ciphers on;
    # pre trustwave PCI check
    #ssl_ciphers         HIGH:!aNULL:!MD5; 
    # post trustwave PCI check
    ssl_ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS;
    
    # GeoIP support is included in the Ubuntu 12.04 Nginx.
    # This enables logging, and the following:
    #    if ($geoip_country_code ~ (CN|ZW) ) {
    #      return 403;
    #    }
    # geoip_country         /usr/share/GeoIP/GeoIP.dat;
    
    # CloudFlare GeoIP Mapping 07-10-2016
        map $http_cf_ipcountry $country {
		default us;
		AU au; NZ au;
		CA ca;
		DE uk; GB uk; FR uk; AT uk; BE uk; HR uk; CY uk; CZ uk; DK uk; EE uk; FI uk; GR uk; HU uk; IS uk; IE uk; IT uk; LU uk; MD uk; MC uk; NL uk; NO uk; PL uk; PT uk; RO uk; RU uk; SK uk; SI uk; ES uk; SE uk; CH uk; UA uk; VA uk;
	}
    
    #gzip settings
        gzip on;
        gzip_disable "msie6";
        gzip_vary on;
        gzip_proxied any;
        gzip_comp_level 6;
        gzip_buffers 16 8k;
        gzip_http_version 1.1;
        gzip_min_length 1100;
        gzip_types
           text/plain
           text/css
           text/js
           text/xml
           text/javascript
           application/javascript
           application/x-javascript
           application/json
           application/xml
           application/xml+rss ;

    # Determine whether a request comes from a human, a search crawler or another bot.
    # [client] SOE bot is Screaming Frog SEO Spider/7.2
    map $http_user_agent $is_non_search_bot {
        default '';
        ~*(Screaming|Frog|dotMailer) '';
        ~*(google|pingdom|monitis.com|Zend_Http_Client) '';
        ~*(http|bing|crawler|spider|bot|search|ForusP|Wget/|Python-urllib|PHPCrawl|bGenius|LieBaoFast) 'bot';
        ~*(Mb2345Browser|zh-CN|MicroMessenger|zh_CN|HUAWEIFRD) 'bot';
    }
    
    # Rate limit bots (that are not search spiders) to one PHP request per second.
    # An empty '$limit_bots' would disable rate limiting for this requests

    # limit_req_zone $is_non_search_bot zone=bots:1m rate=1r/s;
    limit_req_zone $is_non_search_bot zone=bots:1m rate=6r/m;

    # need to use x_forward so we dont limit CDN
    limit_req_zone $http_x_forwarded_for zone=allusers:16m rate=5r/s;

    limit_req_log_level error;

    # As this is the first server definition it will be used if there is a good match to server_name OR
    # if there is no match to any server name
    server {

        server_name [client].com, [client]one.org;
        # if no server name all requests get here
        #server_name [client].com www.[client].com

        # single server that listens on both http and https
        listen              80;
        listen              443 default ssl;

        include /var/www/keyconfigfiles/cloudflare.whitelist;
        #deny all;
        # go daddy crts, not complete need to generate bundle
        #ssl_certificate     /home/[mag_user]/xp-demo.com/e7a321f0b88a7e89.crt;
        #ssl_certificate_key /home/[mag_user]/xp-demo.com/xp-demo.key;

        # may be able to use these SSL should be linked to domain and web server not the hardware or IP
        #ssl_certificate     /var/www/ssl/[client].com.crt;
        #ssl_certificate_key /var/www/ssl/[client].com.key;
	ssl_certificate     /var/www/ssl2018/[client].com.crt;
        ssl_certificate_key /var/www/ssl2018/[client].com.key;

        # openssl req -new -newkey rsa:2048 -nodes -keyout example.key -out example.csr
        # use the csr request to get the crt files
        # cat all crt's into one bundle file, make sure the main one is top of list
        #ssl_certificate     /home/[mag_user]/[client]one_org-ssl-bundle.crt;
        #ssl_certificate_key /home/[mag_user]/[client]one_org.key;
       
        # If the site is accessed via mydomain.com
        # automatically redirect to www.mydomain.com.
        #if ($host = 'mydomain.com' ) {
        #    rewrite ^/(.*)$ http://www.mydomain.com/$1permanent;
        #}

        # if you are using a load balancer uncomment these lines
        # header from the hardware load balancers
        #real_ip_header X-Forwarded-For;
        # trust this header from anything inside the subnet
        #set_real_ip_from X.X.X.1/24;
        # the header is a comma-separated list; the left-most IP is the end user
        #real_ip_recursive on;
        #listen      10.10.10.11:80;
        #set_real_ip_from 103.248.190.60;

        # use this to test pre-production magento folder, danger new code will update database
        set $my_path var/www/magento;
        if ($remote_addr ~ "^(110\.175\.187\.117)$" ) {
          # set $my_path var/www/temp-magento;
          set $my_path var/www/magento;
        }
        root /$my_path;
       
        error_page 500 502 503 504 /custom_50x.html;
        location = /custom_50x.html {
          root /usr/share/nginx/html;
          internal;
        }
 
        # magento specific
        set $storecode "default";
        index index.html index.php;
        autoindex off; # we don’t want users to see files in directories

        # security
        if ($http_user_agent = "") { return 444;}

        # sitemap files
        location ~ /sitemap.xml|/([a-z][a-z]|[a-z][a-z]-[a-z][a-z])/(.*)sitemap.xml {
           try_files /sitemaps/$1/sitemap.xml /sitemaps/sitemap.xml =404;
        }

	# allow view of sub folders and files here
        location /downloads/ { autoindex on; }

        # Denied locations require a "^~" to prevent regexes (such as the PHP handler below) from matching
        # http://nginx.org/en/docs/http/ngx_http_core_module.html#location
        location ^~ /app/                       { return 403; }
        location ^~ /shell/                     { return 403; }
        location ^~ /vendor/                    { return 403; }
        location ^~ /includes/                  { return 403; }
        location ^~ /media/downloadable/        { return 403; }
        location ^~ /pkginfo/                   { return 403; }
        location ^~ /report/config.xml          { return 403; }
        location ^~ /var/                       { return 403; }
        location ^~ /lib/                       { return 403; }
        location ^~ /dev/                       { return 403; }
        location ^~ /RELEASE_NOTES.txt          { return 403; }
        location ^~ /downloader/pearlib         { return 403; }
        location ^~ /downloader/template        { return 403; }
        location ^~ /downloader/Maged           { return 403; }
        location ~* ^/errors/.+\.xml            { return 403; }

        # Extra protection
        location ~* ^/rss/(order|catalog)/ { return 444; }
        location ~ /(dev/tests/|errors/local.xml|cron\.php) { deny all; }
        # note this will stop zip download  location ~ ^/.*\.(sh|pl|swp|phar|sql|conf|zip|tar|.+gz)$ { return 444; }
        location ~ ^/.*\.(sh|pl|swp|phar|sql|conf|tar|.+gz)$ { return 444; }
        location ~ /\.(svn|git|hg|htpasswd|bash|ssh) { return 444; }
        location ~* /(lib|media|shell|skin)/.*\.php$ { deny all; }
       
        # CVE-2015-3428 / AW_Blog vulnerability
        # Note the .+ at the start: We want to allow url's like
        # order=create_date, which would otherwise match.
        if ($arg_order ~* .+(select|create|insert|update|drop|delete|concat|alter|load)) {
           return 403;
        }

        # Don't skip .thumbs, this is a default directory where Magento places thumbnails
        # Nginx cannot "not" match something, instead the target is matched with an empty block
        # http://stackoverflow.com/a/16304073
        location ~ /\.thumbs {
        }

        # Skip .git, .htpasswd etc
        location ~ /\. {
            return 404;
        }
        
        # Disable all methods besides HEAD, GET and POST.
        if ($request_method !~ ^(GET|HEAD|POST)$ ) { return 444; }
   
        # Try static files first, then pass to Magento handler
        location / {
         
          # stop specific IPs until fixed in CDN - added in to CF 24032020
          #deny 77.246.157.20;
 
          # enable limits
          limit_req zone=allusers burst=5 nodelay;
          limit_req zone=bots;
          #
          try_files $uri $uri/ @handler;
          access_log off; # do not log access to static files
          expires max;    # cache static files aggressively
        }

        # Pass paths to Magento entry point
        location @handler {
           rewrite / /index.php?$args;
        }   
    
        # Forward paths like /js/index.php/x.js to relevant handler
        location ~ \.php/ {
           rewrite ^(.*\.php)/ $1 last;
        }   

        # Run PHP files using FastCGI
        location ~ \.php$ {
           try_files $uri $uri/ =404;
           expires off; # no need to cache php executable files
           #try_files $uri $uri/ @handler; 
           #fastcgi_pass   127.0.0.1:9000;
           #fastcgi_pass unix:/var/run/hhvm/hhvm.sock;
           fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
           fastcgi_keep_conn on; # use persistent connects to backend
           fastcgi_param  HTTPS $fastcgi_https;   # needed for https
           fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
           fastcgi_param  MAGE_RUN_CODE $storecode;
           fastcgi_param  MAGE_RUN_TYPE store; ## or website;

            fastcgi_buffer_size 128k;
            fastcgi_buffers 256 16k;
            fastcgi_busy_buffers_size 256k;
            fastcgi_temp_file_write_size 256k;

           include        fastcgi_params; ## See /etc/nginx/fastcgi_params
       }   

       # [client]store Banner 30m browser expiry
       location = /media/bannerslider/s/w/[client]store_au_banner.jpg {
          expires 30m;
       }

       location = /media/bannerslider/s/w/[client]store_us_banner.jpg {
          expires 30m; # All other image expires
       }
       location ~* \.(?:csv|tsv|xml|txt)$ {
          expires 1m; # dont cache data feeds
       }

       location ^~ /blog/ {
	   proxy_intercept_errors off;
	   proxy_pass https://blog.[client].com/;
       }

       location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
          expires 5d; 
          log_not_found off;
       }

    }

    # add the other servers here, these only work if matched, they tell the browser to cache this redirect
    # once a browser has one of these 301 redirects it will always use it until cache is cleared

    server {
        server_name www.[client]store.co.uk [client]store.co.uk;
        return 301 $scheme://www.[client].com/uk$request_uri;
    }

    server {
        server_name www.[client]store.com.au [client]store.com.au www.[client].com.au [client].com.au;
        return 301 $scheme://www.[client].com/au$request_uri;
    }

    server {
        server_name www.[client]store.com [client]store.com www.[client]security.com [client]security.com;
        return 301 $scheme://www.[client].com/us$request_uri;
    }
    
}

@ollyboy
Copy link

ollyboy commented Jul 9, 2020

Some notes on this:

  • the PCI work was trial and error, we did set up, the external audit did penetration/probe testing, we adjusted settings until given a pass
  • we used to use hhvm until php 7 turned up. Boy was that a speed advantage back in the php 5 days
  • Note that we dont service any traffic unless it comes from CDN, protecting magneto php code from noise can really reduce load on you systems
  • we use plenty of rate limiting as this is a well known global site and we get attacks once to twice a month
  • you may think its crazy but we use no DB server or load balancers or multiple nginx servers, its just not needed if you setup magneto well, magento is very talkative to the database, see the use of sockets instead of IP. DB and php using sokets on one server is faster that seperate DB and the web server.
  • pages have got slower over the years as marketing add more and more stuff
  • the rate limiting by x_forward_for is a good trick when using CDN. Be careful to not limit you CDN via the primary IP passed in requests
  • we use no varnish and we gave up on magento cache modules, you dont need them. Just use CDN, GC or AWS firewall and a 4-8 core 32gig mem ubuntu server with 100gig SSD hard disk, all image assets are on the SSD
  • we have a hacked up index.php for more protection. Can publish a copy of that if people interested, but its a bit ugly
  • Here we take care to not allow magneto folders to be visible, we rename the admin path something crazy hard that no one will guess, although I do think there may be some ways to display this path and should put in IP restriction to any admin
  • all config is in this file, we probably should break into bits and use site_enabled but if we get an attack late at night tired brains work better on one file

@ansgarbecker
Copy link

Regarding Stripe, using their latest module version - am I safe in PCI terms to use it with OpenMage, or do I need some asssessor checking my site?
Reading their Magento end of life article :

... you’ll be required to provide additional certification of your Magento 1 compensating controls by a PCI Qualified Security Assessor (QSA).

@diamondavocado
Copy link

* we have a hacked up index.php for more protection. Can publish a copy of that if people interested, but its a bit ugly

Yes please. The more info we have the better. :)

@colinmollenhour
Copy link
Member

@ansgarbecker Ouch, if you're not already doing it, hiring a QSA will be a significant additional cost..

What Stripe extension are you using?

I would suggest you have a backup plan for moving to a new processor (that doesn't require a QSA) and then trying the approach of "I am migrating to OpenMage LTS, please support it with your Magento 1 extension" and see if that is accepted. If you're using a third-party extension you could contact the extension developer and ask them to support OpenMage (no changes required, just a pledge really) and then that would be another good argument for OpenMage acceptance by Stripe. If you have a contact at Stripe it would be great to convince them to add OpenMage to the list of supported options for M1 users.

@ansgarbecker
Copy link

ansgarbecker commented Jul 24, 2020

@colinmollenhour we're using their official Stripe/Payment module latest version (v1.1.5 currently), from their download page.
(Btw, we're also using your Redis module, thanks hereby!)
I will go and ask the Stripe support for accepting OpenMage - nice idea.

@colinmollenhour
Copy link
Member

It looks like they've scrubbed the M1 extension from their help doc.. Please keep me posted on how it goes with the Stripe conversation or feel free to include me in it directly (colin.mollenhour@openamge.org).

I see you're the author of HeidiSQL which I've used before, so thanks to you as well! :)

@ansgarbecker
Copy link

Good news from Stripe payments provider:

If you have opted in for OpenMage, then for the foreseeable future, there won't be any PCI compliance concerns as updates will be supplied by both Stripe (for the Stripe for Magento Plugin) and OpenMage (for your Magento website).

@colinmollenhour
Copy link
Member

colinmollenhour commented Aug 17, 2020

@ansgarbecker That's awesome! Unfortunately it looks like they removed the M1 plugin docs from their site, it would be great if they'd restore the old docs and update references to Magento with OpenMage. Can you ask them about that?

EDIT: I see now there is a separate "tab" for Magento 1..

It would be great if there was an official mention of OpenMage on their EOL page or the Magento 1 tab, or if they added a new tab specifically for OpenMage since we are considering removing the Connect manager.. Or maybe this is a strong case for keeping it? (#903)

@colinmollenhour colinmollenhour changed the title PCI Compliancy PCI Compliance Aug 26, 2020
@Adel-Magebinary
Copy link
Collaborator

Hi all. Here is nginx config. I have text replaced my clients name with [client]. You need to review any of the [...] strings and replace for your own use. This config is passing a UK penetration/PCI scan.

# xpractical nginx config, adapted from www.hypernode.com

user www-data;

# hypernode use 4 here, we have quad core
worker_processes 8;

pid /var/run/nginx.pid;

events {
    worker_connections 1024;
}

http {

    # This uses Nginx’s Map module to set a variable $fastcgi_https. 
    # The variable is set to “off” by default, and “on” for HTTPS connections. We then use the variable to set a FastCGI parameter.
    map $scheme $fastcgi_https {
        default off;
        https on;
    }

    # extra logging
    log_format timed_combined '$remote_addr - $remote_user [$time_local] '
       '"$http_x_forwarded_for" '
       '"$request" $status $body_bytes_sent '
       '"$http_referer" "$http_user_agent" '
       '$request_time $upstream_response_time $pipe $host $request_uri';

    access_log  /var/log/nginx/access.log  timed_combined;
    error_log   /var/log/nginx/error.log   notice;

    # basic settings
    disable_symlinks off; 
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    server_tokens off;
    server_names_hash_bucket_size 64;

    # allows big media uploads
    client_max_body_size 120m;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # SSL global Settings
    #ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
    ssl_protocols TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE , dropping TLSv1 for PCI compliance
    ssl_prefer_server_ciphers on;
    # pre trustwave PCI check
    #ssl_ciphers         HIGH:!aNULL:!MD5; 
    # post trustwave PCI check
    ssl_ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS;
    
    # GeoIP support is included in the Ubuntu 12.04 Nginx.
    # This enables logging, and the following:
    #    if ($geoip_country_code ~ (CN|ZW) ) {
    #      return 403;
    #    }
    # geoip_country         /usr/share/GeoIP/GeoIP.dat;
    
    # CloudFlare GeoIP Mapping 07-10-2016
        map $http_cf_ipcountry $country {
		default us;
		AU au; NZ au;
		CA ca;
		DE uk; GB uk; FR uk; AT uk; BE uk; HR uk; CY uk; CZ uk; DK uk; EE uk; FI uk; GR uk; HU uk; IS uk; IE uk; IT uk; LU uk; MD uk; MC uk; NL uk; NO uk; PL uk; PT uk; RO uk; RU uk; SK uk; SI uk; ES uk; SE uk; CH uk; UA uk; VA uk;
	}
    
    #gzip settings
        gzip on;
        gzip_disable "msie6";
        gzip_vary on;
        gzip_proxied any;
        gzip_comp_level 6;
        gzip_buffers 16 8k;
        gzip_http_version 1.1;
        gzip_min_length 1100;
        gzip_types
           text/plain
           text/css
           text/js
           text/xml
           text/javascript
           application/javascript
           application/x-javascript
           application/json
           application/xml
           application/xml+rss ;

    # Determine whether a request comes from a human, a search crawler or another bot.
    # [client] SOE bot is Screaming Frog SEO Spider/7.2
    map $http_user_agent $is_non_search_bot {
        default '';
        ~*(Screaming|Frog|dotMailer) '';
        ~*(google|pingdom|monitis.com|Zend_Http_Client) '';
        ~*(http|bing|crawler|spider|bot|search|ForusP|Wget/|Python-urllib|PHPCrawl|bGenius|LieBaoFast) 'bot';
        ~*(Mb2345Browser|zh-CN|MicroMessenger|zh_CN|HUAWEIFRD) 'bot';
    }
    
    # Rate limit bots (that are not search spiders) to one PHP request per second.
    # An empty '$limit_bots' would disable rate limiting for this requests

    # limit_req_zone $is_non_search_bot zone=bots:1m rate=1r/s;
    limit_req_zone $is_non_search_bot zone=bots:1m rate=6r/m;

    # need to use x_forward so we dont limit CDN
    limit_req_zone $http_x_forwarded_for zone=allusers:16m rate=5r/s;

    limit_req_log_level error;

    # As this is the first server definition it will be used if there is a good match to server_name OR
    # if there is no match to any server name
    server {

        server_name [client].com, [client]one.org;
        # if no server name all requests get here
        #server_name [client].com www.[client].com

        # single server that listens on both http and https
        listen              80;
        listen              443 default ssl;

        include /var/www/keyconfigfiles/cloudflare.whitelist;
        #deny all;
        # go daddy crts, not complete need to generate bundle
        #ssl_certificate     /home/[mag_user]/xp-demo.com/e7a321f0b88a7e89.crt;
        #ssl_certificate_key /home/[mag_user]/xp-demo.com/xp-demo.key;

        # may be able to use these SSL should be linked to domain and web server not the hardware or IP
        #ssl_certificate     /var/www/ssl/[client].com.crt;
        #ssl_certificate_key /var/www/ssl/[client].com.key;
	ssl_certificate     /var/www/ssl2018/[client].com.crt;
        ssl_certificate_key /var/www/ssl2018/[client].com.key;

        # openssl req -new -newkey rsa:2048 -nodes -keyout example.key -out example.csr
        # use the csr request to get the crt files
        # cat all crt's into one bundle file, make sure the main one is top of list
        #ssl_certificate     /home/[mag_user]/[client]one_org-ssl-bundle.crt;
        #ssl_certificate_key /home/[mag_user]/[client]one_org.key;
       
        # If the site is accessed via mydomain.com
        # automatically redirect to www.mydomain.com.
        #if ($host = 'mydomain.com' ) {
        #    rewrite ^/(.*)$ http://www.mydomain.com/$1permanent;
        #}

        # if you are using a load balancer uncomment these lines
        # header from the hardware load balancers
        #real_ip_header X-Forwarded-For;
        # trust this header from anything inside the subnet
        #set_real_ip_from X.X.X.1/24;
        # the header is a comma-separated list; the left-most IP is the end user
        #real_ip_recursive on;
        #listen      10.10.10.11:80;
        #set_real_ip_from 103.248.190.60;

        # use this to test pre-production magento folder, danger new code will update database
        set $my_path var/www/magento;
        if ($remote_addr ~ "^(110\.175\.187\.117)$" ) {
          # set $my_path var/www/temp-magento;
          set $my_path var/www/magento;
        }
        root /$my_path;
       
        error_page 500 502 503 504 /custom_50x.html;
        location = /custom_50x.html {
          root /usr/share/nginx/html;
          internal;
        }
 
        # magento specific
        set $storecode "default";
        index index.html index.php;
        autoindex off; # we don’t want users to see files in directories

        # security
        if ($http_user_agent = "") { return 444;}

        # sitemap files
        location ~ /sitemap.xml|/([a-z][a-z]|[a-z][a-z]-[a-z][a-z])/(.*)sitemap.xml {
           try_files /sitemaps/$1/sitemap.xml /sitemaps/sitemap.xml =404;
        }

	# allow view of sub folders and files here
        location /downloads/ { autoindex on; }

        # Denied locations require a "^~" to prevent regexes (such as the PHP handler below) from matching
        # http://nginx.org/en/docs/http/ngx_http_core_module.html#location
        location ^~ /app/                       { return 403; }
        location ^~ /shell/                     { return 403; }
        location ^~ /vendor/                    { return 403; }
        location ^~ /includes/                  { return 403; }
        location ^~ /media/downloadable/        { return 403; }
        location ^~ /pkginfo/                   { return 403; }
        location ^~ /report/config.xml          { return 403; }
        location ^~ /var/                       { return 403; }
        location ^~ /lib/                       { return 403; }
        location ^~ /dev/                       { return 403; }
        location ^~ /RELEASE_NOTES.txt          { return 403; }
        location ^~ /downloader/pearlib         { return 403; }
        location ^~ /downloader/template        { return 403; }
        location ^~ /downloader/Maged           { return 403; }
        location ~* ^/errors/.+\.xml            { return 403; }

        # Extra protection
        location ~* ^/rss/(order|catalog)/ { return 444; }
        location ~ /(dev/tests/|errors/local.xml|cron\.php) { deny all; }
        # note this will stop zip download  location ~ ^/.*\.(sh|pl|swp|phar|sql|conf|zip|tar|.+gz)$ { return 444; }
        location ~ ^/.*\.(sh|pl|swp|phar|sql|conf|tar|.+gz)$ { return 444; }
        location ~ /\.(svn|git|hg|htpasswd|bash|ssh) { return 444; }
        location ~* /(lib|media|shell|skin)/.*\.php$ { deny all; }
       
        # CVE-2015-3428 / AW_Blog vulnerability
        # Note the .+ at the start: We want to allow url's like
        # order=create_date, which would otherwise match.
        if ($arg_order ~* .+(select|create|insert|update|drop|delete|concat|alter|load)) {
           return 403;
        }

        # Don't skip .thumbs, this is a default directory where Magento places thumbnails
        # Nginx cannot "not" match something, instead the target is matched with an empty block
        # http://stackoverflow.com/a/16304073
        location ~ /\.thumbs {
        }

        # Skip .git, .htpasswd etc
        location ~ /\. {
            return 404;
        }
        
        # Disable all methods besides HEAD, GET and POST.
        if ($request_method !~ ^(GET|HEAD|POST)$ ) { return 444; }
   
        # Try static files first, then pass to Magento handler
        location / {
         
          # stop specific IPs until fixed in CDN - added in to CF 24032020
          #deny 77.246.157.20;
 
          # enable limits
          limit_req zone=allusers burst=5 nodelay;
          limit_req zone=bots;
          #
          try_files $uri $uri/ @handler;
          access_log off; # do not log access to static files
          expires max;    # cache static files aggressively
        }

        # Pass paths to Magento entry point
        location @handler {
           rewrite / /index.php?$args;
        }   
    
        # Forward paths like /js/index.php/x.js to relevant handler
        location ~ \.php/ {
           rewrite ^(.*\.php)/ $1 last;
        }   

        # Run PHP files using FastCGI
        location ~ \.php$ {
           try_files $uri $uri/ =404;
           expires off; # no need to cache php executable files
           #try_files $uri $uri/ @handler; 
           #fastcgi_pass   127.0.0.1:9000;
           #fastcgi_pass unix:/var/run/hhvm/hhvm.sock;
           fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
           fastcgi_keep_conn on; # use persistent connects to backend
           fastcgi_param  HTTPS $fastcgi_https;   # needed for https
           fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
           fastcgi_param  MAGE_RUN_CODE $storecode;
           fastcgi_param  MAGE_RUN_TYPE store; ## or website;

            fastcgi_buffer_size 128k;
            fastcgi_buffers 256 16k;
            fastcgi_busy_buffers_size 256k;
            fastcgi_temp_file_write_size 256k;

           include        fastcgi_params; ## See /etc/nginx/fastcgi_params
       }   

       # [client]store Banner 30m browser expiry
       location = /media/bannerslider/s/w/[client]store_au_banner.jpg {
          expires 30m;
       }

       location = /media/bannerslider/s/w/[client]store_us_banner.jpg {
          expires 30m; # All other image expires
       }
       location ~* \.(?:csv|tsv|xml|txt)$ {
          expires 1m; # dont cache data feeds
       }

       location ^~ /blog/ {
	   proxy_intercept_errors off;
	   proxy_pass https://blog.[client].com/;
       }

       location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
          expires 5d; 
          log_not_found off;
       }

    }

    # add the other servers here, these only work if matched, they tell the browser to cache this redirect
    # once a browser has one of these 301 redirects it will always use it until cache is cleared

    server {
        server_name www.[client]store.co.uk [client]store.co.uk;
        return 301 $scheme://www.[client].com/uk$request_uri;
    }

    server {
        server_name www.[client]store.com.au [client]store.com.au www.[client].com.au [client].com.au;
        return 301 $scheme://www.[client].com/au$request_uri;
    }

    server {
        server_name www.[client]store.com [client]store.com www.[client]security.com [client]security.com;
        return 301 $scheme://www.[client].com/us$request_uri;
    }
    
}

I will be review this config and get back to you in the next few days.

@Adel-Magebinary
Copy link
Collaborator

Some notes on this:

  • the PCI work was trial and error, we did set up, the external audit did penetration/probe testing, we adjusted settings until given a pass
  • we used to use hhvm until php 7 turned up. Boy was that a speed advantage back in the php 5 days
  • Note that we dont service any traffic unless it comes from CDN, protecting magneto php code from noise can really reduce load on you systems
  • we use plenty of rate limiting as this is a well known global site and we get attacks once to twice a month
  • you may think its crazy but we use no DB server or load balancers or multiple nginx servers, its just not needed if you setup magneto well, magento is very talkative to the database, see the use of sockets instead of IP. DB and php using sokets on one server is faster that seperate DB and the web server.
  • pages have got slower over the years as marketing add more and more stuff
  • the rate limiting by x_forward_for is a good trick when using CDN. Be careful to not limit you CDN via the primary IP passed in requests
  • we use no varnish and we gave up on magento cache modules, you dont need them. Just use CDN, GC or AWS firewall and a 4-8 core 32gig mem ubuntu server with 100gig SSD hard disk, all image assets are on the SSD
  • we have a hacked up index.php for more protection. Can publish a copy of that if people interested, but its a bit ugly
  • Here we take care to not allow magneto folders to be visible, we rename the admin path something crazy hard that no one will guess, although I do think there may be some ways to display this path and should put in IP restriction to any admin
  • all config is in this file, we probably should break into bits and use site_enabled but if we get an attack late at night tired brains work better on one file

Can you please post your index.php?

@seansan
Copy link
Contributor Author

seansan commented Sep 28, 2020

Index php interest too ;)

Addition to nginx conf

################# ADMIN AREA PROTECTION NEXT TO OTHER #################

## Best admin area protection
set $ADMIN_URI "mysecretbackend";

## Main Magento @location
## You can also add some rate limiting on backend ... to prevent ddos/brute force
location ~ ^/(index.php/)?$ADMIN_URI {
   auth_basic "Private Area";
   auth_basic_user_file /var/www/html/$ROOT/.htpasswd;
   try_files $uri $uri/ @rewrite;
   expires max;
   }

################# COMMONS STOPS #################

location ~ rss/(order|catalog) {deny all;}
location ~ /(dev/tests/|errors/local.xml|cron\.php|install\.php) { deny all; }
location ~ ^/([^/])+\.(sh|pl|swp|phar|sql|zip|conf|tar|gz)$ { return 403; }
location ~ /\.(svn|git|hg|htpasswd|bash|ssh) { return 410; }
location ~* /(lib|media|shell|skin)/.*\.php$ { deny all; }
location ~ ^/(app)/ { deny all; }
location ~ ^/.*(modman|myadmin).*$ { return 410; }
location ~ magenotification { return 410; }

################# BLOCK SEO TOOLS #################

# BLOCK SEO TOOLS
# BLock aggresive crawlers
if ($http_user_agent ~* "LieBaoFast|Mb2345Browser|UCBrowser|megaindex|MQQBrowser|mj12bot|mj12|GarlikCrawler") { return 403; }
if ($http_user_agent ~* "ShoppimonAgent|MauiBot|DomainCrawler|MJ12bot|SemrushBot|OpenLinkProfiler|rogerbot|exabot|dotbot|gigabot|sitebot|spbot|prijsbest|seokicks") { return 403; }

# BLOCK BOTS AND CRAWLERS THAT MAKE NO SENSE
# IF ITS NOT A HUMAN AND NOT GOOGLE OR BING THEN BLOCK THEM
if ($http_user_agent ~* "SpiderLing|360Spider|VelenPublicWebCrawler|Baiduspider|MegaIndex|Yahoo|YandexBot|Barkrowler|ZoominfoBot|MJ12bot|Cliqzbot|DotBot|YandexBot|SearchAtlas") { return 403; }
if ($http_user_agent ~* "Go-http-client|GuzzleHttp|Slackbot|SeznamBot|coccocbot|SemrushBot|Sogou|MojeekBot|DomainStatsBot|prijsbest|GrapeshotCrawler") { return 403; }
if ($http_user_agent ~* "Vagabondo|Bilbo|MegaIndex|GrapeshotCrawler|BLEXBot|rc-crawler|SearchAtlas|linkfluence|Internet-structure-research") { return 403; }
if ($http_user_agent ~* "RU_Bot|MojeekBot|SE Ranking Gentle|MixnodeCache|bidswitchbot|niocBot|SeznamBot|YandexImages| oBot/|Konturbot|ZoomBot|YandexMetrika|Cincraw") { return 403; }
if ($http_user_agent ~* "Linkbot|YandexMobileBot|CCBot|Sidetrade|TweetmemeBot") { return 403; }

# if ($http_user_agent ~* "AhrefsBot|AhrefsSiteAudit") { return 403; }

################# RATE LIMIT DYNAMIC URLS #################

location ~ ^/(hsapi|wishlist|customer|catalogsearch|newsletter|sales/guest/view|contacts/index/post|review/product/post|(fire|one.+)?checkout)/ {
        limit_req zone=goeasy burst=6;
        limit_req_status 429;
        # if ($http_user_agent ~* "Baiduspider|AdsBot|Google|DotBot|bingbot|Yahoo|YandexBot") { return 410; }
        try_files $uri $uri/ @rewrite;
       }

################# STOP HACKERS LOOKING FOR LOOP HOLES #################

# HALT TO HACKERS https://gwillem.gitlab.io/2018/10/23/magecart-extension-0days/
location ~* ^/index.php/(quickshop|giftcards|quickview|catalog/product_frontend_action|madecache|freegift|qquoteadv|ajaxproducts|minifilterproducts|advancedreports|bssreorderproduct|rewards|gwishlist|aheadmetrics|customgrid|tabshome|vendors|multidealpro|layaway|simplebundle|netgocust|prescription|ajax/Showroom)/.*$ { 
        deny all; 
        }

## Block some common admin url searchers
## Would rather send this to fw ban fail2ban; you kind of know they are wrong clients
location ~* ^(/en|/fr|/nl|/de)?/(BackgroundTask|M2ePro|bannerpopup|admin|beheer|memberadmin|superadmin|admin2016|admin2014|admin_area|administratorlogin|admin1|ctrl_page|admin111|admin123|admin3|admin2|adminlogin|admin2010|admin2012|admin2013|admin2015|panel-administracion|moderator|myadmin|admin5|admin4|adminsecret|adminarea)/?.*$ { 
	return 444; 
	}

## Would rather send this to fw ban fail2ban; you kind of know they are wrong clients
location ~* ^(/en|/fr|/nl|/de)?/(advradmin|forum_admin|blog_admin|rewardsadmin|rma_admin|banner/admin|amflags/admin|amshopby/admin|affiliateplusa|amacart/adminh|searchindex/adm|wp_usersbackup|wp_users_backu|site_database|marketsuite/adm|affiliatepluss|amnotfound/adm|customerprices|amogrid/admi|amoaction/admi|amfile/admi|ammethods/admi|searchsphinx/adm|orderattach/adm|megamenuadmin|exporter/admin|imagegallery/adm|igallery/admin|attributemanag|amcustomerattr|vendor/phpunit|presets-admin|slocator/adm|profitlossrepo|mageworx/downl|ecfplus/admi) {
	return 444;
	}

################# HALT TO OLD ADMIN URLS FROM THE PAST #################

location ~* ^/(oldadmin1|oldadmin2) { deny all; }

###### OPTIONAL: START Blocks good bots that just crawl too much ######

# Step 1 identify the good bots
# CCBOt is open source bot
if ($http_user_agent ~* "Googlebot|bingbot|MegaIndex|Yahoo|YandexBot") { set $is_goodbot O; }

# Step 2 identify the actions we need to block for these bots
# All Magento attributes that can be filtered paste them here
if ($args ~ ^(bowtie_type|color|cost|country_of_manufacture|custom_comment|custom_design|custom_design_from|custom_design_to|custom_gender|custom_layout_update|delivery_period|description|discount_option|discount_selector|ebizmarts_mark_visited|gallery|gift_message_available|google_product_category|group_price|gr_ean|gr_valid_through|image|is_recurring|manufacturer|materiaal_type|media_gallery|meta_description|meta_keyword|meta_title|msrp|msrp_display_actual_price_type|msrp_enabled|naam_merchandiser|name|news_from_date|news_to_date|occasion|options_container|pack_multiplier|pack_size|page_layout|postnl_allow_delivery_days|postnl_allow_delivery_options|postnl_allow_pakje_gemak|postnl_allow_pakketautomaat|postnl_allow_timeframes|postnl_max_qty_for_buspakje|postnl_product_parcel_count|postnl_product_type|postnl_product_volume|postnl_shipping_duration|preorder_cart_label|preorder_note|price|price_view|products_in_pack|product_collection|product_discount|product_mainmaterial|product_minqty|product_modelname|product_noslosseas|product_overlay|product_popup|product_season|product_sluiting|product_taxonomy|product_width|recurring_profile|rw_google_base_12_digit_sku|rw_google_base_product_categ|rw_google_base_product_type|rw_google_base_skip_submi|searchindex_weight|shirt_boord|shirt_boordkleur|shirt_colours|shirt_coupenaad|shirt_cuff|shirt_dessin|shirt_fit|shirt_generalfit|shirt_iron|shirt_length|shirt_mill|shirt_neck|shirt_pocket|shirt_size|shirt_sleevelength|shirt_stof|shirt_stretch|shirt_subtype|shirt_tapekleur|shirt_threadcount|shirt_twoply|shirt_weving|short_description|sku|small_image|soort_shirt|special_from_date|special_price|special_to_date|status|strikmethode|tax_class_id|thumbnail|tier_price|tradetracker_exclude|type_shirt|underwear_length|url_key|visibility|washing_instructions|weight)=.+) { set $bot_layered_nav "${is_goodbot}N"; }
# Sorting listing ordering
if ($args ~ ^(dir|order|limit|sort)=.+) { set $bot_layered_nav "${is_goodbot}N"; }
# Block google searching all the time
if ($args ~ ^(q)=.+) { set $bot_layered_nav "${is_goodbot}N"; }
# No need to index the checkout checkout/login/account and such
if ($request_uri ~ 'checkout|account|login|cart|onepage|success') { set $bot_layered_nav "${is_goodbot}N"; }

# Step 3 if both step 1 and 2 are true then block the bot and send 410
if ($bot_layered_nav = ON) { return 410; }

###### END Blocks good bots that just crawl too much ######

################# NOPE WE ARE NOT WORDPRESS #################

## Wordpress files and locations protection
location ~ /wp-config\.php { deny all; }
location ~ /wp-includes/(.*)\.php { deny all; }
location ~ /wp-admin/includes(.*)$ { deny all; }
location ~ /xmlrpc\.php { deny all; }
location ~ /wp-content/uploads/(.*)\.php(.?) { deny all; }

################# BLOCK MYSQL #################

## Block MYSQL
if ($request_uri ~ '.*select.*from.*where.*') { return 444; }
if ($request_uri ~ '.*update.*set.*where.*') { return 444; }
if ($request_uri ~ '.*select.*sleep.*') { return 444; }
if ($request_uri ~ '.*insert into.*values.*') { return 444; }

@colinmollenhour
Copy link
Member

@Adel-Magebinary and @seansan please see the PR #1209 which proposes a simple nginx config as the default dev environment. It is not meant to be production-ready per-se but I would like to create a simple "reference" config that users can use as a starting point for replacing Apache.

This config uses a very simple and secure technique for controlling what static files are made public using a separate "/pub" directory rather than using the Magento root for static files. It also restricts all PHP requests to just index.php rather than allowing arbitrary fastcgi script filenames (avoids using $fastcgi_script_name). If your deployment's source code is write-protected from the web server user it should make it near impossible to ever run unauthorized PHP code without the entire server first getting hacked.

Your additional WAF-like rules are a great reference, perhaps these could be added to "WAF rules cookbook" wiki page so users can pick and choose and customize easily?

https://github.com/OpenMage/magento-lts/pull/1209/files#diff-9e3923f9fdb3de5d1849d1eca7869eb6R10

@seansan
Copy link
Contributor Author

seansan commented Sep 29, 2020 via email

@colinmollenhour
Copy link
Member

Thanks @seansan I'll check that out and consider adding htpasswd to the default.

@ollyboy
Copy link

ollyboy commented Oct 27, 2020

Looked at the index.php again as requested, can't really send as it's too client-specific and Cloudflare specific, you can switch between 15+ store views on this site, but we try to send new users to the most logical store. Most of the protection is done in Nginx, especially the include white CDN white list and then deny all other. The protection in index.php is about the final check of all _SERVER variables and avoiding any rubbish calls to Mage::run

What it does:

  • looks for a cookie we set using Cloudflare

     if (isset ($_SERVER["HTTP_CF_IPCOUNTRY"])) 
      $country_code = $_SERVER["HTTP_CF_IPCOUNTRY"];
    
  • sets cookie based on this or some other URL rules ( like a specific store path call )

  • exits if we cant work out rule or there is blanks / junk / !isset for $_SERVER['REQUEST_URI'] or $_SERVER['HTTP_HOST'] etc

  • finally does very strict and specific store call stuff ( including some admin rules ) like:

if ($country_code == "US") {
       if ( !$store_call ) {
		$_SERVER['REQUEST_URI'] = '/us' . $_SERVER['REQUEST_URI'];
	}
        Mage::run('us','store');
}
elseif (in_array($country_code,$auArray)){
	if ( !$store_call ) {
		$_SERVER['REQUEST_URI'] = '/au' . $_SERVER['REQUEST_URI'];
	} 	
	Mage::run('au','store');
}

... etc ...

@ollyboy
Copy link

ollyboy commented Oct 28, 2020

PCI and the future

LTS is a great product, super stable and there are 10’s of thousands of businesses that don’t have the funds, time or focus to convert to magento2 in these troubled times. My view is 2021/22 is going to be very tough for small/medium businesses. My idea is to put together a project and offer that will “destroy” the arguments that magento1 is dead or non PCI compliant.

Rough ideas for discussion to agree an “LTS 2030” plan

  1. Once the plan is agreed upon, set up a Patreon account to fund the following work/services where needed
  2. Find a list of the 20,000 store owners and send this plan/EDM to join the Patreon, get some marketing help
  3. Eliminate any existing payment modules/code that store cards, punched out card numbers ie "Visa xxx-xxx-1234” probably ok - call out to LTS community for patches
  4. Put some new code in place that allows ONLY secure payment pop-ups/frame modules for credit cards - maybe?
  5. Keep up to date with php security patches and versions ( already doing that )
  6. Ask agencies like IWD, who have pulled down from sale magneto 1 one-page-checkout, can LTS have and maintain the one-page check-out code - maybe offer some of the Patreon funding as a retainer
  7. Write or adapt your own one-page check-out
  8. Have store owners lobby module vendors for source
  9. Keep a list of compliant checkout modules
  10. Provide an Nginx PCI compliant config - apache??
  11. Host/deploy each release in a PCI compliant test environment and do regular ASV scans, do the automated deploy and test data sets plus pay for the regular AVS scans
  12. Maybe even do a yearly penetration test
  13. Look into partnering/funding one of the Magento security scan services to keep their services switched on and pointed at the test environment
  14. Composer Deploy “approved” modules to test environment/AVS environment
  15. Showcase the test environment, nothing like letting hackers try to get in to prove our defenses. offer challenges or small bounties
  16. Test/showcase environment would need simple and multi-store versions plus a tidy theme
  17. Allow clients to spin up a test environment ( limited time ) with admin access given a Patreon donation key
  18. Find a list of agencies/consultants who will reverse port EE to LTS for a reasonable fee for those stuck on EE magento 1

Some background info….

PCI DSS ( Data Security Standard ) considers merchants to be:

Level 1

    * more than 6 million Visa, Mastercard, or Discover transactions annually via any channel
    * more than 2.5 million American Express transactions annually
    * more than 1 million JCB transactions annually
    * have suffered a data breach or cyberattack that resulted in cardholder data being compromised
    * have been identified by another card issuer as Level 1

Validation Requirements:
* Annual Report on Compliance (ROC) by a Qualified Security Assessor (QSA) - will cost $10-50K US - penetration testing and venerability testing required
* Quarterly network scan by Approved Scan Vendor (ASV) - $100-$1000 dollars
* Attestation of Compliance Form

Level 2

    * between 1 million and 6 million Visa, Mastercard, or Discover transactions per year via any channel
    * between 50,000 to 2.5 million American Express transactions annually
    * less than 1 million JCB transactions annually

Validation Requirements:
* Annual Self-Assessment Questionnaire (SAQ) - no penetration testing required??
* Quarterly network scan by Approved Scan Vendor (ASV)
* Compliance Form

Level 3

    * between 20,000 and 1 million Visa e-commerce transactions annually
    * processing 20,000 Mastercard e-commerce transactions annually, but less than or equal to 1 million total Mastercard transactions annually
    * process 20,000 to 1 million Discover card-not-present only transactions annually
    * Less than 50,000 American Express transactions

Validation Requirements:
* SAQ
* Quarterly network scan by ASV
* Compliance Form

Level 4

    * Merchants processing less than 20,000 Visa or Mastercard e-commerce transactions annually
    * All other merchants processing up to 1 million Visa or Mastercard transactions annually

Validation Requirements:
* These largely depend on the requirements of the merchant’s acquiring bank
* Typically include an SAQ and Quarterly Network Scan by ASV

What does PayPal say:

Requirement 6 of the PCI DSS requires merchants to "develop and maintain secure systems and applications by installing applicable vendor-supplied security patches." Without future security patches, Magento 1 merchants will no longer be able to meet this requirement, which could result in costly and time-consuming remediation.

This is not a PayPal-specific requirement. PCI DSS requirements apply to your integrations with card payment brands, such as Visa, MasterCard, American Express, Discover, JCB, and any other payment processor on the Magento 1 platform. Visa has stressed that urgent action is required for merchants to migrate from Magento 1 and advised merchants to be aware of their responsibilities in securing their environment to help prevent the loss of payment card data.

@OpenMage OpenMage locked and limited conversation to collaborators May 26, 2022
@ADDISON74 ADDISON74 converted this issue into discussion #2160 May 26, 2022
@fballiano fballiano unpinned this issue May 31, 2022

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
None yet
Projects
None yet
Development

No branches or pull requests