Préambule La sécurité PHP pour les tout petits
←
→
Transcription du contenu de la page
Si votre navigateur ne rend pas la page correctement, lisez s'il vous plaît le contenu de la page ci-dessous
La sécurité PHP pour les tout petits 1. Préambule Ce document est un livre de recettes pour protéger une architecture LAMP (Linux, Apache, MySQL, PHP), voire WAMP (Windows, Apache, MySQL, PHP) mais elle peut aussi aider, pour peu qu'on l'adapte correctement, à protéger toute architecture Web. La démarche ne garantit absolument pas l'inviolabilité, le 100% ne faisant pas partie de ce monde, mais va rendre la tâche du pirate plus compliquée, plus coûteuse et plus longue. 1.1. Pré-requis Nous supposerons que PHP (5.6 minimum) est installé en tant que module dans Apache. Le cas de PHP utilisé comme CGI ne devrait recouvrir que le cas des particuliers chez leur FAI favori. La version PHP-PFM est plus performante, mais plus complexe, elle n’est pas traitée ici. Nous supposerons aussi que le lecteur est au moins habitué à la programmation, même sans être un expert. Nous supposerons enfin que les développeurs web ne sont pas hostiles, tout au plus inconscients. 1.2. But des scripts Les scripts, PHP par exemple, ont pour but d’interagir avec l’utilisateur, ce qui signifie que celui-ci doit envoyer, d’une manière ou d’une autre, des données qui seront utilisées par le programme. Ces données seront, après transformation, stockées par le programme, soit dans des fichiers, soit dans des bases de données, MySQL par exemple, pour être ensuite renvoyées d'une manière ou d'une autre à un utilisateur. La difficulté va donc porter sur la protection de l'ensemble de cette chaîne en évitant ou limitant les attaques informatiques. Version du jeudi 29 mars 2018 Fabrice Prigent 1/17
2. Linux Plusieurs choses peuvent être faites au niveau du Linux pour renforcer la sécurité. Parmi les plus simples, mettre en place le pare-feu pour interdire les connexions extérieures sur autre chose que les ports spécifiquement ouverts: iptables –A INPUT –m state –-state ESTABLISHED –j ACCEPT iptables –A INPUT –i lo –j ACCEPT iptables –A INPUT –p tcp –-dport 80 –j ACCEPT iptables –A INPUT –p tcp –-dport 443 –j ACCEPT iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT iptables –A INPUT –j LOG iptables –A INPUT –j DROP Reste ensuite à interdire aux processus apache et MySQL à sortir de la machine : qu'auraient-ils donc à faire à l'extérieur (sauf cas du mod_proxy) ? iptables –A OUTPUT –o lo –j ACCEPT iptables –A OUTPUT –m state –-state ESTABLISHED –j ACCEPT iptables –A OUTPUT –d mysql.site.fr –p tcp –-dport 3306 –j ACCEPT # Serveur MySQL distant iptables –A OUTPUT –d smtp.site.fr –p tcp –-dport 25 –j ACCEPT # Serveur SMTP du site iptables –A OUTPUT –d dns.site.fr –p udp –-dport 53 –j ACCEPT # Serveur DNS du site iptables -A OUTPUT -m owner --uid-owner 48 –j LOG # Utilisateur Apache iptables -A OUTPUT -m owner --uid-owner 27 –j LOG # Utilisateur MySQL iptables -A OUTPUT -m owner --uid-owner 48 –j DROP # Utilisateur Apache iptables -A OUTPUT -m owner --uid-owner 27 –j DROP # Utilisateur MySQL iptables –A OUTPUT –j ACCEPT Dans les solutions plus ardues, on trouvera tous les processus de durcissement d'OS, au rang desquels on trouvera SELinux, GrSecurity, Pax, Apparmor. Dans les solutions générales et assez longues, on se reportera sur les excellents guides de la N.S.A. qui parlent, entre autres, de Linux, Cisco, Windows, etc. 2.1. SeLinux De manière générale : ne pas le désactiver (hormis cas spécifiques) , par contre, il sera utile d’ouvrir certaines capacités : getsebool -a # Pour la liste des autorisations "simples" activées ou pas setsebool httpd_can_network_connect_db on # Pour autoriser, par exemple, les connexions aux SGBDs 3. Apache 3.1. Le logiciel lui-même Plusieurs méthodes existent pour sécuriser apache. La première idée est de le mettre en chroot. Cela impose de nombreuses contraintes, difficiles à respecter en production. Heureusement, la NSA pense à nous grâce à son SELinux qui permet de contraindre un processus, même root, dans un environnement sécurisé. 3.2. Le paramétrage Il est important de vérifier quelques paramètres, et de limiter leur action suivant vos utilisateurs (les options allowOverride sont là pour cela). Attention : cela empêche l'utilisation des .htaccess. A vous de décider ce que vous placer. AllowOverride None Désactivez la signature de l'apache: ServerSignature Off ServerTokens Prod Désactiver le mode trace TraceEnable off Pensez aussi à journaliser correctement : Version du jeudi 29 mars 2018 Fabrice Prigent 2/17
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined CustomLog logs/access_log combined Évitez les log DNS qui sont une mauvaise idée, car ils prennent du temps pour une information inutile, voire dangereuse : l'information DNS peut changer entre le problème et sa détection. Idem pour les IdentityCheck, hormis cas particulier. HostnameLookups Off IdentityCheck off Mettez en place le mécanisme des .htaccess et empêchez leur rapatriement. AccessFileName .htaccess Order allow,deny Deny from all On peut aussi limiter les requêtes en taille et réduire le timeout. LimitRequestBody 1048576 LimitXMLRequestBody 10485760 Timeout 45 Bloquer par défaut les outils qui donneraient trop d’informations (même s’ils ne sont pas présents) ######################################################### # # Restrictions sur des outils d'administration # ExtendedStatus On SetHandler server-status Require ip 127.0.0.1 192.168.1 10.26 allow from univ-tlse1.fr allow from ut-capitole.fr deny from all allow from univ-tlse1.fr allow from ut-capitole.fr deny from all Tuez dans l’œuf toute tentative de XSS distant (https://content-security-policy.com/) Header set Content-Security-Policy "default-src 'self' ;" Header set Content-Security-Policy "script-src 'self' www.google-analytics.com ajax.googleapis.com;" Refusez les indexations automatiques de répertoires, tous vos fichiers seraient visibles (hormis cas précis). Cela peut-être fait en créant systématiquement un index.html, mais aussi au niveau du serveur ou des répertoires grâce à la directive. Options -Indexes 3.3. Les VirtualHosts Pensez à faire des journaux individuels pour chaque virtualhost, ainsi qu'un fichier de configuration par virtualhost (le répertoire conf.d est fait pour cela). Choisissez bien leur nom, surtout si vous souhaitez utiliser les certificats qui vont se baser sur ce nom. Créer un serveur par défaut, sans la moindre information. 3.4. L'arborescence 3.4.1.Fichiers à rendre inaccessibles Prévoyez une routine d'effacement de vos fichiers temporaires ou de sauvegarde (*.bak, *.old, *.$$$, *.tmp, ficher~ ou *.2), éventuellement, vous pouvez aussi les rendre illisibles par le daemon apache. chown root:root *.bak Version du jeudi 29 mars 2018 Fabrice Prigent 3/17
chmod 400 *.bak 3.4.2.Répertoires et fichiers Il faut aussi faire sortir de l'arborescence, tant que faire se peut, les fichiers qui n'ont pas vocation à être directement visibles. En particulier tout ce qui concerne les include et les fichiers de données. 3.4.3.Le fichier robots.txt Le fichier /robots.txt doit contenir l'ensemble des répertoires à ne pas laisser explorer par des robots tels GOOGLEBOT ou autre. Cela pourrait effectivement donner beaucoup trop d'informations aux pirates, en particulier doivent être exclus : les répertoires de statistiques, les répertoires privés, plus tout répertoire "piégé" que vous pourriez créer. Exemple d'un tel fichier : User-agent: * # Répertoires standard Disallow: /cgi-bin # Quelques fichiers Disallow: admin.php Disallow: test.php # Statistiques Disallow: /usage/ Disallow: /mrtg/ Disallow: /webalizer/ Disallow: /private/mrtg/ # Répertoires prives Disallow: /install_redhat/ Disallow: /noaccess/ Rappel : une règle est faite pour être contournée, et les pirates ne s'en priveront pas, le robots.txt n'ayant qu'une vocation d'indication. Ce fichier peut être validé grâce à cet outil http://www.searchengineworld.com/cgi-bin/robotcheck.cgi. 3.4.4.Les journaux sont vos amis Il est important de suivre la vie de vos journaux, et de collecter le plus d'information possible sur votre serveur. Journaliser, c'est bien, mais il est aussi souhaitable de repérer les anomalies. On regardera donc avec attention (ou plutôt, on fera regarder par un programme) les tentatives étranges, répétitives. Un bon analyseur de journaux peut souvent (s'il n'est pas lui-même piratable) donner un bon aperçu de choses "étonnantes". On regardera du côté de webstats, awstats ou autres. 3.4.5.Le 6G Firewall Un développeur web (Jeff Starr)a conçu un .htaccess, à placer à la racine web du site, qui permet de limiter grandement le potentiel des attaquants, en utilisant le mod_rewrite. On le trouve sur https://perishablepress.com/6g/. Les règles ne sont pas toutes adaptées à tous les contextes. Il vous faudra vérifier leur adéquation. # 6G FIREWALL/BLACKLIST # @ https://perishablepress.com/6g/ # 6G:[QUERY STRINGS] RewriteEngine On RewriteCond %{QUERY_STRING} (eval\() [NC,OR] RewriteCond %{QUERY_STRING} (127\.0\.0\.1) [NC,OR] RewriteCond %{QUERY_STRING} ([a-z0-9]{2000}) [NC,OR] RewriteCond %{QUERY_STRING} (javascript:)(.*)(;) [NC,OR] RewriteCond %{QUERY_STRING} (base64_encode)(.*)(\() [NC,OR] RewriteCond %{QUERY_STRING} (GLOBALS|REQUEST)(=|\[|%) [NC,OR] RewriteCond %{QUERY_STRING} (|%3) [NC,OR] RewriteCond %{QUERY_STRING} (\\|\.\.\.|\.\./|~|`||\|) [NC,OR] RewriteCond %{QUERY_STRING} (boot\.ini|etc/passwd|self/environ) [NC,OR] RewriteCond %{QUERY_STRING} (thumbs?(_editor|open)?|tim(thumb)?)\.php [NC,OR] RewriteCond %{QUERY_STRING} (\'|\")(.*)(drop|insert|md5|select|union) [NC] RewriteRule .* - [F] Version du jeudi 29 mars 2018 Fabrice Prigent 4/17
# 6G:[REQUEST METHOD] RewriteCond %{REQUEST_METHOD} ^(connect|debug|delete|move|put|trace|track) [NC] RewriteRule .* - [F] # 6G:[REFERRERS] RewriteCond %{HTTP_REFERER} ([a-z0-9]{2000}) [NC,OR] RewriteCond %{HTTP_REFERER} (semalt.com|todaperfeita) [NC] RewriteRule .* - [F] # 6G:[REQUEST STRINGS] RedirectMatch 403 (?i)([a-z0-9]{2000}) RedirectMatch 403 (?i)(https?|ftp|php):/ RedirectMatch 403 (?i)(base64_encode)(.*)(\() RedirectMatch 403 (?i)(=\\\'|=\\%27|/\\\'/?)\. RedirectMatch 403 (?i)/(\$(\&)?|\*|\"|\.|,|&|&?)/?$ RedirectMatch 403 (?i)(\{0\}|\(/\(|\.\.\.|\+\+\+|\\\"\\\") RedirectMatch 403 (?i)(~|`||:|;|,|%|\\|\s|\{|\}|\[|\]|\|) RedirectMatch 403 (?i)/(=|\$&|_mm|cgi-|etc/passwd|muieblack) RedirectMatch 403 (?i)(&pws=0|_vti_|\(null\)|\{\$itemURL\}|echo(.*)kae|etc/passwd| eval\(|self/environ) RedirectMatch 403 (?i)\.(aspx?|bash|bak?|cfg|cgi|dll|exe|git|hg|ini|jsp|log|mdb|out| sql|svn|swp|tar|rar|rdf)$ RedirectMatch 403 (?i)/(^$|(wp-)?config|mobiquo|phpinfo|shell|sqlpatch|thumb| thumb_editor|thumbopen|timthumb|webshell)\.php # 6G:[USER AGENTS] SetEnvIfNoCase User-Agent ([a-z0-9]{2000}) bad_bot SetEnvIfNoCase User-Agent (archive.org|binlar|casper|checkpriv|choppy|clshttp| cmsworld|diavol|dotbot|extract|feedfinder|flicky|g00g1e|harvest|heritrix|httrack|kmccrew| loader|miner|nikto|nutch|planetwork|postrank|purebot|pycurl|python|seekerspider|siclab| skygrid|sqlmap|sucker|turnit|vikspider|winhttp|xxxyy|youda|zmeu|zune) bad_bot Order Allow,Deny Allow from All Deny from env=bad_bot # 6G:[BAD IPS] Order Allow,Deny Allow from All # uncomment/edit/repeat next line to block IPs # Deny from 123.456.789 3.5. Les modules de sécurité Afin de renforcer la sécurité d'apache, plusieurs modules ont été développés. On trouvera aussi des routines intéressantes en PHP. 3.5.1.Mod_chroot Mod_chroot qui se trouve sur http://core.segfault.pl/~hobbit/mod_chroot/permet de mettre Apache dans un chroot, sans avoir à copier les innombrables fichiers nécessaires à son fonctionnement. Rapide et efficace, il n'atteint pas, malgré tout, la précision d'une installation manuelle. De plus, il sera nécessaire de garder à l'esprit que tous les programmes lancés par apache seront cantonnés au répertoire chrooté. SecChrootDir /chroot/apache Version du jeudi 29 mars 2018 Fabrice Prigent 5/17
3.5.2.Mod_security Mod_security se trouve sur http://www.modsecurity.org. Il permet de normaliser les urls avant l'envoi aux divers modules, et surtout de repérer les chaînes de caractères, ou plutôt les expressions régulières dangereuses dans les variables envoyées, ainsi que repérer, dans les pages de retour, les informations qui sont signes d'une attaque réussie. Il est de plus capable de chrooter apache de la même manière que mod_chroot. Son emploi est plus adapté à un Apache 2.x. Mais sa réputation est telle que désormais il est intégré dans quasiment toutes les distributions. Les signatures sont, quant à elles, gérées par l'OWASP, qui les met à jour régulièrement. Comodo en propose aussi gratuitement (https://modsecurity.comodo.com/ ) Pour une « redhat » yum install mod_security mod_security_crs ou sur une ubuntu/debian apt-get install libapache2-modsecurity cd /etc/modsecurity/ cp modsecurity.conf-recommended modsecurity.conf service apache2 restart Puis configuration pour une instance donnée, ou au global. Avec les règles fournies, le plus gros travail consiste à empêcher certaines règles de fonctionner. # Pour retirer une règle spécifiquement pour un répertoire SecRuleRemoveById 950009 # Pour retirer globalement une règle SecRuleRemoveById 960010 # Pour retirer une règle qui déclenche en phase 1 SecRule REQUEST_URI "^/hr-rich-client/hrservlet/GetHRPage" "phase:1,id:10001,nolog,ctl:ruleRemoveById=960010" # Pour retirer une règle pour une variable précise. SecRule ARGS "logoutRequest" "phase:2,id:100020,nolog,ctl:ruleRemoveById=973332 SecRule ARGS "javax.faces.ViewState" "phase:2,id:10021,nolog,ctl:ruleRemoveById=950006 On pourra trouver sur le site http://www.modsecurity.org/projects/rules/index.html des règles de filtrage, qui seront, bien évidemment, à adapter : on ne sécurise pas une application simple comme le logiciel phpmyadmin. Pour plus de précision sur les « phase 1» et « phase 2» http://blog.modsecurity.org/2007/12/using-transacti.html 3.5.3.Mod_evasive Ce module permet de mettre en liste noire des machines qui tentent de saturer un serveur web, par des requêtes trop rapides. Voici un exemple de configuration pour apache 2.x. A mettre dans le httpd.conf. Une option supplémentaire permet aussi de lancer une commande système. DOSHashTableSize 3097 DOSPageCount 2 DOSSiteCount 50 DOSPageInterval 1 DOSSiteInterval 1 DOSBlockingPeriod 10 DOSEmailNotify webmaster@site.fr DOSLogDir "/var/lock/mod_evasive" DOSWhitelist 127.0.0.1 DOSWhitelist 193.49.*.* Version du jeudi 29 mars 2018 Fabrice Prigent 6/17
3.6. La sécurisation par chiffrement Saint Graal de la sécurité il y a quelques années, on s'aperçoit que le chiffrement n'est pas plus sûr que les entrées du "tunnel" qu'il crée. Il s'agit toutefois d'un pré requis pour permettre de faire transiter des données sans risquer l'observation et l'interception des communications. 3.6.1.L'installation du chiffrement En apache, le chiffrement se fait de manière relativement simple : l'écoute se fera sur le port dédié 443. Le reste dépendra des certificats obtenus, plus quelques options si l'on souhaite récupérer des variables dans les certificats (le nom de l'utilisateur en particulier). Nous ne nous attarderons pas ici sur la génération des certificats, qui relèverait d'un chapitre complet. ServerAdmin reseau@site.fr DocumentRoot /var/www/html_webmail/ ServerName webmail.site.fr SSLEngine on SSLCertificateFile /etc/pki/tls/certs/webmail.site.fr.crt SSLCertificateKeyFile /etc/pki/tls/private/webmail.site.fr.key SSLCertificateChainFile /etc/pki/tls/certs/cachain.txt SSLOptions +StdEnvVars ErrorDocument 402 https://webmail.site.fr/ SSLOptions +StdEnvVars ErrorLog logs/error_webmail_log CustomLog logs/access_webmail_log combined 3.6.2.Le choix des algorithmes Les algorithmes de chiffrement se négocient au début de la session entre serveur et client. Mais de nombreux critères vont présider au choix de la « CIPHERSUITE » • les protocoles cassés, • les incompatibles avec des navigateurs parce que trop vieux ou trop récents, • les insuffisants, • ceux spécialisés en chiffrement symétriques ou asymétriques, • ceux spécialisés en signature (hashage), • les performants mais pas efficaces, etc. Il est conseillé par certains experts (cf https://wiki.mozilla.org/Security/Server_Side_TLS) de sélectionner la ciphersuite suivante (en mode « intermédiaire » pour des clients pas trop vieux). SSLProtocol all -SSLv2 -SSLv3 SSLCipherSuite ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE- ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA- AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128- SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA- AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA- AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES- CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM- SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS SSLHonorCipherOrder on # OCSP Stapling, only in httpd 2.3.3 and later SSLUseStapling on SSLStaplingResponderTimeout 5 SSLStaplingReturnResponderErrors off SSLStaplingCache shmcb:/var/run/ocsp(128000) Version du jeudi 29 mars 2018 Fabrice Prigent 7/17
La version pour les navigateurs modernes est celle-ci : SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1 SSLCipherSuite ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE- ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA- AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128- SHA256:ECDHE-RSA-AES128-SHA256 SSLHonorCipherOrder on SSLCompression off SSLSessionTickets off # OCSP Stapling, only in httpd 2.3.3 and later SSLUseStapling on SSLStaplingResponderTimeout 5 SSLStaplingReturnResponderErrors off SSLStaplingCache shmcb:/var/run/ocsp(128000) On pourra avec profit, s'intéresser aux outils de tests de validité de https://www.ssllabs.com (compter 10 minutes complètes de test). 3.6.3.Le HSTS et le certificate pinning Certains pirates peuvent tenter 2 actions techniques pour contourner le chiffrement : faire « redescendre » la communication en HTTP, et éventuellement (plus complexe) obtenir un vrai-faux certificat (auprès d’une autorité complaisante ou piratable, ou bien en intégrant une fausse AC sur un poste). On se prémunit de ceci en imposant le HTTPS grâce au HSTS et en ajoutant une durée de validité au certificat (attention, il faut que le navigateur accepte ces 2 ordres). LoadModule headers_module modules/mod_headers.so #la valeur de max-age correspond ici a 180 jours Header always set Strict-Transport-Security "max-age=15552001; includeSubdomains;" 3.6.4.Les contournements du chiffrement Ils sont nombreux, malgré les apparences, et sont généralement liés à l'utilisateur qui fait trop confiance, ou trop peu attention à ce qu'il voit. 3.6.4.1.Problème du domaine "valide" à l'orthographe similaire Un pirate peut acheter un certificat auprès d'une société (Verisign par exemple) pour le domaine bnppariba.com. L'utilisateur recevra un certificat, valide, qu'il acceptera. On pourra aussi regarder du côté des codes utf8 qui permettent l’utilisation d’une graphie identique, mais d’un sens différent. 3.6.4.2.L'attaque Man in the Middle Un pirate peut, en se plaçant de manière réelle ou virtuelle entre la victime et le site sécurisé, intercepter les communications. Il va présenter un pseudo certificat à l'utilisateur, qui va l'accepter. Le pirate va retransmettre les données de la session au vrai site en les chiffrant réellement, mais tout sera visible pour lui. De nombreux outils permettent de faire cela de manière très simple. 3.7. Les processus d'authentification Il faut regarder avec attention les processus d'authentification, qui ne doivent en aucun cas être une source supplémentaire d'intrusion. Il est par exemple très dangereux de faire circuler les mots de passe en clair sur le réseau, ce qui implique le chiffrement des échanges d'authentification. Mais cela ne suffit pas : si la personne croit parler au serveur d'authentification, alors qu'il s'agit d'un pirate (notion de « rogue » tel que les rogue AP en WiFi), il faut absolument que la personne le sache, et qu'elle n'ait pas pris l'habitude d'accepter n'importe quel certificat ! Ce qui signifie utiliser une VRAIE notion de certificat et donc d'IGC (Infrastructure de Gestion de Clés), aussi appelée PKI (Public Key Infrastructure). Version du jeudi 29 mars 2018 Fabrice Prigent 8/17
3.7.1.Le cookie d'authentification Après une authentification, plus ou moins sérieuse, l'idée est souvent de stocker l'authentification dans un cookie et de l'utiliser dans des applications non chiffrées. Le but pour le pirate consiste alors à récupérer le cookie d'authentification pour le rejouer immédiatement. C'est une action tout à faire faisable grâce aux attaques dites XSS (Cross Site Scripting). A minima, il faudra, pour limiter les conséquences de ce vol, stocker dans le cookie, ou dans les variables de sessions dont il dépend, l'IP d'origine de la session, puis de refuser les connexions qui viennent d'une autre IP. Cela se passera certainement moins bien si la victime et l'attaquant ont la même adresse IP extérieure (NAT, proxy-cache, etc.), ou si l'IP est "changeante" (voir le protocole DHCP). 3.7.2.Le SSO Acronyme de Single Sign On, il décrit un mécanisme d'authentification unique permettant à l'utilisateur de ne rentrer qu'une seule fois son mot de passe pour plusieurs applications (souvent web). Cela recouvre plusieurs techniques basées quasiment toutes sur un serveur d'authentification. Dans un cas le serveur connaît tous les mots de passe et les fournit à l'application qui le demande. Ceci est particulièrement dangereux : l'application va être au courant du login et du mot de passe. Si elle est pirate ou piratée, ceux-ci vont être diffusés. Dans le second, le serveur fournit l'identité du client et confirme son authentification à l'application demandeuse (voir par exemple le projet CAS). L'application ne connaît donc pas le mot de passe. 3.7.3.Les fédérations d'identité Les fédérations d'identité ont pour but de permettre à un individu, faisant partie d'un organisme A d'être reconnu par un autre organisme B pour accéder à certains de ses services. B (appelé fournisseur de services), lorsque l'individu le contacte, va demander à A de confirmer son identité, par exemple grâce à un SSO. B ne connaîtra ni le login, ni le mot de passe de l'individu, mais, parce qu'il fait confiance à A, lui accordera des droits. Deux projets sont en lice : http://shibboleth.internet2.edu (shibboleth) http://www.projectliberty.org (liberty alliance) 3.7.4.Les authentifications multi-facteurs Il est parfois utile d’utiliser l’authentification multi-facteurs, celle-ci se base en plus de ce que l’on sait (un mot de passe) un élément que l’on est (biométrique), ou que l’on a (un token). Cela peut être fait par SMS, mais les banques ne sont plus si sures que cela marche (roaming étranger pour intercepter les SMS). Ce qui est relativement utilisable : googleauthenticator, freeotp (https://freeotp.github.io/) pour utiliser les smartphones, la partie serveur est disponible ici : https://github.com/archiecobbs/mod-authn-otp. 3.8. Les reverse-proxy 3.8.1.Le mod_proxy Le mod_proxy va permettre de placer un apache en frontal d'un autre serveur HTTP, de type apache ou non. Cela va décharger le serveur protégé d'un certain nombre d'actions de sécurité (voir le module mod_security), et éventuellement du chiffrement des communications. Le serveur frontal n'ayant qu'un rôle de relais, on va pouvoir lui faire subir une cure d'amaigrissement, toujours profitable, et lui restreindre ses possibilités de communications. Il deviendra alors moins vulnérable, et moins capable d'actions néfastes. Afin de pouvoir faire du reverse-proxying en toute circonstance, on regardera du coté de mod_ext_filter pour faire une réécriture des urls, même dans les javascript. La configuration d'un reverse-proxy pouvant être assez longue, nous laisserons le lecteur faire ses propres recherches. Version du jeudi 29 mars 2018 Fabrice Prigent 9/17
3.8.2.Un reverse-proxy de sécurité : Vulture Il existe un reverse-proxy de sécurité complet qui existe en GPL : il s'agit de Vulture, disponible sur http://vulture.open-source.fr. Il combine les fonctionnalités de l'ensemble des outils précédents, puisqu'il les intègre. Il est aussi capable de faire du SSO. 3.8.3.Un reverse-proxy SSO : LemonLDAP Lemonldap est un reverse-proxy qui permet de créer un serveur apte à gérer une authentification unique pour un ensemble d'applications web. Il est disponible sur http://LemonLDAP.sourceforge.net. 3.8.4.NGINX Serveur web d'origine russe il est réputé pour sa faible empreinte mémoire et CPU, ainsi que pour sa rapidité. On l'utilise soit en remplacement, soit en reverse-proxy de Apache. http://www.nginx.org. Sa faible modularité et sa rigidité en font un outil dur, mais terriblement efficace. Certains le coupleront, à profit, avec son module NAXSI qui fait du filtrage positif (on n'autorise que ce que l'on connaît) contrairement à mod_security. 4. Paramétrer PHP 4.1. Les autres paramétrages D’autres variables permettent de limiter encore les droits des scripts PHP. 4.1.1.open_basedir Permet de limiter l’ouverture des fichiers à un répertoire donné. Attention à bien le terminer par /. open_basedir="/var/www/html/:/var/www/html_toto:/usr/local/php" 4.1.2.allow_url_fopen et allow_url_include Permet d’empêcher de chercher des urls à distance allow_url_fopen=Off allow_url_include=Off 4.1.3.disable_functions On peut désactiver des fonctions. Est-il indispensable de laisser les commandes system et exec actives ? enable_dl = On disable_functions = system, exec, shell_exec, passthru, phpinfo, show_source, popen, proc_open disable_functions = fopen_with_path, dbmopen, dbase_open, putenv, move_uploaded_file disable_functions = chdir, mkdir, rmdir, chmod, rename disable_functions = filepro, filepro_rowcount, filepro_retrieve, posix_mkfifodisable_functions 4.1.4.disable_classes Idem, mais avec les classes. 4.1.5.Limiter les informations pour le pirate On empêche l'affichage d'informations trop précises sur la version de php, les erreurs qui ne doivent pas s'afficher dans le navigateur mais dans les logs. expose_php = off display_errors = off # Si vous voulez mettre les messages dans le syslog. # error_log syslog Version du jeudi 29 mars 2018 Fabrice Prigent 10/17
error_log /var/log/httpd/error_php.log report_memleaks = On track_errors = Off html_errors = Off 4.1.6.Protéger les sessions session.auto_start = Off session.save_path = /path/PHP-session/ session.name = myPHPSESSID session.hash_function = 1 session.hash_bits_per_character = 6 session.use_trans_sid = 0 session.cookie_domain = full.qualified.domain.name session.cookie_lifetime = 0 session.cookie_secure = On session.cookie_httponly = 1 session.use_only_cookies= 1 session.cache_expire = 30 default_socket_timeout = 60 4.1.7.Paramètres de limitation divers Afin de limiter les dégâts d'un déni de service (D.O.S.) on peut mettre des contraintes sur les ressources. En voici quelques unes : max_execution_time = 30 ; Temps maximum d'éxecution en secondes memory_limit = 8M ; taille mémoire maximale par script 4.1.8.Les modules de protection Quelques modules d’aide au filtrage des variables ont été développés pour PHP, on pourra se reporter en particulier à http://www.phpclasses.org/browse/class/78.html qui les recense. En voici un. 4.1.8.1.HTMLPURIFIER Il s'agit d'une classe php qui va vérifier la conformité de la page et la nettoyer de tout XSS qui pourrait être présent avant son affichage. Elle est disponible sur http://htmlpurifier.org/ et son utilisation est relativement simple. 4.1.8.2.Suhosin Ceci est un module pour PHP, qui va en combler les trous les plus flagrants. Il se compose de deux parties indépendantes : un patch et une extension. On peut le trouver sur http://www.hardened- php.net/suhosin.127.html. Le patch va nécessiter de recompiler php (et tous ses modules). L'extension, quant à elle, peut s'utiliser seule sans modification. Mais elle ne donne sa pleine puissance qu'avec le patch. Il est livré avec les debian, mais n’est plus activement suivi depuis PHP5.. 4.1.8.3.Snuffleupagus C’est un module développé par la société NBS System, déjà auteur de naxsi. C’est le fils spirituel de suhosin : il permet de protéger PHP 7 en bloquant des fonctions dangereuses, et en « patchant » virtuellement les applications web de manière globale. Il est disponible sur https://github.com/nbs-system/snuffleupagus. 4.1.9.Les frameworks de développements Ils permettent, pour les plus sérieux, de gérer une part importante des processus de sécurité : ● gestion des tokens POST contre les CSRF ● protection contre certains XSS ● vérification des injections SQL (ou LDAP) Version du jeudi 29 mars 2018 Fabrice Prigent 11/17
● déplacement des répertoires hors du répertoire WWW On y trouvera ● cakePHP ● symfony-project 5. MySQL 5.1. Installer MySQL MySQL peut très facilement se placer en chroot, afin de limiter ses actions en cas d'intrusion : mkdir -p /chrooted/chrooted-mysqld cd /chrooted/chrooted-mysqld/ mkdir -p var/lib var/run lib etc tmp grep "mysql:" /etc/passwd >etc/passwd chmod 1777 tmp cp -rp /var/lib/mysql var/lib/ cp -rp /var/run/mysqld var/run ldd /usr/libexec/mysqld #Créer les répertoires repérés #copier les fichiers repérés dans les répertoires mkdir -p lib/i686 usr/lib cp -rp /lib/….. cp -rp /lib/libnss* lib/ # ajouter dans /etc/my.cnf chroot=/chrooted/chrooted-mysqld 5.2. Configurer MySQL MySQL est le support de beaucoup d'applications web. Les questions qui se posent : Le MySQL doit-il être accessible hors de la machine qui l'héberge ? Non à 90%, donc on n'attache le processus qu'à l'interface localhost, ou, au pire, on utilise les iptables, ou tout autre procédé de pare-feu local pour limiter l'accès aux seules machines autorisées. Est-il normal que l'accès administrateur se fasse sans mot de passe ou avec un mot de passe par défaut ? Mettez en un nouveau. On peut utiliser pour cela la commande mysql_secure_installation, qui vous permettra d'enlever les bases et les comptes inutiles. Désactiver les fonctions trop dangereuses en ajoutant dans /etc/my.cnf set-variable=local-infile=0 5.3. Utiliser MySQL Lors de la création de compte MySQL, séparez les comptes suivants les applications. Profitez-en pour ne donnez que les droits nécessaires, et en les divisant éventuellement : ● un compte d'administration (création des tables), ● un compte en écriture ● un compte en lecture. 5.4. Piéger MySQL Créer des données "honeytokens" afin de faciliter le repérage de vol de données (pour un I.D.S. ou pour des preuves devant un tribunal). Cela peut-être un compte, un enregistrement, etc. 5.5. Travailler avec de vrais jeux de tests et pas de vraies données pydbgen permet de générer des jeux de test pour des infrastructures de préproduction et de test afin d’être compatible avec la RGPD/GDPR. Version du jeudi 29 mars 2018 Fabrice Prigent 12/17
6. Programmer en PHP Une fois que l’on a pris en compte l’environnement de travail, mis en place les protections nécessaires sur les scripts PHP et autres fichiers de configuration, un grand nombre de choses restent à regarder. 6.1. Bien programmer Cela semble évident, mais certains réflexes s’oublient en programmation web. Les principes doivent être les suivants : ● Une variable doit toujours être affectée. ● Vérifiez toujours le résultat des actions que vous effectuez La connexion à la base s'est-elle bien passée ? Y a-t-il un résultat dans votre requête ? La valeur est-elle définie ? 6.2. Contrôler les variables La grande majorité des failles actuelles proviennent des variables qui nous sont fournies par l’utilisateur. Le contenu des variables peut être dangereux. Voici quelques méthodes pour vérifier les variables. ● N’utilisez pas les variables directement. ● Faites attention ● Si une variable est sensée avoir un format particulier, vérifiez le grâce aux expressions régulières : if (preg_match('/^[A-Z0-9\._\-]{1,200}@[A-Z0-9\._-]{1,100}\.[A-Za-z]{2,6}$/', $email)) {suite_du_traitement();} else {die "erreur dans la variable email\n";}; // Remarquez que l'on n'utilise pas la valeur de $email. ● Dans le cas de variables énumératives, qui ne peuvent prendre leurs valeurs que dans un intervalle, on utilise soit les "elseif", soit un tableau intermédiaire : $valeur=$_GET['valeur']; if ($tab_valeurs_autorisees[$valeur]) {$valeur=$tab_valeurs_autorisees[$valeur]);} else {die "valeur est incorrecte"} ● Tente-t-on d'utiliser la variable pour envoyer du javascript ? Il faut enlever les caractères ""; Le mieux toutefois étant d’utiliser htmlentities (voir plus bas). ● Vous utilisez une variable qui contient nom de fichier, ou dont on dérive un nom de fichier, répertoire, ou commande ? Effacez les .. / ; * et autre ? Mieux n’utilisez QUE les caractères autorisés. ● Une base de données est impliquée ? Attention aux séquences dangereuses -- ou # (qui sont des commentaires) ; (qui est un séparateur de commande) l'espace (très utilisé si vous n'avez pas "quoté" la variable) les mots clés SQL (UNION étant le plus prisé). 6.3. Les pièges à éviter 6.3.1.Le stockage des mots de passe Doit-on stocker les mots de passe en clair ? A 99% c'est non, car on ne veut que comparer un mot de passe, pas le lire. Il faut utiliser les commandes de hashage telles que SHA2 ou SHA3 et ne stocker que le haché d'un mot de passe dans les bases de données. De plus, le hash doit inclure un partie « sel » pour éviter le phénomène des « rainbow tables ». Le sel doit être différent pour chaque utilisateur, sinon toute personne pouvant créer un compte va pouvoir deviner le sel, puis reconstituer une rainbow table : Une Radeon 7970 est capable de faire 400 millions de SHA256 à la seconde. La bonne solution en php est l’utilisation de la fonction password_hash et son pendant password_verify avec les paramétrage par défaut. Ces procédures vont stocker un mot de passe irréversible et quasiment impossible à comparer avec un dictionnaire dans un temps "raisonnable". Il sera alors impératif de Version du jeudi 29 mars 2018 Fabrice Prigent 13/17
penser que ce mot de passe "haché" devra être stocké dans un champ de 60 octets minimum, 256 étant le choix recommandé hash=password_hash("motdepassedelamortquitue", PASSWORD_DEFAULT); if (password_verify('motdepassedelamortquitue', $hash)) ; {echo "Vous êtes autorisé" ;} 6.3.2.L'emplacement des fichiers de données Les fichiers de données doivent-ils être dans un répertoire accessible par le web ? à 99% non, donc déplacez les (dans la limite des processus de confinement de votre serveur). 6.3.3.Le signalement des erreurs La tentation, si l’on fait des tests de variables est de signaler exactement le problème à l’utilisateur. Erreur ! ! ! Le programme doit stopper le traitement, proprement, mais sans réutiliser la variable. Un pirate pourrait utiliser cette protection pour entrer dans le système. Un comble ! 6.4. Contre les XSS : htmlentities() ou son synomyme htmlspecialchars() Pour se protéger des XSS, une idée simple est d'encoder les caractères particuliers en leur équivalent web. Cela empêche leur interprétation par les navigateurs. 6.5. Contre le SQL-Injection: mysql_real_escape_string() Afin d'éviter l'exécution de champs variables, il peut être utile "d'échapper", c'est à dire rendre inoffensifs, les caractères dangereux pour MySQL. C'est le rôle de cette commande, qui doit être préférée à sa petite sœur mysql_escape_string(). Pour être utilisée, une connexion à la base doit avoir été faite. Une version postgresql existe. $link=mysql_connect('mysql_host','mysql_user','mysql_password') or die(mysql_error()); $query=sprintf("SELECT * FROM users WHERE users='%s'",mysql_real_escape_string($user)); mysql_query($query); 6.6. Utilisation des prepare/execute Mieux encore, on peut aussi utiliser les commandes paramétrées plus résistantes encore, qui ont de plus l'avantage d'accélérer les traitements : require_once("DB.php"); $db = &DB::connect("mysql://user:pass@host/database1"); $p = $db->prepare("SELECT * FROM users WHERE username = ?"); $db->execute( $p, array($_GET['username']) ); et $db->query( "SELECT * FROM users WHERE username = ?", array($_GET['username']) ); ou encore, avec l'extension mysqli (php5). $db = new mysqli("localhost", "user", "pass", "database"); $stmt = $db -> prepare("SELECT priv FROM testUsers WHERE username=? AND password=?"); $stmt -> bind_param("ss", $user, $pass); $stmt -> execute(); 7. Les expressions régulières Avant toute chose : http://www.commitstrip.com/fr/page/10/ Les expressions régulières sont un formidable moyen de contrôle des données. Elles travaillent sur la notion de schéma suite de caractères, et non de chiffres ou de mots. Il existe, pour simplifier, deux types d'expressions régulières : les posix et les expressions perl. Celles- ci, plus complètes et puissantes sont de plus en plus utilisées. En php on vérifiera la présence d'une chaîne grâce à la fonction preg_match. Nous ne voyons ici qu'une toute petite partie des expressions régulières. Version du jeudi 29 mars 2018 Fabrice Prigent 14/17
7.1. Les caractères Un caractère représente généralement son propre caractère a représente la lettre a. [a-z] représente tout caractère entre a et z [B-E] représente tout caractère entre B et E [0-9] représente tout caractère entre 0 et 9 . (le point) représente n'importe quel caractère 7.2. Les opérateurs Les opérateurs permettent de préciser le nombre de fois où un caractère apparaît ou bien de faire des opérations de concaténation ou de choix AB représente la séquence des caractères A et B A* représente n'importe quelle chaine de caractères contenant des A (de 0 à autant que l'on veut) A+ représente n'importe quelle chaine ayant au moins un A A? représente le fait d'avoir zéro ou un A. A{3} signifie trois A de suite A{2,5} signifie que l'on peut avoir de deux à cinq A. A|B signifie que l'on peut avoir A ou B 7.3. Les autres éléments ^ représente le début de la chaîne de caractères $ représente la fin de la chaîne de caractères ( ) les parenthèses permettent d'appliquer un opérateur à un ensemble de caractères. \ permet de faire perdre à un opérateur ou à un caractère son "caractère" spécial. \. représente le caractère point. 7.4. Les commutateurs Certaines options dans la sélection permettent de préciser les traitements lors de la recherche i pour ne pas faire de distinction entre majuscule et minuscule o pour ne "compiler" la règle de détection qu'une fois (ce qui est plus rapide) 7.5. Quelques exemples If preg_match("/Fabrice/",$chaine) le prénom Fabrice est-il présent dans $chaine (fabrice ne sera par repéré car le f n'est pas majuscule) If preg_match("/h(ab)*/i",$chaine) Toute chaîne composée de h suivi de 0 à x fois de ab sera repérée. Les lettres pouvant être en minuscule ou en majuscule. If preg_match("/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/",$chaine) Toute chaîne de caractère contenant une adresse IP (ou du moins qui y ressemble). If preg_match("/[0-9]{1,4}((bis)|(ter))?/",$chaine) Toute chaîne qui contient un numéro de rue (avec éventuellement le bis ou le ter) If preg_match("/^[0-9]{5}$/",$chaine) La chaîne est égale à un code postal (ni plus ni moins). Version du jeudi 29 mars 2018 Fabrice Prigent 15/17
8. Les outils de tests 8.1. Les tests externes Ils se comportent comme les pirates en essayant d'obtenir des informations en étant sans droits particuliers. 8.1.1.Nmap Grand classique. Il permet de repérer les ports ouverts, la version du logiciel qui tourne (en se basant sur les bannières). Il est disponible sur http://insecure.org 8.1.2.Wapiti Scanneur de vulnérabilités inconnues, principalement pour XSS. Il est disponible sur http://wapiti.sourceforge.net/. 8.1.3.Autres outils NIKTO http://www.cirt.net/code/nikto.shtml (vulnérabilités web connues) Nessus http://www.nessus.org (vulnérabilités connues) Springenwerk http://www.springenwerk.org (failles XSS) skipfish http://www.googlecode.com/skipfish (scanner « exhaustif » et très simple d’utilisation). sqlmap http://sqlmap.sourceforge.net/ (SQL injection) 8.1.4.Les sites de surveillance et leurs abonnements Plusieurs services existent sur internet pour ausculter les sites. N'hésitez pas à vous abonner pour être alerté si votre domaine a été piraté : • https://www.pastbin.com • https://www.haveibeenpwned.com • https://www.zone-h.org 8.2. Les tests internes Ce sont les validateurs de code. Ils essaient de repérer dans les codes sources les fonctions et méthodes de programmation dangereuses. 8.2.1.PHPsecAudit Il est disponible sur http://developer.spikesource.com/projects/phpsecaudit 8.2.2.Pixy Auditeur de code PHP en java pour faille SQL et XSS http://pixybox.seclab.tuwien.ac.at/pixy/ 8.2.3.RATS http://www.securesoftware.com/resources/download_rats.html 9. URLographie Voici quelques URLs sur la bonne protection des scripts en PHP Version du jeudi 29 mars 2018 Fabrice Prigent 16/17
• http://www.owasp.org/ • https://www.owasp.org/index.php/PHP_Security_Cheat_Sheet • http://fr2.php.net/manual/fr/security.php • http://phpsec.org/ • http://www.phpsecure.info/ • http://shiflett.org/php-security.pdf • http://regexlib.com/ • http://www.securityfocus.com/infocus/1694 • http://dev.mysql.com/doc/mysql/en/Security.html • http://www.securityfocus.com/infocus/1726 • http://www.kitebird.com/articles/ins-sec.html • http://www.certa.ssi.gouv.fr/site/CERTA-2007-INF-002.pdf • http://www.certa.ssi.gouv.fr/site/CERTA-2004-INF-001/index.html • http://www.apachesecurity.net/ (horse book de chez O'Reilly) • http://en.wikibooks.org/wiki/Programming:PHP:SQL_Injection 10.Autres informations Ce document est largement perfectible et surtout, il a été fait pour débuter en sécurité web, et limiter 80 % des attaques. Contre un agresseur déterminé, regardez le site de l’OWASP. N 'hésitez pas à me signaler les erreurs et les améliorations à l'adresse fabrice.prigent@laposte.net. Il est disponible sous licence creative commons http://creativecommons.org/licenses/by-nc-sa/2.0/fr/ Version du jeudi 29 mars 2018 Fabrice Prigent 17/17
Vous pouvez aussi lire