wordpress sql injection
Home » Sicurezza Wordpress » Interazioni del database e SQL

Interazioni del database e SQL

L’API di WordPress mette a disposizione un gran numero di funzioni di database, in modo che gli sviluppatori possano evitare di passare input non attendibili forniti dall’utente direttamente nelle query del database.

Queste funzioni sono solitamente precedute da: 

  • add_ 
  • update_ 
  • delete_
  • get_

ad esempio: add_option(), update_user_meta(), delete_comment_meta(), o get_comment_meta(),

oppure da: 

  • wp_insert_
  • wp_update_
  • wp_delete_

ad esempio: wp_insert_post()wp_update_comment()wp_delete_user()

Tuttavia, gli sviluppatori spesso non riescono a utilizzare queste funzioni o a utilizzarle correttamente.

Ciò è evidente dalla moltitudine di CVE di tipo SQL injection nei plugin e nei temi di WordPress.

Come ricercatore di vulnerabilità, è importante comprendere come utilizzare correttamente queste funzioni quando si esegue un’analisi statica.

Per quanto riguarda le interazioni con il database, WordPress sottolinea una sola regola per prevenire l’iniezione SQL: 

Quando c’è una funzione di WordPress, usala!

Tuttavia, questo non è sempre semplice per gli sviluppatori.

Se una funzione predefinita non si adatta al caso d’uso dello sviluppatore, WordPress rende disponibili i metodi $wpdb.

La variabile globale $wpdb rappresenta un’astrazione del database, semplificando le interazioni con esso e garantisce che gli sviluppatori seguano le best practice dal punto di vista della sicurezza.

Anche in questo caso però spesso viene utilizzata in un modo che la rende vulnerabile all’iniezione SQL.

Di seguito è riportata una panoramica di alcuni dei principali metodi di $wpdb che probabilmente vedrai utilizzati in produzione.

Inserisce una nuova riga in una tabella personalizzata.

$wpdb->insert($table, $data, $format);

global $wpdb;
$wpdb->insert('wp_users', array(
    'name' => 'John',
    'age' => 30
), array('%s', '%d'));

Aggiorna le righe esistenti in una tabella personalizzata in base alle condizioni.

$wpdb->update($table, $data, $where, $format, $where_format)

global $wpdb;
$wpdb->update('wp_users', array(
    'age' => 35
), array(
    'name' => 'John'
), array('%d'), array('%s'));

Elimina le righe da una tabella personalizzata in base alle condizioni.

$wpdb->delete($table, $where, $where_format);

global $wpdb;
$wpdb->delete('wp_users', array(
    'name' => 'John'
), array('%s'));

Esegue una query SQL personalizzata e restituisce i risultati come un array di oggetti.

Nota: senza l’uso di $wpdb->prepare() questo metodo, il codice potrebbe essere vulnerabile a SQL Injection.

$wpdb->get_results($query, $output_type);

global $wpdb;
$results = $wpdb->get_results("SELECT * FROM wp_users WHERE age > 30", OBJECT);

Esegue una query SQL personalizzata in cui i valori forniti vengono parametrizzati e inseriti in modo sicuro in una query SQL.

Questa operazione è necessaria per i seguenti metodi quando l’input fornito dall’utente viene accettato per impedire l’iniezione di codice SQL: $wpdb->get_results, $wpdb->query, $wpdb->get_row e $wpdb->get_col.

$wpdb->prepare($query, $value1, $value2, ...);

global $wpdb;
 
// Preparing a complex custom query with user input
$age = 30;
$name = 'John';
$query = $wpdb->prepare(
    "SELECT * FROM wp_users WHERE age = %d AND name = %s",
    $age, $name
);
 
// Run the query
$results = $wpdb->get_results($query);

Iniezione SQL: cosa cercare

Le SQL Injection possono essere difficili da individuare, poiché potrebbero esistere più livelli di astrazione dal momento in cui l’input dell’utente viene elaborato fino a quando viene utilizzato in una query SQL.

Inoltre, esistono molte query SQL che non utilizzano input forniti dall’utente, ma possono utilizzare variabili che devono essere ricondotte alle posizioni di assegnazione per la verifica.

