SlideShare uma empresa Scribd logo
1 de 86
Baixar para ler offline
Segurança & Performance
WordPress
VIP.WORDPRESS.COM
Henrique Mouta
twitter.com/vaurdan
henrique@automattic.com
henrique.blog
560
TOTAL DE AUTOMATTICIANS
69
LINGUAS FALADAS
51
PAISES REPRESENTADOS
Automattic foi fundada em
2005 por Matt Mullenweg,
co-criador do WordPress, para
construir uma nova geração de
ferramentas para publicar
conteúdo online e para apoiar
a comunidade open-source.
VIP.WORDPRESS.COM
Alguns dos nossos clientes
VIP.WORDPRESS.COM
Segurança WordPress
VIP.WORDPRESS.COM
Validação de Dados
Escaping
VIP.WORDPRESS.COM
Porquê & Importância
VIP.WORDPRESS.COM
Late Escaping - Sempre!
VIP.WORDPRESS.COM
CORRECTO
ERRADO
$value = some_attribute();
echo "<div class='" . esc_attr( $value ) . "'> Hello! </div>";
$value = esc_attr( $some_attribute );
echo "<div class='$value'> Hello! </div>";
VIP.WORDPRESS.COM
ERRADO PORQUÊ?
// (...) muito codigo novo aqui
// Mudámos o value para ir buscar um valor à API, que não está escaped
$value = get_attribute_from_api();
// (...) muito código novo aqui
// Esta linha acaba esquecida e intocada, sem ter sido feito o escaping devido.
echo "<div class='$value'> Hello! </div>";
$value = esc_attr( $some_attribute );
echo "<div class='$value'> Hello! </div>";
após vários commits…
VIP.WORDPRESS.COM
Funções de Escaping
Attributes Nodes
esc_attr( $text )
esc_attr__( $text )
esc_attr_e ( $text )
Text Nodes
esc_html( $text )
esc_html__ ( $text )
esc_html_e ( $text )
esc_textarea( $text )
JavaScript
esc_js( $text )
wp_json_encode ( $var )
https://codex.wordpress.org/Data_Validation#Output_Sanitation
URL’s
esc_url( $url, (array) $protocols = null )
esc_url_raw( $url, (array) $protocols = null )
rawurlencode( $string )
Fragmentos de HTML & XHTML
wp_kses( (string) $fragment, (array) $allowed_html, (array) $protocols = null )
wp_kses_post ( $data )
Inteiros
intval( $int )
(int) $int
absint ( $int )
VIP.WORDPRESS.COM
esc_html ( $text )
$html = esc_html( ' <a href="http://example.com/">Exemplo</a>' );
echo $html;
A string é codificada para entidades HTML
&lt;a href=&quot;http://example.com/&quot;&gt;Exemplo&lt;/a&gt;
<a href="http://example.com/">Exemplo</a>
VIP.WORDPRESS.COM
esc_html__ ( $text )
Faz escape a uma string traduzida. Equivalente a esc_html( __( $text ) )
esc_html_e ( $text )
O mesmo que o anterior, mas para _e( $text ). Imprime e não necessita de echo
esc_textarea( $text )
Codifica o texto para ser utilizado dentro de uma <textarea>
VIP.WORDPRESS.COM
esc_attr ( $text )
$caption = "'><img src='jadsusaidaijd' onerror='javascript:alert('Hacked!')";
echo "<img src='https://exemplo.com/img.png' alt='" . esc_attr( $caption ) . "'>";
<img src='https://exemplo.com/img.png' alt='&#039;&gt;&lt;img src=&#039;jadsusaidaijd&#039;
onerror=&#039;javascript:alert(&#039;Hacked!&#039;)'>
'><img src='jadsusaidaijd' onerror='javascript:alert('Hacked!')
VIP.WORDPRESS.COM
esc_attr__ ( $text )
Faz escape a uma string traduzida. Equivalente a esc_attr( __( $text ) )
esc_attr_e ( $text )
O mesmo que o anterior, mas para _e( $text ). Imprime e não necessita de echo
VIP.WORDPRESS.COM
esc_url( $url, (array) $protocols = null )
<a href="<?php echo esc_url( home_url( '/' ) ); ?>">Home</a>
Home
VIP.WORDPRESS.COM
$url = 'http://wordpress.org';
$response = wp_remote_get( esc_url_raw( $url ) );
esc_url_raw ( $url, $protocols )
● Semelhante ao esc_url, valida o URL
● Não faz substituição de caracteres
● Não é seguro para utilizar no output
<img src='<?php echo esc_url_raw( $url ); ?>' />
VIP.WORDPRESS.COM
rawurlencode ( $string )
<script src="https://sometracker.com/client_id=<?php echo rawurlencode(
$client_id ); ?>">
<script src="https://sometracker.com/client_id= 1%26client_id%3D123456">
$client_id = " 1&client_id=123456";
VIP.WORDPRESS.COM
Porquê usar o rawurlencode e não o esc_url?
$client_id = " 1&client_id=123456";
<script src="https://sometracker.com/client_id=<?php echoesc_url( $client_id ); ?>">
<script src="https://sometracker.com/client_id= 1&client_id=123456">
O parametro client_id no servidor sometracker.com será 123456, visto que houve sobreposição.
VIP.WORDPRESS.COM
esc_js ( $text )
Só deve ser utilizado para JS inline (num atributo, como por exemplo no onclick=”...”)
<input type="text" onfocus="if ( this.value == ' <?php echo esc_js(
$instance['input_text'] ); ?>') { this.value = ''; }" name="email" />
Para fazer escape de JavaScript que não seja inline, deverá ser utilizado o wp_json_encode
VIP.WORDPRESS.COM
Errado!
<script>
var title = ' <?php echo esc_js( $title ); ?>';
var content = ' <?php echo esc_js( $content ); ?>';
var comment_count = ' <?php echo esc_js( $comment_count ); ?>';
</script>
Deve ser usado o wp_json_encode!
VIP.WORDPRESS.COM
<script>
var title = <?php echo wp_json_encode( $title ); ?>;
var content = <?php echo wp_json_encode( $content ); ?>;
var comment_count = <?php echo wp_json_encode( $comment_count ); ?>;
</script>
wp_json_encode ( $data )
<script>
var title = "Olá Mundo";
var content = "Isto é um exemplo.";
var comment_count = 42;
</script>
VIP.WORDPRESS.COM
wp_kses( $fragment, $allowed_html, $protocols = null )
$allowed_html = array(
'a' => array(
'href' => array(),
'title' => array()
),
'br' => array(),
'em' => array(),
'strong' => array(),
);
$content = mytheme_get_some_html_content();
echo wp_kses( $content, $allowed_html );
Limpa o HTML gerado pelo mytheme_get_some_html_content de modo a apenas permitir os
elementos e atributos listados em $allowed_html
Nota!
Não se deve permitir iframe ou script no $allowed_html porque não é seguro.
VIP.WORDPRESS.COM
wp_kses_post ( $text )
$content = mytheme_get_some_html_content();
echo wp_kses_post( $content );
● Utilizado no get_content()
● Evita definir um array $allowed_html
VIP.WORDPRESS.COM
intval ( $int )
ou (int) $int
$post_id = get_the_ID();
echo 'Post #' . intval( $post_id );
VIP.WORDPRESS.COM
absint ( $int )
$var = -1;
echo absint( $var ); // returns "1"
$posts_per_page = get_option( 'posts_per_page' );
$query = WP_Query( array(
// Makes sure $posts_per_page is never -1
'posts_per_page' => absint( $posts_per_page ),
// ...
) );
VIP.WORDPRESS.COM
Validação de Dados
Sanitation
VIP.WORDPRESS.COM
Porquê & Importância
VIP.WORDPRESS.COM
HTTP GET request para http://example.com/?name=Henrique
$name = $_GET['name'];
// Store the option
wp_update_option( 'saved_name', $name );
E se for para http://example.com/?name= <img src=”somethinginvalid.png” onerror=”(.
..)”> ?
Vai ser guardado um valor potencialmente perigoso na base de dados. A solução é sanitizar (limpar) o input o
mais cedo possível, ou seja, assim que o obtemos.
$name = sanitize_text_field( $_GET['name'] );
// Store the sanitized option
wp_update_option( 'saved_name', $name );
VIP.WORDPRESS.COM
Funções de Sanitização
sanitize_email( $data )
sanitize_file_name( $data )
sanitize_html_class( $data )
sanitize_key( $data )
sanitize_meta( $data )
sanitize_mime_type( $data )
sanitize_option( $data )
sanitize_sql_orderby( $data )
sanitize_text_field( $data )
sanitize_title( $data )
sanitize_title_for_query( $data )
sanitize_title_with_dashes( $data )
sanitize_user( $data )
esc_url_raw( $data )
wp_filter_post_kses( $data )
wp_filter_nohtml_kses( $data )
Sanitiza emails
Sanitiza nomes de ficheiros
Sanitiza classes HTML
Sanitiza chaves
Sanitiza meta (para post meta)
Sanitiza Mime Types
Sanitiza opções (para Options API)
Sanitiza o ORDER BY do SQL
Sanitiza campos de texto
Sanitiza titulos
Sanitiza titulos para utilização em queries
Sanitiza títulos com traços
Sanitiza usuários
Sanitiza URL’s (conforme já vimos)
Sanitiza conteudo HTML (à semelhança do wp_kses)
Remove todo o HTML da string
VIP.WORDPRESS.COM
Sanitizar a Base de Dados
VIP.WORDPRESS.COM
$wpdb->query( "INSERT INTO product_review (review) VALUES '" . strip_tags(
$_GET['review'], '<a><b><i><img>') . "'" );
http://example.com/?review='; DROP DATABASE;
Pode ser utilizado não só para destruir a base de dados, mas também para selecionar e devolver
usernames e passwords encriptadas.
INSERT INTO product_review (review) VALUES ''; DROP DATABASE;
VIP.WORDPRESS.COM
$wpdb->query(
$wpdb->prepare( " INSERT INTO product_review (review) VALUES %s ",
$_GET['review']
)
);
Seguro mas insuficiente
Deve ser sempre feita a sanitização antes de inserir na base de dados!
$wpdb->query(
$wpdb->prepare( " INSERT INTO product_review (review) VALUES %s ",
wp_kses_post( $_GET['review'] )
)
);
VIP.WORDPRESS.COM
$wpdb->query(
"SELECT FROM product_review WHERE review LIKE ' ". esc_like(
sanitize_text_field( $_GET['review'] ) ) . " ' "
);
Utilizar o esc_like()?
Errado :(
O esc_like() funciona como esperado, apenas para as operações LIKE do SQL.
Apenas faz o escape de % / e  que são caracteres com significado nos argumentos LIKE.
Continua a ser necessário passar os valores pelo $wpdb->prepare().
VIP.WORDPRESS.COM
Escaping & Sanitization
Nunca confiar no input dos usuários.
O escaping deve ser o mais tarde possível
Fazer escape a tudo com origem em fontes desconhecidas ou terceiras
Nunca assumir nada
Nunca confiar no input dos usuários
Sanitização é bom, mas validação/rejeição é melhor
Nunca confiar no input dos usuários
1.
2.
3.
4.
5.
6.
7.
https://vip.wordpress.com/documentation/vip/best-practices/security/validating-sanitizing-escaping/
Regras Principais
VIP.WORDPRESS.COM
Capabilities & Roles
VIP.WORDPRESS.COM
Exemplo
Queremos criar um role para Revisor de Artigos (Reviewer).
function add_reviewer_role() {
add_role( 'reviewer', 'Reviewer', array(
'read' => true,
// (...) All the necessary capabilities (...)
'edit_posts' => true,
'edit_others_posts' => true,
'edit_private_posts' => true,
) );
}
register_activation_hook( __FILE__, 'add_reviewer_role' );
Os Roles são guardados na base de dados, pelo que devem ser criados/removidos nos hooks de activação do
tema/plugin.
VIP.WORDPRESS.COM
Funções disponíveis
add_role( $role, $name, $capabilities )
remove_role( $role )
get_role( $role )
$wp_roles->add_cap( $role, $cap )
$wp_roles->remove_cap( $role, $cap )
https://vip-svn.wordpress.com/plugins/vip-do-not-include-on-wpcom/vip-roles.php
VIP.WORDPRESS.COM
add_role ( $role, $name, $capabilities )
Aviso!
function mytheme_add_foo_role() {
add_role( 'foo', 'Foo', array(
'read' => true,
) );
}
register_activation_hook( __FILE__, 'mytheme_add_foo_role' );
Os novos roles deverão conter sempre a capability read, caso
contrário o usuários não conseguirá navegar no site.
VIP.WORDPRESS.COM
get_role ( $role )
function mytheme_add_foo_caps() {
$role = get_role( 'foo' ); // Devolve um objecto WP_Role
$role->add_cap( 'edit_posts' );
}
add_action( 'admin_init', 'mytheme_add_foo_caps');
VIP.WORDPRESS.COM
$wp_roles->add_cap ( $role, $cap )
ou $role->add_cap( $cap )
function mytheme_add_foo_caps() {
$role = get_role( 'foo' );
$role->add_cap( 'edit_posts' );
}
add_action( 'admin_init', 'mytheme_add_foo_caps');
function mytheme_add_foo_caps() {
global $wp_roles;
$wp_roles->add_cap( 'foo', 'edit_posts' );
}
add_action( 'admin_init', 'mytheme_add_foo_caps');
ou
VIP.WORDPRESS.COM
$wp_roles->remove_cap ( $role, $cap )
ou $role->remove_cap( $cap )
function mytheme_remove_author_caps() {
$role = get_role( 'author' );
$role->remove_cap( 'edit_posts' );
}
add_action( 'admin_init', mytheme_remove_author_caps);
function mytheme_remove_author_caps() {
global $wp_roles;
$wp_roles->remove_cap( 'author', 'edit_posts' );
}
add_action( 'admin_init', 'mytheme_remove_author_caps');
ou
VIP.WORDPRESS.COM
Como utilizar os Roles & Capabilities
para validar permissões?
VIP.WORDPRESS.COM
current_user_can ( $capability , $object_id );
add_action( 'wp_ajax_update_foo', 'update_foo' );
function update_foo() {
// Se o usuário não pode gerir opções, retorna falso
if ( ! current_user_can('manage_options') ) {
return false;
}
update_option( 'foo', sanitize_text_field( $_POST['bar'] )) ;
}
VIP.WORDPRESS.COM
current_user_can ( $capability , $object_id );
E caso queira verificar se o usuário tem permissão para um determinado objecto?
add_action( 'wp_ajax_update_foo', 'update_foo' );
function update_foo() {
$post_id = intval( $_GET['post_id'] );
// Passa-se o $post_id no segundo argumento
if ( ! current_user_can( 'edit_post', $post_id ) ){
return false;
}
update_post_meta( $post_id, 'foo', sanitize_text_field( $_GET['bar'] )
);
}
VIP.WORDPRESS.COM
Como se pode validar a intenção do
usuário?
VIP.WORDPRESS.COM
Nonces
VIP.WORDPRESS.COM
<img src=”http://example.com/admin-ajax.php?action=update_foo&post_id=123&bar=Olá
Mundo”>
wp_create_nonce ( $action )
http://example.com/admin-ajax.php?action=update_foo&post_id=123&bar=Olá Mundo
&_wpnonce=ba623fa
VIP.WORDPRESS.COM
wp_create_nonce ( $action )
$nonce = wp_create_nonce( 'update_foo_' . $post_id );
echo "<a href='" . esc_url(
"http://example.com/admin-ajax.php?action=update_foo&post_id=123&bar=Ol
á Mundo&_wpnonce=" . $nonce ) . "'>Update Foo!</a>";
update_foo_$post_id é o action name
VIP.WORDPRESS.COM
wp_verify_nonce ( $nonce, $action )
add_action( 'wp_ajax_update_foo', 'update_foo' );
function update_foo() {
$post_id = intval( $_GET['post_id'] );
// Valida se o nonce está presente e se é válido
if ( ! wp_verify_nonce( $_GET[ '_wpnonce' ], 'update_foo_' . $post_id ) ) {
return false;
}
// (...) o resto das validações de capabilities (...)
}
update_foo_$post_id é o action name
VIP.WORDPRESS.COM
wp_nonce_url ( $actionurl, $action, $name )
$nonce = wp_create_nonce( 'update_foo_' . $post_id );
echo "<a href='" . wp_nonce_url( admin_url(
'admin-ajax.php?action=update_foo&post_id=123&bar=Olá Mundo'), "update_foo_" .
$post_id ) . "'>Update Foo!</a>";
VIP.WORDPRESS.COM
wp_nonce_field ( $action, $name, $referer, $echo )
// Cria e imprime um campo escondido com o nonce
wp_nonce_field( "update_foo_" . $post_id );
Vai imprimir algo como:
<input type="hidden" id="_wpnonce" name="_wpnonce" value=" 796c7766b1" />
VIP.WORDPRESS.COM
check_admin_referer ( $action, $query_arg )
add_action( 'wp_ajax_update_foo', 'update_foo' );
function update_foo() {
$post_id = intval( $_GET['post_id'] );
check_admin_referer( 'update_foo_' . $post_id );
// (...) o resto das validações de capabilities (...)
}
VIP.WORDPRESS.COM
Manipulação de tipos de dados
VIP.WORDPRESS.COM
Utilizar sempre comparações estritas
'hello' == 0
Qual o valor de
retorno?
VIP.WORDPRESS.COM
Utilizar sempre comparações estritas
'hello' == 0
É uma comparação com um int.
Converte 'hello' para 0
(int) "hello"; // devolve 0
(int) "1hello"; // devolve 1
Portanto
0 == 0
Retorna true.
VIP.WORDPRESS.COM
Utilizar sempre comparações estritas
$string = 'Olá Mundo';
if ( $string == 0 ) {
echo "$string é um inteiro de valor 0";
} else {
echo "$string não é um inteiro de valor 0";
}
Olá Mundo é um inteiro de valor 0
$string == 0 avalia como true
VIP.WORDPRESS.COM
Utilizar sempre comparações estritas
$string = 'Olá Mundo';
if ( $string === 0 ) {
echo "$string é um inteiro de valor 0";
} else {
echo "$string não é um inteiro de valor 0";
}
Olá Mundo não é um inteiro de valor 0
$string === 0 avalia como false
VIP.WORDPRESS.COM
Utilizar sempre comparações estritas
$array = array(
"foo" => "bar",
"word" => "press",
"php" => 0
);
$string = "hello";
if ( in_array( $string, $array ) ) {
echo 'Encontrado!';
} else {
echo 'Não Encontrado!';
}
Encontrado!
Mas "hello" não está no array!
"hello" == 0 avalia como true
VIP.WORDPRESS.COM
Utilizar sempre comparações estritas
$array = array(
"foo" => "bar",
"word" => "press",
"php" => 0
);
$string = "hello";
if ( in_array( $string, $array, true ) ) {
echo 'Encontrado!';
} else {
echo 'Não Encontrado!';
}
Não Encontrado!
Utilizando o parâmetro strict, o resultado já é o esperado.
"hello" === 0 avalia como false
VIP.WORDPRESS.COM
Utilizar sempre comparações estritas
Sempre que for feita uma comparação, deverá ser feita de forma estrita. Desta
forma, o interpretador não tenta adivinhar qual o tipo de dados (Type Juggling)
e não comete estes erros inesperados.
Tentar utilizar sempre o parâmetro strict no in_array(), e === nas
comparações.
VIP.WORDPRESS.COM
Performance WordPress
VIP.WORDPRESS.COM
Performance WordPress
Full Page Caching
VIP.WORDPRESS.COM
Full Page Cache
● Fazer sempre o full page cache de páginas com todos os parâmetros GET, ou
ignorar alguns como o 'fb*', 'utm_*', etc
● Inclui também requests ao WP-API
● Para gerir o cache pode ser utilizado Varnish, Memcache(d) (juntamente com o
plugin Batcache), ou outras soluções com os respectivos plugins (W3 Total
Cache, WP Super Cache, …)
VIP.WORDPRESS.COM
Full Page Cache e AJAX
● O Full Page Cache deverá ser configurado para fazer o cache de certos
parametros para os pedidos AJAX.
● Para aproveitar o Full Page Cache, utilizar a API do WP_Rewrite para converter os
URLs AJAX:
https://example.com/admin-ajax.php?category_id=123&page=2
com o WP_Rewrite pode-se transformar em
https://example.com/ajax/category/123/2/
VIP.WORDPRESS.COM
Performance WordPress
Queries SQL Lentas
VIP.WORDPRESS.COM
Como identificar?
VIP.WORDPRESS.COM
Como identificar?
● Plugins de apoio ao desenvolvimento:
Query Monitor - https://wordpress.org/plugins/query-monitor/
Debug Bar - https://wordpress.org/plugins/debug-bar/
● New Relic
http://newrelic.com/
● Bom conhecimento do DB Schema do WordPress
http://codex.wordpress.org/Database_Description
VIP.WORDPRESS.COM
O que evitar?
VIP.WORDPRESS.COM
● Queries que pesquisem por meta value
○ WP_Query( 'meta_key' => 'city', 'meta_value' =>
'São Paulo' );
● Utilização de NOT IN
○ WP_Query( 'category__not_in' => 'featured' );
● Muitos JOINs
○ WP_Query( 'category_in' => [ 1,3,4,6,8,9 ],
'meta_key' => 'is_starred', 'post_tag_in' => ...
);
VIP.WORDPRESS.COM
Post Meta
● Utilizar um termo em vez de um post meta
'meta_key' => 'city', 'meta_value' => 'São Paulo' torna-se
'taxonomy' => 'city', 'term' => 'São Paulo'
1.
● Situações Binárias ( is_featured = true ou is_featured = false )
Procura pela existencia da meta_key
Apagar a meta_key se falso
● Situações Não Binárias ( meta_key => primary_category, meta_value => sports )
Procurar pela meta_key primary_category_sports e aplicar a mesma lógica binária
● Utilizar Elastic Search (https://github.com/alleyinteractive/es-wp-query ou outro)
VIP.WORDPRESS.COM
NOT IN em Taxonomias
● Remover todos os posts dessa categoria e coloca-los num novo Post Type.
● Saltar posts no PHP
Em vez de pedir apenas 10 posts, pedir 20 e saltar os posts que não queremos no PHP. (não é
garantido obter os 10 posts que queremos)
// Antes
get_posts( 'category__not_in' => 'featured');
// Depois
get_posts( 'post_type' => 'featured' );
VIP.WORDPRESS.COM
Alavancar a Cache de Base de Dados
● Garantir que todas as chamadas para get_posts() incluem
suppress_filters => false para permitir o caching
● Generalizar as queries o mais possível
Em vez de utilizar posts__not_in,fazer o query com num_posts + xe saltar
posts condicionalmente no PHP
VIP.WORDPRESS.COM
Performance WordPress
Funções sem Cache
VIP.WORDPRESS.COM
Funções que precisam de suppress_filters =>
false
● get_posts()
● wp_get_recent_posts()
● get_children()
(é também necessário um limite na query)
VIP.WORDPRESS.COM
Funções sem Cache - Core
● Funções que fazem chamadas directas à base de dados sempre
● wp_get_post_terms()
● wp_get_post_categories()
● wp_get_post_tags()
● wp_get_object_terms()
● Solução: Utilizar get_the_terms() em vez destas funções
https://vip.wordpress.com/documentation/uncached-functions/
● term_exists()
● get_page_by_title()
● get_page_by_path()
● url_to_post_id()
● count_users_posts()
● Solução: Utilizar as funções de substituição que tenham cache
VIP.WORDPRESS.COM
E se a query não poder ser mais
otimizada?
VIP.WORDPRESS.COM
Faz-se o cache dos resultados
wp_cache_set() e wp_cache_get() (ou transientes)
$something = wp_cache_get( 'cache_key' );
if ( ! $something ) {
// Obter os dados necessarios
$something = new WP_Query( ... );
wp_cache_set( 'cache_key', $something );
}
return $something;
Não fazer o cache para usuários autenticados ou no wp-admin (excepto AJAX)
VIP.WORDPRESS.COM
Cache de Pedidos Remotos
VIP.WORDPRESS.COM
● wp_remote_get()
● WP_HTTP
● wp_oembed_get()
Solução: Utilizar o Object Caching (wp_cache_set e wp_cache_get) para fazer o
cache do resultado.
Não esquecer de programar o comportamento quando o servidor remoto está em
baixo.
https://vip.wordpress.com/documentation/best-practices/fetching-remote-data/
https://vip-svn.wordpress.com/vip-helper.php
Estamos contratando!
https://vip.wordpress.com/jobs/
Perguntas?

Mais conteúdo relacionado

Mais procurados

Otimização e Escalabilidade
Otimização e EscalabilidadeOtimização e Escalabilidade
Otimização e Escalabilidademetzen
 
Prog web 02-php-primeiros-passos
Prog web 02-php-primeiros-passosProg web 02-php-primeiros-passos
Prog web 02-php-primeiros-passosRegis Magalhães
 
Aplicações rápidas para a Web com Django
Aplicações rápidas para a Web com DjangoAplicações rápidas para a Web com Django
Aplicações rápidas para a Web com DjangoFreedom DayMS
 
Refactoring sem complicação!
Refactoring sem complicação!Refactoring sem complicação!
Refactoring sem complicação!Thamara Hessel
 
Palestra de segurança em PHP - Hacking
Palestra de segurança em PHP - HackingPalestra de segurança em PHP - Hacking
Palestra de segurança em PHP - HackingLuis Gustavo Almeida
 
PHP básico para iniciantes
PHP básico para iniciantesPHP básico para iniciantes
PHP básico para iniciantesEduardo Mendes
 
Cuso Ruby - Aula 05 - Testes com RSpec
Cuso Ruby - Aula 05 - Testes com RSpecCuso Ruby - Aula 05 - Testes com RSpec
Cuso Ruby - Aula 05 - Testes com RSpecMaurício Linhares
 
Rafael Garcia - Yii Framework, principais características e em ação
Rafael Garcia - Yii Framework, principais características e em açãoRafael Garcia - Yii Framework, principais características e em ação
Rafael Garcia - Yii Framework, principais características e em açãoRafael Garcia
 
JS Experience 2017 - Javascript Funcional
JS Experience 2017 - Javascript FuncionalJS Experience 2017 - Javascript Funcional
JS Experience 2017 - Javascript FuncionaliMasters
 
Criando uma arquitetura de front-end do zero
Criando uma arquitetura de front-end do zeroCriando uma arquitetura de front-end do zero
Criando uma arquitetura de front-end do zeroEduardo Shiota Yasuda
 
Desafios do Desenvolvimento de Front-end em um e-commerce
Desafios do Desenvolvimento de Front-end em um e-commerceDesafios do Desenvolvimento de Front-end em um e-commerce
Desafios do Desenvolvimento de Front-end em um e-commerceEduardo Shiota Yasuda
 

Mais procurados (16)

Otimização e Escalabilidade
Otimização e EscalabilidadeOtimização e Escalabilidade
Otimização e Escalabilidade
 
Php 02 Primeiros Passos
Php 02 Primeiros PassosPhp 02 Primeiros Passos
Php 02 Primeiros Passos
 
Prog web 02-php-primeiros-passos
Prog web 02-php-primeiros-passosProg web 02-php-primeiros-passos
Prog web 02-php-primeiros-passos
 
jQuery Simplificando o JavaScript
jQuery Simplificando o JavaScriptjQuery Simplificando o JavaScript
jQuery Simplificando o JavaScript
 
Aplicações rápidas para a Web com Django
Aplicações rápidas para a Web com DjangoAplicações rápidas para a Web com Django
Aplicações rápidas para a Web com Django
 
Refactoring sem complicação!
Refactoring sem complicação!Refactoring sem complicação!
Refactoring sem complicação!
 
Palestra de segurança em PHP - Hacking
Palestra de segurança em PHP - HackingPalestra de segurança em PHP - Hacking
Palestra de segurança em PHP - Hacking
 
PHP básico para iniciantes
PHP básico para iniciantesPHP básico para iniciantes
PHP básico para iniciantes
 
Curso de Introdução - PHP
Curso de Introdução - PHPCurso de Introdução - PHP
Curso de Introdução - PHP
 
Cuso Ruby - Aula 05 - Testes com RSpec
Cuso Ruby - Aula 05 - Testes com RSpecCuso Ruby - Aula 05 - Testes com RSpec
Cuso Ruby - Aula 05 - Testes com RSpec
 
Rafael Garcia - Yii Framework, principais características e em ação
Rafael Garcia - Yii Framework, principais características e em açãoRafael Garcia - Yii Framework, principais características e em ação
Rafael Garcia - Yii Framework, principais características e em ação
 
JS Experience 2017 - Javascript Funcional
JS Experience 2017 - Javascript FuncionalJS Experience 2017 - Javascript Funcional
JS Experience 2017 - Javascript Funcional
 
Criando uma arquitetura de front-end do zero
Criando uma arquitetura de front-end do zeroCriando uma arquitetura de front-end do zero
Criando uma arquitetura de front-end do zero
 
PHP MySQL Aula 07
PHP MySQL Aula 07PHP MySQL Aula 07
PHP MySQL Aula 07
 
Desafios do Desenvolvimento de Front-end em um e-commerce
Desafios do Desenvolvimento de Front-end em um e-commerceDesafios do Desenvolvimento de Front-end em um e-commerce
Desafios do Desenvolvimento de Front-end em um e-commerce
 
Java script aula 10 - angularjs
Java script   aula 10 - angularjsJava script   aula 10 - angularjs
Java script aula 10 - angularjs
 

Semelhante a Segurança e Performance WordPress

Evento Front End SP - Arquitetura de Front
Evento Front End SP - Arquitetura de FrontEvento Front End SP - Arquitetura de Front
Evento Front End SP - Arquitetura de FrontMichel Ribeiro
 
Como Perder Peso (no browser)
Como Perder Peso (no browser)Como Perder Peso (no browser)
Como Perder Peso (no browser)Zeno Rocha
 
MeetUp WP Floripa - dicas simples de como deixar o admin com a cara do seu cl...
MeetUp WP Floripa - dicas simples de como deixar o admin com a cara do seu cl...MeetUp WP Floripa - dicas simples de como deixar o admin com a cara do seu cl...
MeetUp WP Floripa - dicas simples de como deixar o admin com a cara do seu cl...InCuca
 
Dicas simples de como deixar o admin do wordpress com a cara do seu cliente
Dicas simples de como deixar o admin do wordpress com a cara do seu clienteDicas simples de como deixar o admin do wordpress com a cara do seu cliente
Dicas simples de como deixar o admin do wordpress com a cara do seu clienteLuã Ciceri Schwertner
 
JavaScript e JQuery para Webdesigners
JavaScript e JQuery para WebdesignersJavaScript e JQuery para Webdesigners
JavaScript e JQuery para WebdesignersHarlley Oliveira
 
Desenvolvendo aplicações web com o framework cakephp
Desenvolvendo aplicações web com o framework cakephpDesenvolvendo aplicações web com o framework cakephp
Desenvolvendo aplicações web com o framework cakephpRodrigo Aramburu
 
Simplificando o Javascrip
Simplificando o JavascripSimplificando o Javascrip
Simplificando o JavascripMiquéias Amaro
 
Como criar um plugin para WordPress
Como criar um plugin para WordPressComo criar um plugin para WordPress
Como criar um plugin para WordPressLeandrinho Vieira
 
Mini Curso PHP Twig - PHP Conference 2017
Mini Curso PHP Twig - PHP Conference 2017 Mini Curso PHP Twig - PHP Conference 2017
Mini Curso PHP Twig - PHP Conference 2017 Luis Gustavo Almeida
 
Da argila ao forte: como desenvolver uma loja com PagSeguro
Da argila ao forte: como desenvolver uma loja com PagSeguroDa argila ao forte: como desenvolver uma loja com PagSeguro
Da argila ao forte: como desenvolver uma loja com PagSeguroMichael Castillo Granados
 
Coisas que eu gostaria de saber antes de começar a desenvolver temas e plugin...
Coisas que eu gostaria de saber antes de começar a desenvolver temas e plugin...Coisas que eu gostaria de saber antes de começar a desenvolver temas e plugin...
Coisas que eu gostaria de saber antes de começar a desenvolver temas e plugin...Leo Baiano
 
LabMM4 (T22 - 12/13) - segurança
LabMM4 (T22 - 12/13) - segurançaLabMM4 (T22 - 12/13) - segurança
LabMM4 (T22 - 12/13) - segurançaCarlos Santos
 

Semelhante a Segurança e Performance WordPress (20)

Aula 8 php
Aula 8 phpAula 8 php
Aula 8 php
 
Hello SAFE World!!!
Hello SAFE World!!!Hello SAFE World!!!
Hello SAFE World!!!
 
Zend Framework
Zend FrameworkZend Framework
Zend Framework
 
Evento Front End SP - Arquitetura de Front
Evento Front End SP - Arquitetura de FrontEvento Front End SP - Arquitetura de Front
Evento Front End SP - Arquitetura de Front
 
Wicket 2008
Wicket 2008Wicket 2008
Wicket 2008
 
Como Perder Peso (no browser)
Como Perder Peso (no browser)Como Perder Peso (no browser)
Como Perder Peso (no browser)
 
Php 07 Cakephp
Php 07 CakephpPhp 07 Cakephp
Php 07 Cakephp
 
MeetUp WP Floripa - dicas simples de como deixar o admin com a cara do seu cl...
MeetUp WP Floripa - dicas simples de como deixar o admin com a cara do seu cl...MeetUp WP Floripa - dicas simples de como deixar o admin com a cara do seu cl...
MeetUp WP Floripa - dicas simples de como deixar o admin com a cara do seu cl...
 
Dicas simples de como deixar o admin do wordpress com a cara do seu cliente
Dicas simples de como deixar o admin do wordpress com a cara do seu clienteDicas simples de como deixar o admin do wordpress com a cara do seu cliente
Dicas simples de como deixar o admin do wordpress com a cara do seu cliente
 
JavaScript e JQuery para Webdesigners
JavaScript e JQuery para WebdesignersJavaScript e JQuery para Webdesigners
JavaScript e JQuery para Webdesigners
 
Desenvolvendo aplicações web com o framework cakephp
Desenvolvendo aplicações web com o framework cakephpDesenvolvendo aplicações web com o framework cakephp
Desenvolvendo aplicações web com o framework cakephp
 
Introdução ao JQuery e AJAX
Introdução ao JQuery e AJAXIntrodução ao JQuery e AJAX
Introdução ao JQuery e AJAX
 
Simplificando o Javascrip
Simplificando o JavascripSimplificando o Javascrip
Simplificando o Javascrip
 
Como criar um plugin para WordPress
Como criar um plugin para WordPressComo criar um plugin para WordPress
Como criar um plugin para WordPress
 
Mini Curso PHP Twig - PHP Conference 2017
Mini Curso PHP Twig - PHP Conference 2017 Mini Curso PHP Twig - PHP Conference 2017
Mini Curso PHP Twig - PHP Conference 2017
 
Da argila ao forte: como desenvolver uma loja com PagSeguro
Da argila ao forte: como desenvolver uma loja com PagSeguroDa argila ao forte: como desenvolver uma loja com PagSeguro
Da argila ao forte: como desenvolver uma loja com PagSeguro
 
Aplicacoes Rapidas Para Web Com Django
Aplicacoes Rapidas Para Web Com DjangoAplicacoes Rapidas Para Web Com Django
Aplicacoes Rapidas Para Web Com Django
 
Coisas que eu gostaria de saber antes de começar a desenvolver temas e plugin...
Coisas que eu gostaria de saber antes de começar a desenvolver temas e plugin...Coisas que eu gostaria de saber antes de começar a desenvolver temas e plugin...
Coisas que eu gostaria de saber antes de começar a desenvolver temas e plugin...
 
Java e Cloud Computing
Java e Cloud ComputingJava e Cloud Computing
Java e Cloud Computing
 
LabMM4 (T22 - 12/13) - segurança
LabMM4 (T22 - 12/13) - segurançaLabMM4 (T22 - 12/13) - segurança
LabMM4 (T22 - 12/13) - segurança
 

Segurança e Performance WordPress

  • 3. 560 TOTAL DE AUTOMATTICIANS 69 LINGUAS FALADAS 51 PAISES REPRESENTADOS Automattic foi fundada em 2005 por Matt Mullenweg, co-criador do WordPress, para construir uma nova geração de ferramentas para publicar conteúdo online e para apoiar a comunidade open-source.
  • 4.
  • 10. VIP.WORDPRESS.COM CORRECTO ERRADO $value = some_attribute(); echo "<div class='" . esc_attr( $value ) . "'> Hello! </div>"; $value = esc_attr( $some_attribute ); echo "<div class='$value'> Hello! </div>";
  • 11. VIP.WORDPRESS.COM ERRADO PORQUÊ? // (...) muito codigo novo aqui // Mudámos o value para ir buscar um valor à API, que não está escaped $value = get_attribute_from_api(); // (...) muito código novo aqui // Esta linha acaba esquecida e intocada, sem ter sido feito o escaping devido. echo "<div class='$value'> Hello! </div>"; $value = esc_attr( $some_attribute ); echo "<div class='$value'> Hello! </div>"; após vários commits…
  • 13. Attributes Nodes esc_attr( $text ) esc_attr__( $text ) esc_attr_e ( $text ) Text Nodes esc_html( $text ) esc_html__ ( $text ) esc_html_e ( $text ) esc_textarea( $text ) JavaScript esc_js( $text ) wp_json_encode ( $var ) https://codex.wordpress.org/Data_Validation#Output_Sanitation URL’s esc_url( $url, (array) $protocols = null ) esc_url_raw( $url, (array) $protocols = null ) rawurlencode( $string ) Fragmentos de HTML & XHTML wp_kses( (string) $fragment, (array) $allowed_html, (array) $protocols = null ) wp_kses_post ( $data ) Inteiros intval( $int ) (int) $int absint ( $int )
  • 14. VIP.WORDPRESS.COM esc_html ( $text ) $html = esc_html( ' <a href="http://example.com/">Exemplo</a>' ); echo $html; A string é codificada para entidades HTML &lt;a href=&quot;http://example.com/&quot;&gt;Exemplo&lt;/a&gt; <a href="http://example.com/">Exemplo</a>
  • 15. VIP.WORDPRESS.COM esc_html__ ( $text ) Faz escape a uma string traduzida. Equivalente a esc_html( __( $text ) ) esc_html_e ( $text ) O mesmo que o anterior, mas para _e( $text ). Imprime e não necessita de echo esc_textarea( $text ) Codifica o texto para ser utilizado dentro de uma <textarea>
  • 16. VIP.WORDPRESS.COM esc_attr ( $text ) $caption = "'><img src='jadsusaidaijd' onerror='javascript:alert('Hacked!')"; echo "<img src='https://exemplo.com/img.png' alt='" . esc_attr( $caption ) . "'>"; <img src='https://exemplo.com/img.png' alt='&#039;&gt;&lt;img src=&#039;jadsusaidaijd&#039; onerror=&#039;javascript:alert(&#039;Hacked!&#039;)'> '><img src='jadsusaidaijd' onerror='javascript:alert('Hacked!')
  • 17. VIP.WORDPRESS.COM esc_attr__ ( $text ) Faz escape a uma string traduzida. Equivalente a esc_attr( __( $text ) ) esc_attr_e ( $text ) O mesmo que o anterior, mas para _e( $text ). Imprime e não necessita de echo
  • 18. VIP.WORDPRESS.COM esc_url( $url, (array) $protocols = null ) <a href="<?php echo esc_url( home_url( '/' ) ); ?>">Home</a> Home
  • 19. VIP.WORDPRESS.COM $url = 'http://wordpress.org'; $response = wp_remote_get( esc_url_raw( $url ) ); esc_url_raw ( $url, $protocols ) ● Semelhante ao esc_url, valida o URL ● Não faz substituição de caracteres ● Não é seguro para utilizar no output <img src='<?php echo esc_url_raw( $url ); ?>' />
  • 20. VIP.WORDPRESS.COM rawurlencode ( $string ) <script src="https://sometracker.com/client_id=<?php echo rawurlencode( $client_id ); ?>"> <script src="https://sometracker.com/client_id= 1%26client_id%3D123456"> $client_id = " 1&client_id=123456";
  • 21. VIP.WORDPRESS.COM Porquê usar o rawurlencode e não o esc_url? $client_id = " 1&client_id=123456"; <script src="https://sometracker.com/client_id=<?php echoesc_url( $client_id ); ?>"> <script src="https://sometracker.com/client_id= 1&client_id=123456"> O parametro client_id no servidor sometracker.com será 123456, visto que houve sobreposição.
  • 22. VIP.WORDPRESS.COM esc_js ( $text ) Só deve ser utilizado para JS inline (num atributo, como por exemplo no onclick=”...”) <input type="text" onfocus="if ( this.value == ' <?php echo esc_js( $instance['input_text'] ); ?>') { this.value = ''; }" name="email" /> Para fazer escape de JavaScript que não seja inline, deverá ser utilizado o wp_json_encode
  • 23. VIP.WORDPRESS.COM Errado! <script> var title = ' <?php echo esc_js( $title ); ?>'; var content = ' <?php echo esc_js( $content ); ?>'; var comment_count = ' <?php echo esc_js( $comment_count ); ?>'; </script> Deve ser usado o wp_json_encode!
  • 24. VIP.WORDPRESS.COM <script> var title = <?php echo wp_json_encode( $title ); ?>; var content = <?php echo wp_json_encode( $content ); ?>; var comment_count = <?php echo wp_json_encode( $comment_count ); ?>; </script> wp_json_encode ( $data ) <script> var title = "Olá Mundo"; var content = "Isto é um exemplo."; var comment_count = 42; </script>
  • 25. VIP.WORDPRESS.COM wp_kses( $fragment, $allowed_html, $protocols = null ) $allowed_html = array( 'a' => array( 'href' => array(), 'title' => array() ), 'br' => array(), 'em' => array(), 'strong' => array(), ); $content = mytheme_get_some_html_content(); echo wp_kses( $content, $allowed_html ); Limpa o HTML gerado pelo mytheme_get_some_html_content de modo a apenas permitir os elementos e atributos listados em $allowed_html Nota! Não se deve permitir iframe ou script no $allowed_html porque não é seguro.
  • 26. VIP.WORDPRESS.COM wp_kses_post ( $text ) $content = mytheme_get_some_html_content(); echo wp_kses_post( $content ); ● Utilizado no get_content() ● Evita definir um array $allowed_html
  • 27. VIP.WORDPRESS.COM intval ( $int ) ou (int) $int $post_id = get_the_ID(); echo 'Post #' . intval( $post_id );
  • 28. VIP.WORDPRESS.COM absint ( $int ) $var = -1; echo absint( $var ); // returns "1" $posts_per_page = get_option( 'posts_per_page' ); $query = WP_Query( array( // Makes sure $posts_per_page is never -1 'posts_per_page' => absint( $posts_per_page ), // ... ) );
  • 31. VIP.WORDPRESS.COM HTTP GET request para http://example.com/?name=Henrique $name = $_GET['name']; // Store the option wp_update_option( 'saved_name', $name ); E se for para http://example.com/?name= <img src=”somethinginvalid.png” onerror=”(. ..)”> ? Vai ser guardado um valor potencialmente perigoso na base de dados. A solução é sanitizar (limpar) o input o mais cedo possível, ou seja, assim que o obtemos. $name = sanitize_text_field( $_GET['name'] ); // Store the sanitized option wp_update_option( 'saved_name', $name );
  • 33. sanitize_email( $data ) sanitize_file_name( $data ) sanitize_html_class( $data ) sanitize_key( $data ) sanitize_meta( $data ) sanitize_mime_type( $data ) sanitize_option( $data ) sanitize_sql_orderby( $data ) sanitize_text_field( $data ) sanitize_title( $data ) sanitize_title_for_query( $data ) sanitize_title_with_dashes( $data ) sanitize_user( $data ) esc_url_raw( $data ) wp_filter_post_kses( $data ) wp_filter_nohtml_kses( $data ) Sanitiza emails Sanitiza nomes de ficheiros Sanitiza classes HTML Sanitiza chaves Sanitiza meta (para post meta) Sanitiza Mime Types Sanitiza opções (para Options API) Sanitiza o ORDER BY do SQL Sanitiza campos de texto Sanitiza titulos Sanitiza titulos para utilização em queries Sanitiza títulos com traços Sanitiza usuários Sanitiza URL’s (conforme já vimos) Sanitiza conteudo HTML (à semelhança do wp_kses) Remove todo o HTML da string
  • 35. VIP.WORDPRESS.COM $wpdb->query( "INSERT INTO product_review (review) VALUES '" . strip_tags( $_GET['review'], '<a><b><i><img>') . "'" ); http://example.com/?review='; DROP DATABASE; Pode ser utilizado não só para destruir a base de dados, mas também para selecionar e devolver usernames e passwords encriptadas. INSERT INTO product_review (review) VALUES ''; DROP DATABASE;
  • 36. VIP.WORDPRESS.COM $wpdb->query( $wpdb->prepare( " INSERT INTO product_review (review) VALUES %s ", $_GET['review'] ) ); Seguro mas insuficiente Deve ser sempre feita a sanitização antes de inserir na base de dados! $wpdb->query( $wpdb->prepare( " INSERT INTO product_review (review) VALUES %s ", wp_kses_post( $_GET['review'] ) ) );
  • 37. VIP.WORDPRESS.COM $wpdb->query( "SELECT FROM product_review WHERE review LIKE ' ". esc_like( sanitize_text_field( $_GET['review'] ) ) . " ' " ); Utilizar o esc_like()? Errado :( O esc_like() funciona como esperado, apenas para as operações LIKE do SQL. Apenas faz o escape de % / e que são caracteres com significado nos argumentos LIKE. Continua a ser necessário passar os valores pelo $wpdb->prepare().
  • 38. VIP.WORDPRESS.COM Escaping & Sanitization Nunca confiar no input dos usuários. O escaping deve ser o mais tarde possível Fazer escape a tudo com origem em fontes desconhecidas ou terceiras Nunca assumir nada Nunca confiar no input dos usuários Sanitização é bom, mas validação/rejeição é melhor Nunca confiar no input dos usuários 1. 2. 3. 4. 5. 6. 7. https://vip.wordpress.com/documentation/vip/best-practices/security/validating-sanitizing-escaping/ Regras Principais
  • 40. VIP.WORDPRESS.COM Exemplo Queremos criar um role para Revisor de Artigos (Reviewer). function add_reviewer_role() { add_role( 'reviewer', 'Reviewer', array( 'read' => true, // (...) All the necessary capabilities (...) 'edit_posts' => true, 'edit_others_posts' => true, 'edit_private_posts' => true, ) ); } register_activation_hook( __FILE__, 'add_reviewer_role' ); Os Roles são guardados na base de dados, pelo que devem ser criados/removidos nos hooks de activação do tema/plugin.
  • 42. add_role( $role, $name, $capabilities ) remove_role( $role ) get_role( $role ) $wp_roles->add_cap( $role, $cap ) $wp_roles->remove_cap( $role, $cap ) https://vip-svn.wordpress.com/plugins/vip-do-not-include-on-wpcom/vip-roles.php
  • 43. VIP.WORDPRESS.COM add_role ( $role, $name, $capabilities ) Aviso! function mytheme_add_foo_role() { add_role( 'foo', 'Foo', array( 'read' => true, ) ); } register_activation_hook( __FILE__, 'mytheme_add_foo_role' ); Os novos roles deverão conter sempre a capability read, caso contrário o usuários não conseguirá navegar no site.
  • 44. VIP.WORDPRESS.COM get_role ( $role ) function mytheme_add_foo_caps() { $role = get_role( 'foo' ); // Devolve um objecto WP_Role $role->add_cap( 'edit_posts' ); } add_action( 'admin_init', 'mytheme_add_foo_caps');
  • 45. VIP.WORDPRESS.COM $wp_roles->add_cap ( $role, $cap ) ou $role->add_cap( $cap ) function mytheme_add_foo_caps() { $role = get_role( 'foo' ); $role->add_cap( 'edit_posts' ); } add_action( 'admin_init', 'mytheme_add_foo_caps'); function mytheme_add_foo_caps() { global $wp_roles; $wp_roles->add_cap( 'foo', 'edit_posts' ); } add_action( 'admin_init', 'mytheme_add_foo_caps'); ou
  • 46. VIP.WORDPRESS.COM $wp_roles->remove_cap ( $role, $cap ) ou $role->remove_cap( $cap ) function mytheme_remove_author_caps() { $role = get_role( 'author' ); $role->remove_cap( 'edit_posts' ); } add_action( 'admin_init', mytheme_remove_author_caps); function mytheme_remove_author_caps() { global $wp_roles; $wp_roles->remove_cap( 'author', 'edit_posts' ); } add_action( 'admin_init', 'mytheme_remove_author_caps'); ou
  • 47. VIP.WORDPRESS.COM Como utilizar os Roles & Capabilities para validar permissões?
  • 48. VIP.WORDPRESS.COM current_user_can ( $capability , $object_id ); add_action( 'wp_ajax_update_foo', 'update_foo' ); function update_foo() { // Se o usuário não pode gerir opções, retorna falso if ( ! current_user_can('manage_options') ) { return false; } update_option( 'foo', sanitize_text_field( $_POST['bar'] )) ; }
  • 49. VIP.WORDPRESS.COM current_user_can ( $capability , $object_id ); E caso queira verificar se o usuário tem permissão para um determinado objecto? add_action( 'wp_ajax_update_foo', 'update_foo' ); function update_foo() { $post_id = intval( $_GET['post_id'] ); // Passa-se o $post_id no segundo argumento if ( ! current_user_can( 'edit_post', $post_id ) ){ return false; } update_post_meta( $post_id, 'foo', sanitize_text_field( $_GET['bar'] ) ); }
  • 50. VIP.WORDPRESS.COM Como se pode validar a intenção do usuário?
  • 52. VIP.WORDPRESS.COM <img src=”http://example.com/admin-ajax.php?action=update_foo&post_id=123&bar=Olá Mundo”> wp_create_nonce ( $action ) http://example.com/admin-ajax.php?action=update_foo&post_id=123&bar=Olá Mundo &_wpnonce=ba623fa
  • 53. VIP.WORDPRESS.COM wp_create_nonce ( $action ) $nonce = wp_create_nonce( 'update_foo_' . $post_id ); echo "<a href='" . esc_url( "http://example.com/admin-ajax.php?action=update_foo&post_id=123&bar=Ol á Mundo&_wpnonce=" . $nonce ) . "'>Update Foo!</a>"; update_foo_$post_id é o action name
  • 54. VIP.WORDPRESS.COM wp_verify_nonce ( $nonce, $action ) add_action( 'wp_ajax_update_foo', 'update_foo' ); function update_foo() { $post_id = intval( $_GET['post_id'] ); // Valida se o nonce está presente e se é válido if ( ! wp_verify_nonce( $_GET[ '_wpnonce' ], 'update_foo_' . $post_id ) ) { return false; } // (...) o resto das validações de capabilities (...) } update_foo_$post_id é o action name
  • 55. VIP.WORDPRESS.COM wp_nonce_url ( $actionurl, $action, $name ) $nonce = wp_create_nonce( 'update_foo_' . $post_id ); echo "<a href='" . wp_nonce_url( admin_url( 'admin-ajax.php?action=update_foo&post_id=123&bar=Olá Mundo'), "update_foo_" . $post_id ) . "'>Update Foo!</a>";
  • 56. VIP.WORDPRESS.COM wp_nonce_field ( $action, $name, $referer, $echo ) // Cria e imprime um campo escondido com o nonce wp_nonce_field( "update_foo_" . $post_id ); Vai imprimir algo como: <input type="hidden" id="_wpnonce" name="_wpnonce" value=" 796c7766b1" />
  • 57. VIP.WORDPRESS.COM check_admin_referer ( $action, $query_arg ) add_action( 'wp_ajax_update_foo', 'update_foo' ); function update_foo() { $post_id = intval( $_GET['post_id'] ); check_admin_referer( 'update_foo_' . $post_id ); // (...) o resto das validações de capabilities (...) }
  • 59. VIP.WORDPRESS.COM Utilizar sempre comparações estritas 'hello' == 0 Qual o valor de retorno?
  • 60. VIP.WORDPRESS.COM Utilizar sempre comparações estritas 'hello' == 0 É uma comparação com um int. Converte 'hello' para 0 (int) "hello"; // devolve 0 (int) "1hello"; // devolve 1 Portanto 0 == 0 Retorna true.
  • 61. VIP.WORDPRESS.COM Utilizar sempre comparações estritas $string = 'Olá Mundo'; if ( $string == 0 ) { echo "$string é um inteiro de valor 0"; } else { echo "$string não é um inteiro de valor 0"; } Olá Mundo é um inteiro de valor 0 $string == 0 avalia como true
  • 62. VIP.WORDPRESS.COM Utilizar sempre comparações estritas $string = 'Olá Mundo'; if ( $string === 0 ) { echo "$string é um inteiro de valor 0"; } else { echo "$string não é um inteiro de valor 0"; } Olá Mundo não é um inteiro de valor 0 $string === 0 avalia como false
  • 63. VIP.WORDPRESS.COM Utilizar sempre comparações estritas $array = array( "foo" => "bar", "word" => "press", "php" => 0 ); $string = "hello"; if ( in_array( $string, $array ) ) { echo 'Encontrado!'; } else { echo 'Não Encontrado!'; } Encontrado! Mas "hello" não está no array! "hello" == 0 avalia como true
  • 64. VIP.WORDPRESS.COM Utilizar sempre comparações estritas $array = array( "foo" => "bar", "word" => "press", "php" => 0 ); $string = "hello"; if ( in_array( $string, $array, true ) ) { echo 'Encontrado!'; } else { echo 'Não Encontrado!'; } Não Encontrado! Utilizando o parâmetro strict, o resultado já é o esperado. "hello" === 0 avalia como false
  • 65. VIP.WORDPRESS.COM Utilizar sempre comparações estritas Sempre que for feita uma comparação, deverá ser feita de forma estrita. Desta forma, o interpretador não tenta adivinhar qual o tipo de dados (Type Juggling) e não comete estes erros inesperados. Tentar utilizar sempre o parâmetro strict no in_array(), e === nas comparações.
  • 68. VIP.WORDPRESS.COM Full Page Cache ● Fazer sempre o full page cache de páginas com todos os parâmetros GET, ou ignorar alguns como o 'fb*', 'utm_*', etc ● Inclui também requests ao WP-API ● Para gerir o cache pode ser utilizado Varnish, Memcache(d) (juntamente com o plugin Batcache), ou outras soluções com os respectivos plugins (W3 Total Cache, WP Super Cache, …)
  • 69. VIP.WORDPRESS.COM Full Page Cache e AJAX ● O Full Page Cache deverá ser configurado para fazer o cache de certos parametros para os pedidos AJAX. ● Para aproveitar o Full Page Cache, utilizar a API do WP_Rewrite para converter os URLs AJAX: https://example.com/admin-ajax.php?category_id=123&page=2 com o WP_Rewrite pode-se transformar em https://example.com/ajax/category/123/2/
  • 72. VIP.WORDPRESS.COM Como identificar? ● Plugins de apoio ao desenvolvimento: Query Monitor - https://wordpress.org/plugins/query-monitor/ Debug Bar - https://wordpress.org/plugins/debug-bar/ ● New Relic http://newrelic.com/ ● Bom conhecimento do DB Schema do WordPress http://codex.wordpress.org/Database_Description
  • 74. VIP.WORDPRESS.COM ● Queries que pesquisem por meta value ○ WP_Query( 'meta_key' => 'city', 'meta_value' => 'São Paulo' ); ● Utilização de NOT IN ○ WP_Query( 'category__not_in' => 'featured' ); ● Muitos JOINs ○ WP_Query( 'category_in' => [ 1,3,4,6,8,9 ], 'meta_key' => 'is_starred', 'post_tag_in' => ... );
  • 75. VIP.WORDPRESS.COM Post Meta ● Utilizar um termo em vez de um post meta 'meta_key' => 'city', 'meta_value' => 'São Paulo' torna-se 'taxonomy' => 'city', 'term' => 'São Paulo' 1. ● Situações Binárias ( is_featured = true ou is_featured = false ) Procura pela existencia da meta_key Apagar a meta_key se falso ● Situações Não Binárias ( meta_key => primary_category, meta_value => sports ) Procurar pela meta_key primary_category_sports e aplicar a mesma lógica binária ● Utilizar Elastic Search (https://github.com/alleyinteractive/es-wp-query ou outro)
  • 76. VIP.WORDPRESS.COM NOT IN em Taxonomias ● Remover todos os posts dessa categoria e coloca-los num novo Post Type. ● Saltar posts no PHP Em vez de pedir apenas 10 posts, pedir 20 e saltar os posts que não queremos no PHP. (não é garantido obter os 10 posts que queremos) // Antes get_posts( 'category__not_in' => 'featured'); // Depois get_posts( 'post_type' => 'featured' );
  • 77. VIP.WORDPRESS.COM Alavancar a Cache de Base de Dados ● Garantir que todas as chamadas para get_posts() incluem suppress_filters => false para permitir o caching ● Generalizar as queries o mais possível Em vez de utilizar posts__not_in,fazer o query com num_posts + xe saltar posts condicionalmente no PHP
  • 79. VIP.WORDPRESS.COM Funções que precisam de suppress_filters => false ● get_posts() ● wp_get_recent_posts() ● get_children() (é também necessário um limite na query)
  • 80. VIP.WORDPRESS.COM Funções sem Cache - Core ● Funções que fazem chamadas directas à base de dados sempre ● wp_get_post_terms() ● wp_get_post_categories() ● wp_get_post_tags() ● wp_get_object_terms() ● Solução: Utilizar get_the_terms() em vez destas funções https://vip.wordpress.com/documentation/uncached-functions/ ● term_exists() ● get_page_by_title() ● get_page_by_path() ● url_to_post_id() ● count_users_posts() ● Solução: Utilizar as funções de substituição que tenham cache
  • 81. VIP.WORDPRESS.COM E se a query não poder ser mais otimizada?
  • 82. VIP.WORDPRESS.COM Faz-se o cache dos resultados wp_cache_set() e wp_cache_get() (ou transientes) $something = wp_cache_get( 'cache_key' ); if ( ! $something ) { // Obter os dados necessarios $something = new WP_Query( ... ); wp_cache_set( 'cache_key', $something ); } return $something; Não fazer o cache para usuários autenticados ou no wp-admin (excepto AJAX)
  • 84. VIP.WORDPRESS.COM ● wp_remote_get() ● WP_HTTP ● wp_oembed_get() Solução: Utilizar o Object Caching (wp_cache_set e wp_cache_get) para fazer o cache do resultado. Não esquecer de programar o comportamento quando o servidor remoto está em baixo. https://vip.wordpress.com/documentation/best-practices/fetching-remote-data/ https://vip-svn.wordpress.com/vip-helper.php