Poiché le funzioni SQL di WordPress possono essere utilizzate in vari modi, non esiste una singola ricerca o espressione regolare che si possa utilizzare per individuare queste vulnerabilità.

La maggior parte delle query SQL utilizzate nei plugin e nei temi di WordPress dovrebbero essere parametrizzate con $wpdb->prepare().

Se vedi uno schema come il seguente è improbabile che sia vulnerabile a SQL injection.

$post_id = $_GET['post_id'];
...
$wpdb->prepare( "SELECT * FROM wp_posts WHERE ID = %d", $post_id );

Tuttavia, $wpdb->prepare()può essere utilizzato in modo errato (senza parametrizzazione).

Questa funzione serve solo a prevenire l’iniezione SQL se vengono utilizzati segnaposto come %d%s, come visto sopra.

$post_id = $_GET['post_id'];
...
$query = $wpdb->prepare( "SELECT * FROM wp_posts WHERE ID = $post_id" );
$wpdb->get_results( $query );

Le query create concatenando l’input dell’utente con un’istruzione SQL fissa sono spesso vulnerabili se non sono in atto altre protezioni.

// Vulnerable concatenation with user-supplied input
$post_id = $_GET['post_id'];
...
$query = "SELECT * FROM wp_posts WHERE ID = " . $post_id;
$wpdb->get_results( $query );

A volte il codice può sembrare vulnerabile a SQL injection, ma vengono implementati altri meccanismi per impedire che input arbitrari dell’utente vengano introdotti nella query.

Nell’esempio seguente, l’ID del post fornito dall’utente viene convertito in un intero, impedendo il passaggio di valori arbitrari alla query SQL.

// Using (int) cast
$post_id = (int) $_GET['post_id'];
...
$query = "SELECT * FROM wp_posts WHERE ID = " . $post_id;
$wpdb->get_results( $query );
 
// --- or ---
 
// Using intval()
$post_id = intval( $_GET['post_id'] );
...
$query = "SELECT * FROM wp_posts WHERE ID = " . $post_id;
$wpdb->get_results( $query );

Inoltre, WordPress utilizza wp_magic_quotes() , che aggiunge automaticamente virgolette singole ( ) all’input proveniente da superglobali, quindi, sebbene una query SQL come quella qui sotto possa inizialmente sembrare vulnerabile a SQL Injection, non lo è.

Nell’esempio seguente, il valore post_id verrebbe automaticamente racchiuso tra virgolette singole, generando la query  SELECT * FROM wp_posts WHERE ID = ''1''.

// Vulnerable concatenation with user-supplied input
$post_id = $_GET['post_id'];
...
$query = "SELECT * FROM wp_posts WHERE ID = ' " . $post_id . "'";
$wpdb->get_results( $query );

Che cos’è esc_sql()?

WordPress fornisce la funzione esc_sql() per eseguire l’escape dei caratteri nelle stringhe che verrebbero utilizzate nelle query SQL.

Consideratela una funzione di “preparazione delle stringhe”, non una funzione di sicurezza.

In altre parole, prepara una stringa da inserire in una query SQL in modo da non interromperla (ad esempio, convertendo gli apici singoli ( ) in apici singoli con escape ( \’).

Questa funzione viene spesso utilizzata in modo errato dagli sviluppatori per “pulire” i valori forniti dall’utente prima di utilizzarli nelle query SQL al posto di $wpdb->prepare().

Sebbene possa mitigare alcuni attacchi eludendo questi caratteri, non è pensata per contrastare attacchi di iniezione SQL come $wpdb->prepare().

Se un valore fornito dall’utente non è racchiuso tra virgolette singole in una query, esc_sql()come unica forma di protezione potrebbe essere facilmente aggirato, considerando che non sono necessarie virgolette singole per effettuare un attacco.

L’esempio seguente potrebbe essere facilmente aggirato con un valore di $post_id uguale a 1 OR 1=1, la query risultante diverrebbe SELECT * FROM wp_posts WHERE ID = 1 OR 1=1.

$query = "SELECT * FROM wp_posts WHERE ID = " . esc_sql($post_id);
$wpdb->get_results( $query );

Traduzione in italiano della WordPress Security Research di Wordfence

WordPress Security Architecture

Lascia un commento